From b162c5b6f59155f5c576bc39000e1e8a973041a3 Mon Sep 17 00:00:00 2001 From: Yann Hamon Date: Sun, 8 Nov 2020 10:36:53 +0100 Subject: [PATCH] Add support for -reject and update README --- Readme.md | 9 +++++++-- acceptance.bats | 18 +++++++++++++++--- cmd/kubeconform/main.go | 14 ++++++++++++-- pkg/config/config.go | 9 ++++++--- pkg/config/config_test.go | 9 +++++++-- 5 files changed, 47 insertions(+), 12 deletions(-) diff --git a/Readme.md b/Readme.md index 61996f3..51519cf 100644 --- a/Readme.md +++ b/Readme.md @@ -47,15 +47,20 @@ configuration errors. ``` $ ./bin/kubeconform -h -Usage of ./bin/kubeconform: +Usage: ./bin/kubeconform [OPTION]... [FILE OR FOLDER]... + -exit-on-error + immediately stop execution when the first error is encountered + -h show help information -ignore-missing-schemas skip files with missing schemas instead of failing -kubernetes-version string version of Kubernetes to validate against (default "1.18.0") -n int - number of routines to run in parallel (default 4) + number of goroutines to run concurrently (default 4) -output string output format - text, json (default "text") + -reject string + comma-separated list of kinds to reject -schema-location value override schemas location search path (can be specified multiple times) -skip string diff --git a/acceptance.bats b/acceptance.bats index 3975ac7..77c940a 100755 --- a/acceptance.bats +++ b/acceptance.bats @@ -151,6 +151,18 @@ } @test "Fail when parsing an invalid Kubernetes config file on stdin" { - run bash -c "cat fixtures/invalid.yaml | bin/kubeconform -" - [ "$status" -eq 1 ] - } + run bash -c "cat fixtures/invalid.yaml | bin/kubeconform -" + [ "$status" -eq 1 ] +} + +@test "Skip when parsing a resource from a kind to skip" { + run bin/kubeconform -verbose -skip ReplicationController fixtures/valid.yaml + [ "$status" -eq 0 ] + [ "$output" = "fixtures/valid.yaml - bob ReplicationController skipped" ] +} + +@test "Fail when parsing a resource from a kind to reject" { + run bin/kubeconform -verbose -reject ReplicationController fixtures/valid.yaml + [ "$status" -eq 1 ] + [ "$output" = "fixtures/valid.yaml - ReplicationController bob failed validation: prohibited resource kind ReplicationController" ] +} diff --git a/cmd/kubeconform/main.go b/cmd/kubeconform/main.go index 61b7d9e..a69316f 100644 --- a/cmd/kubeconform/main.go +++ b/cmd/kubeconform/main.go @@ -36,7 +36,7 @@ func downloadSchema(registries []registry.Registry, kind, version, k8sVersion st return nil, nil // No schema found - we don't consider it an error, resource will be skipped } -func ValidateResources(resources <-chan resource.Resource, validationResults chan<- validator.Result, regs []registry.Registry, k8sVersion string, c *cache.SchemaCache, skip func(signature resource.Signature) bool, ignoreMissingSchemas bool) { +func ValidateResources(resources <-chan resource.Resource, validationResults chan<- validator.Result, regs []registry.Registry, k8sVersion string, c *cache.SchemaCache, skip func(signature resource.Signature) bool, reject func(signature resource.Signature) bool, ignoreMissingSchemas bool) { for res := range resources { sig, err := res.Signature() if err != nil { @@ -54,6 +54,11 @@ func ValidateResources(resources <-chan resource.Resource, validationResults cha continue } + if reject(*sig) { + validationResults <- validator.Result{Resource: res, Err: fmt.Errorf("prohibited resource kind %s", sig.Kind), Status: validator.Error} + continue + } + cached := false var schema *gojsonschema.Schema cacheKey := "" @@ -138,6 +143,11 @@ func realMain() int { return ok && isSkipKind } + reject := func(signature resource.Signature) bool { + _, ok := cfg.RejectKinds[signature.Kind] + return ok + } + registries := []registry.Registry{} for _, schemaLocation := range cfg.SchemaLocations { registries = append(registries, registry.New(schemaLocation, cfg.Strict)) @@ -167,7 +177,7 @@ func realMain() int { for i := 0; i < cfg.NumberOfWorkers; i++ { wg.Add(1) go func() { - ValidateResources(resourcesChan, validationResults, registries, cfg.KubernetesVersion, c, filter, cfg.IgnoreMissingSchemas) + ValidateResources(resourcesChan, validationResults, registries, cfg.KubernetesVersion, c, filter, reject, cfg.IgnoreMissingSchemas) wg.Done() }() } diff --git a/pkg/config/config.go b/pkg/config/config.go index 5cdb70d..f8b6d34 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -13,6 +13,7 @@ type Config struct { Files []string SchemaLocations []string SkipKinds map[string]bool + RejectKinds map[string]bool OutputFormat string KubernetesVersion string NumberOfWorkers int @@ -34,7 +35,7 @@ func (ap *arrayParam) Set(value string) error { return nil } -func skipKinds(skipKindsCSV string) map[string]bool { +func splitCSV(skipKindsCSV string) map[string]bool { splitKinds := strings.Split(skipKindsCSV, ",") skipKinds := map[string]bool{} @@ -49,7 +50,7 @@ func skipKinds(skipKindsCSV string) map[string]bool { func FromFlags(progName string, args []string) (Config, string, error) { var schemaLocationsParam arrayParam - var skipKindsCSV string + var skipKindsCSV, rejectKindsCSV string flags := flag.NewFlagSet(progName, flag.PanicOnError) var buf bytes.Buffer flags.SetOutput(&buf) @@ -60,6 +61,7 @@ func FromFlags(progName string, args []string) (Config, string, error) { flags.StringVar(&c.KubernetesVersion, "kubernetes-version", "1.18.0", "version of Kubernetes to validate against") flags.Var(&schemaLocationsParam, "schema-location", "override schemas location search path (can be specified multiple times)") flags.StringVar(&skipKindsCSV, "skip", "", "comma-separated list of kinds to ignore") + flags.StringVar(&rejectKindsCSV, "reject", "", "comma-separated list of kinds to reject") flags.BoolVar(&c.ExitOnError, "exit-on-error", false, "immediately stop execution when the first error is encountered") flags.BoolVar(&c.IgnoreMissingSchemas, "ignore-missing-schemas", false, "skip files with missing schemas instead of failing") flags.BoolVar(&c.Summary, "summary", false, "print a summary at the end") @@ -75,7 +77,8 @@ func FromFlags(progName string, args []string) (Config, string, error) { err := flags.Parse(args) - c.SkipKinds = skipKinds(skipKindsCSV) + c.SkipKinds = splitCSV(skipKindsCSV) + c.RejectKinds = splitCSV(rejectKindsCSV) c.SchemaLocations = schemaLocationsParam if len(c.SchemaLocations) == 0 { c.SchemaLocations = append(c.SchemaLocations, "https://kubernetesjsonschema.dev") // if not specified, default behaviour is to use kubernetesjson-schema.dev as registry diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index f9ef0d0..99c2665 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -33,7 +33,7 @@ func TestSkipKindMaps(t *testing.T) { }, }, } { - got := skipKinds(testCase.csvSkipKinds) + got := splitCSV(testCase.csvSkipKinds) if !reflect.DeepEqual(got, testCase.expect) { t.Errorf("%s - got %+v, expected %+v", testCase.name, got, testCase.expect) } @@ -54,6 +54,7 @@ func TestFromFlags(t *testing.T) { OutputFormat: "text", SchemaLocations: []string{"https://kubernetesjsonschema.dev"}, SkipKinds: map[string]bool{}, + RejectKinds: map[string]bool{}, }, }, { @@ -66,6 +67,7 @@ func TestFromFlags(t *testing.T) { OutputFormat: "text", SchemaLocations: []string{"https://kubernetesjsonschema.dev"}, SkipKinds: map[string]bool{}, + RejectKinds: map[string]bool{}, }, }, { @@ -77,6 +79,7 @@ func TestFromFlags(t *testing.T) { OutputFormat: "text", SchemaLocations: []string{"https://kubernetesjsonschema.dev"}, SkipKinds: map[string]bool{"a": true, "b": true, "c": true}, + RejectKinds: map[string]bool{}, }, }, { @@ -88,6 +91,7 @@ func TestFromFlags(t *testing.T) { OutputFormat: "text", SchemaLocations: []string{"https://kubernetesjsonschema.dev"}, SkipKinds: map[string]bool{}, + RejectKinds: map[string]bool{}, Summary: true, Verbose: true, }, @@ -95,7 +99,7 @@ func TestFromFlags(t *testing.T) { { []string{"-ignore-missing-schemas", "-kubernetes-version", "1.16.0", "-n", "2", "-output", "json", "-schema-location", "folder", "-schema-location", "anotherfolder", "-skip", "kinda,kindb", "-strict", - "-summary", "-verbose", "file1", "file2"}, + "-reject", "kindc,kindd", "-summary", "-verbose", "file1", "file2"}, Config{ Files: []string{"file1", "file2"}, IgnoreMissingSchemas: true, @@ -104,6 +108,7 @@ func TestFromFlags(t *testing.T) { OutputFormat: "json", SchemaLocations: []string{"folder", "anotherfolder"}, SkipKinds: map[string]bool{"kinda": true, "kindb": true}, + RejectKinds: map[string]bool{"kindc": true, "kindd": true}, Strict: true, Summary: true, Verbose: true,