Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow ignoring empty kinds or names. #90

Merged
merged 3 commits into from
Aug 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ func root() *cobra.Command {
rootCommand.Flags().BoolVar(&opts.SortByKind, "sort-by-kind", false, "if enabled, resources are sorted by Kind, a la Helm, before saving them to disk")
rootCommand.Flags().BoolVar(&opts.OutputToStdout, "stdout", false, "if enabled, no resource is written to disk and all resources are printed to stdout instead")
rootCommand.Flags().StringVarP(&configFile, "config", "c", "", "path to the config file")
rootCommand.Flags().BoolVar(&opts.AllowEmptyKinds, "allow-empty-kinds", false, "if enabled, resources with empty kinds don't produce an error when filtering")
rootCommand.Flags().BoolVar(&opts.AllowEmptyNames, "allow-empty-names", false, "if enabled, resources with empty names don't produce an error when filtering")

_ = rootCommand.Flags().MarkHidden("debug")
return rootCommand
Expand Down
4 changes: 2 additions & 2 deletions slice/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,12 @@ func (s *Split) parseYAMLManifest(contents []byte) (yamlFile, error) {
s.log.Printf("Kubernetes metadata found -> %#v", k8smeta)

// Check if we have a Kubernetes kind and we're requesting inclusion or exclusion
if k8smeta.Kind == "" && (hasIncluded || hasExcluded) {
if k8smeta.Kind == "" && !s.opts.AllowEmptyKinds && (hasIncluded || hasExcluded) {
return yamlFile{}, fmt.Errorf("unable to find Kubernetes \"kind\" field in file number %d", s.fileCount)
}

// Check if we have a Kubernetes name and we're requesting inclusion or exclusion
if k8smeta.Name == "" && (hasIncluded || hasExcluded) {
if k8smeta.Name == "" && !s.opts.AllowEmptyNames && (hasIncluded || hasExcluded) {
return yamlFile{}, fmt.Errorf("unable to find Kubernetes \"metadata.name\" field in file number %d", s.fileCount)
}

Expand Down
115 changes: 115 additions & 0 deletions slice/process_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -347,3 +347,118 @@ kind: Foo
})
}
}

func TestSplit_parseYamlManifestAllowingEmpties(t *testing.T) {
tests := []struct {
name string
contents []byte
skipEmptyName bool
skipEmptyKind bool
includeKind string
includeName string
want yamlFile
wantErr bool
}{
{
name: "include name and kind",
contents: []byte(`---
apiVersion: v1
kind: Foo
metadata:
name: bar
`),
want: yamlFile{
filename: "foo-bar.yaml",
meta: kubeObjectMeta{APIVersion: "v1", Kind: "Foo", Name: "bar"},
},
includeKind: "Foo",
skipEmptyName: false,
skipEmptyKind: false,
},
{
name: "allow empty kind",
contents: []byte(`---
apiVersion: v1
kind: ""
metadata:
name: bar
`),
want: yamlFile{
filename: "-bar.yaml",
meta: kubeObjectMeta{APIVersion: "v1", Kind: "", Name: "bar"},
},
includeName: "bar",
skipEmptyName: false,
skipEmptyKind: true,
},
{
name: "dont allow empty kind",
contents: []byte(`---
apiVersion: v1
metadata:
name: bar
`),
wantErr: true,
includeName: "bar",
skipEmptyName: false,
skipEmptyKind: false,
},
{
name: "allow empty name",
contents: []byte(`---
apiVersion: v1
kind: Foo
metadata:
name: ""
`),
want: yamlFile{
filename: "foo-.yaml",
meta: kubeObjectMeta{APIVersion: "v1", Kind: "Foo", Name: ""},
},
includeKind: "Foo",
skipEmptyName: true,
skipEmptyKind: false,
},
{
name: "dont allow empty name",
contents: []byte(`---
apiVersion: v1
kind: Foo
`),
wantErr: true,
includeKind: "Foo",
skipEmptyName: false,
skipEmptyKind: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {

s := &Split{
log: nolog,
template: template.Must(template.New(DefaultTemplateName).Funcs(local.Functions).Parse(DefaultTemplateName)),
}

if len(tt.includeKind) > 0 {
s.opts.IncludedKinds = []string{tt.includeKind}
}

if len(tt.includeName) > 0 {
s.opts.IncludedNames = []string{tt.includeName}
}

s.opts.AllowEmptyKinds = tt.skipEmptyKind
s.opts.AllowEmptyNames = tt.skipEmptyName

if err := s.validateFilters(); err != nil {
t.Fatalf("not expecting error validating filters, got: %s", err)
}

got, err := s.parseYAMLManifest(tt.contents)
requireErrorIf(t, tt.wantErr, err)
t.Logf("got: %#v", got)
require.Equal(t, tt.want, got)
})
}
}
3 changes: 3 additions & 0 deletions slice/split.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,7 @@ type Options struct {
StrictKubernetes bool // if true, any YAMLs that don't contain at least an "apiVersion", "kind" and "metadata.name" will be excluded

SortByKind bool // if true, it will sort the resources by kind

AllowEmptyNames bool
AllowEmptyKinds bool
}
16 changes: 16 additions & 0 deletions slice/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,22 @@ func (s *Split) init() error {
}

func (s *Split) validateFilters() error {
if len(s.opts.IncludedKinds) > 0 && s.opts.AllowEmptyKinds {
return fmt.Errorf("cannot specify both included kinds and allow empty kinds")
}

if len(s.opts.ExcludedKinds) > 0 && s.opts.AllowEmptyKinds {
return fmt.Errorf("cannot specify both excluded kinds and allow empty kinds")
}

if len(s.opts.IncludedNames) > 0 && s.opts.AllowEmptyNames {
return fmt.Errorf("cannot specify both included names and allow empty names")
}

if len(s.opts.ExcludedNames) > 0 && s.opts.AllowEmptyNames {
return fmt.Errorf("cannot specify both excluded names and allow empty names")
}

if len(s.opts.IncludedKinds) > 0 && len(s.opts.ExcludedKinds) > 0 {
return fmt.Errorf("cannot specify both included and excluded kinds")
}
Expand Down
62 changes: 62 additions & 0 deletions slice/validate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package slice

import (
"testing"
)

func TestSplit_validateFilters(t *testing.T) {
tests := []struct {
name string
opts Options
wantErr bool
}{
{
name: "prevent using allow skipping kind while using included kinds",
opts: Options{
AllowEmptyKinds: true,
IncludedKinds: []string{"foo"},
},
wantErr: true,
},
{
name: "prevent using allow skipping kind while using excluded kinds",
opts: Options{
AllowEmptyKinds: true,
ExcludedKinds: []string{"foo"},
},
wantErr: true,
},
{
name: "prevent using allow skipping name while using included names",
opts: Options{
AllowEmptyNames: true,
IncludedNames: []string{"foo"},
},
wantErr: true,
},
{
name: "prevent using allow skipping name while using excluded names",
opts: Options{
AllowEmptyNames: true,
ExcludedNames: []string{"foo"},
},
wantErr: true,
},
{
name: "cannot specify included and excluded kinds",
opts: Options{
IncludedKinds: []string{"foo"},
ExcludedKinds: []string{"bar"},
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := &Split{opts: tt.opts}
if err := s.validateFilters(); (err != nil) != tt.wantErr {
t.Errorf("Split.validateFilters() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}