From 0152d8c209b755751e445db5427ec0a893501954 Mon Sep 17 00:00:00 2001 From: Yann Hamon Date: Sun, 1 Nov 2020 23:18:39 +0100 Subject: [PATCH] add tests for command line parsing --- cmd/kubeconform/main.go | 10 +++--- pkg/config/config.go | 42 +++++++++++++------------ pkg/config/config_test.go | 62 +++++++++++++++++++++++++++++++++++++ pkg/resource/stream_test.go | 22 ++++++------- 4 files changed, 100 insertions(+), 36 deletions(-) diff --git a/cmd/kubeconform/main.go b/cmd/kubeconform/main.go index 2fc7939..ca9d85b 100644 --- a/cmd/kubeconform/main.go +++ b/cmd/kubeconform/main.go @@ -100,10 +100,12 @@ func processResults(o output.Output, validationResults <-chan validator.Result, } func realMain() int { - var err error - - cfg := config.FromFlags() - if cfg.Help { + cfg, out, err := config.FromFlags(os.Args[0], os.Args[1:]) + if out != "" { + fmt.Println(out) + return 1 + } else if err != nil { + fmt.Fprintf(os.Stderr, "failed parsing command line: %s\n", err.Error()) return 1 } diff --git a/pkg/config/config.go b/pkg/config/config.go index 4722734..f40d1c3 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -1,6 +1,7 @@ package config import ( + "bytes" "flag" "fmt" "os" @@ -45,29 +46,32 @@ func skipKinds(skipKindsCSV string) map[string]bool { return skipKinds } -func FromFlags() Config { +func FromFlags(progName string, args []string) (Config, string, error) { var schemaLocationsParam arrayParam var skipKindsCSV string + flags := flag.NewFlagSet(progName, flag.PanicOnError) + var buf bytes.Buffer + flags.SetOutput(&buf) c := Config{} c.Files = []string{} - flag.StringVar(&c.KubernetesVersion, "kubernetes-version", "1.18.0", "version of Kubernetes to validate against") - flag.Var(&schemaLocationsParam, "schema-location", "override schemas location search path (can be specified multiple times)") - flag.StringVar(&skipKindsCSV, "skip", "", "comma-separated list of kinds to ignore") - flag.BoolVar(&c.IgnoreMissingSchemas, "ignore-missing-schemas", false, "skip files with missing schemas instead of failing") - flag.BoolVar(&c.Summary, "summary", false, "print a summary at the end") - flag.IntVar(&c.NumberOfWorkers, "n", 4, "number of goroutines to run concurrently") - flag.BoolVar(&c.Strict, "strict", false, "disallow additional properties not in schema") - flag.StringVar(&c.OutputFormat, "output", "text", "output format - text, json") - flag.BoolVar(&c.Verbose, "verbose", false, "print results for all resources") - flag.BoolVar(&c.Help, "h", false, "show help information") - flag.Usage = func() { - fmt.Fprintf(os.Stderr, "Usage: %s [OPTION]... [FILE OR FOLDER]...\n", os.Args[0]) - flag.PrintDefaults() + 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.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") + flags.IntVar(&c.NumberOfWorkers, "n", 4, "number of goroutines to run concurrently") + flags.BoolVar(&c.Strict, "strict", false, "disallow additional properties not in schema") + flags.StringVar(&c.OutputFormat, "output", "text", "output format - text, json") + flags.BoolVar(&c.Verbose, "verbose", false, "print results for all resources") + flags.BoolVar(&c.Help, "h", false, "show help information") + flags.Usage = func() { + fmt.Fprintf(os.Stderr, "Usage: %s [OPTION]... [FILE OR FOLDER]...\n", progName) + flags.PrintDefaults() } - flag.Parse() + err := flags.Parse(args) c.SkipKinds = skipKinds(skipKindsCSV) c.SchemaLocations = schemaLocationsParam @@ -75,13 +79,11 @@ func FromFlags() Config { c.SchemaLocations = append(c.SchemaLocations, "https://kubernetesjsonschema.dev") // if not specified, default behaviour is to use kubernetesjson-schema.dev as registry } - for _, file := range flag.Args() { - c.Files = append(c.Files, file) - } + c.Files = flags.Args() if c.Help { - flag.Usage() + flags.Usage() } - return c + return c, buf.String(), err } diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index e8aef9f..6a68efd 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -39,3 +39,65 @@ func TestSkipKindMaps(t *testing.T) { } } } + +func TestFromFlags(t *testing.T) { + testCases := []struct { + args []string + conf Config + }{ + { + []string{}, + Config{ + Files: []string{}, + KubernetesVersion: "1.18.0", + NumberOfWorkers: 4, + OutputFormat: "text", + SchemaLocations: []string{"https://kubernetesjsonschema.dev"}, + SkipKinds: map[string]bool{}, + }, + }, + { + []string{"-h"}, + Config{ + Files: []string{}, + Help: true, + KubernetesVersion: "1.18.0", + NumberOfWorkers: 4, + OutputFormat: "text", + SchemaLocations: []string{"https://kubernetesjsonschema.dev"}, + SkipKinds: map[string]bool{}, + }, + }, + { + []string{"-skip", "a,b,c"}, + Config{ + Files: []string{}, + KubernetesVersion: "1.18.0", + NumberOfWorkers: 4, + OutputFormat: "text", + SchemaLocations: []string{"https://kubernetesjsonschema.dev"}, + SkipKinds: map[string]bool{"a": true, "b": true, "c": true}, + }, + }, + { + []string{"-summary", "-verbose", "file1", "file2"}, + Config{ + Files: []string{"file1", "file2"}, + KubernetesVersion: "1.18.0", + NumberOfWorkers: 4, + OutputFormat: "text", + SchemaLocations: []string{"https://kubernetesjsonschema.dev"}, + SkipKinds: map[string]bool{}, + Summary: true, + Verbose: true, + }, + }, + } + + for _, testCase := range testCases { + cfg, _, _ := FromFlags("kubeconform", testCase.args) + if reflect.DeepEqual(cfg, testCase.conf) != true { + t.Errorf("failed parsing config - expected , got: \n%+v\n%+v", testCase.conf, cfg) + } + } +} diff --git a/pkg/resource/stream_test.go b/pkg/resource/stream_test.go index 61549e7..5ff06a8 100644 --- a/pkg/resource/stream_test.go +++ b/pkg/resource/stream_test.go @@ -10,25 +10,23 @@ import ( "testing" ) - - func TestFromStream(t *testing.T) { type have struct { - Path string + Path string Reader io.Reader } type want struct { Resources []resource.Resource - Errors []error + Errors []error } testCases := []struct { Have have Want want - } { + }{ { - Have: have { + Have: have{ Path: "myfile", Reader: strings.NewReader(` apiVersion: v1 @@ -49,7 +47,7 @@ kind: ReplicationController }, }, { - Have: have { + Have: have{ Path: "myfile", Reader: strings.NewReader(`apiVersion: v1 --- @@ -65,7 +63,7 @@ apiVersion: v2 `), }, { - Path: "myfile", + Path: "myfile", Bytes: []byte(``), }, { @@ -78,7 +76,7 @@ apiVersion: v2 }, }, { - Have: have { + Have: have{ Path: "myfile", Reader: strings.NewReader(`apiVersion: v1 kind: ReplicationController @@ -115,7 +113,7 @@ kind: CronJob }, }, { - Have: have { + Have: have{ Path: "myfile", Reader: strings.NewReader(`apiVersion: v1 kind: ReplicationController @@ -145,7 +143,7 @@ kind: Deployment } for _, testCase := range testCases { - resChan, errChan :=resource.FromStream(testCase.Have.Path, testCase.Have.Reader) + resChan, errChan := resource.FromStream(testCase.Have.Path, testCase.Have.Reader) var wg sync.WaitGroup wg.Add(2) @@ -180,4 +178,4 @@ kind: Deployment wg.Wait() } -} \ No newline at end of file +}