From 484252d28060aa142802fb12825a20e823402eac Mon Sep 17 00:00:00 2001 From: Ahmed AbouZaid <6760103+aabouzaid@users.noreply.github.com> Date: Sat, 8 Apr 2023 02:51:40 +0200 Subject: [PATCH] refactor: expose kubeconform as a module --- cmd/kubeconform/{main.go => validate.go} | 51 ++++++++---------------- main.go | 24 +++++++++++ pkg/config/config.go | 11 +++++ pkg/output/output.go | 6 +-- 4 files changed, 54 insertions(+), 38 deletions(-) rename cmd/kubeconform/{main.go => validate.go} (80%) create mode 100644 main.go diff --git a/cmd/kubeconform/main.go b/cmd/kubeconform/validate.go similarity index 80% rename from cmd/kubeconform/main.go rename to cmd/kubeconform/validate.go index e8a93e4..c7ff7d2 100644 --- a/cmd/kubeconform/main.go +++ b/cmd/kubeconform/validate.go @@ -1,4 +1,4 @@ -package main +package kubeconform import ( "context" @@ -15,8 +15,6 @@ import ( "github.com/yannh/kubeconform/pkg/validator" ) -var version = "development" - func processResults(cancel context.CancelFunc, o output.Output, validationResults <-chan validator.Result, exitOnError bool) <-chan bool { success := true result := make(chan bool) @@ -28,7 +26,7 @@ func processResults(cancel context.CancelFunc, o output.Output, validationResult } if o != nil { if err := o.Write(res); err != nil { - fmt.Fprint(os.Stderr, "failed writing log\n") + log.Fatal("failed writing log: ", err) } } if !success && exitOnError { @@ -46,27 +44,19 @@ func processResults(cancel context.CancelFunc, o output.Output, validationResult return result } -func realMain() int { - cfg, out, err := config.FromFlags(os.Args[0], os.Args[1:]) +func Validate(cfg config.Config, out string) error { + if out != "" { - o := os.Stderr - errCode := 1 if cfg.Help { - o = os.Stdout - errCode = 0 + fmt.Fprintln(cfg.Stream.Output, out) + return nil } - fmt.Fprintln(o, out) - return errCode + return fmt.Errorf("config out is not empty") } if cfg.Version { - fmt.Println(version) - return 0 - } - - if err != nil { - fmt.Fprintf(os.Stderr, "failed parsing command line: %s\n", err.Error()) - return 1 + fmt.Fprintln(cfg.Stream.Output, out) + return nil } cpuProfileFile := os.Getenv("KUBECONFORM_CPUPROFILE_FILE") @@ -93,13 +83,11 @@ func realMain() int { useStdin = true } - var o output.Output - if o, err = output.New(cfg.OutputFormat, cfg.Summary, useStdin, cfg.Verbose); err != nil { - fmt.Fprintln(os.Stderr, err) - return 1 + o, err := output.New(cfg.Stream.Output, cfg.OutputFormat, cfg.Summary, useStdin, cfg.Verbose) + if err != nil { + return fmt.Errorf("failed to get output: %s", err.Error()) } - var v validator.Validator - v, err = validator.New(cfg.SchemaLocations, validator.Opts{ + v, err := validator.New(cfg.SchemaLocations, validator.Opts{ Cache: cfg.Cache, Debug: cfg.Debug, SkipTLS: cfg.SkipTLS, @@ -110,8 +98,7 @@ func realMain() int { IgnoreMissingSchemas: cfg.IgnoreMissingSchemas, }) if err != nil { - fmt.Fprintln(os.Stderr, err) - return 1 + return fmt.Errorf("failed to validate: %s", err.Error()) } validationResults := make(chan validator.Result) @@ -121,7 +108,7 @@ func realMain() int { var resourcesChan <-chan resource.Resource var errors <-chan error if useStdin { - resourcesChan, errors = resource.FromStream(ctx, "stdin", os.Stdin) + resourcesChan, errors = resource.FromStream(ctx, "stdin", cfg.Stream.Input) } else { resourcesChan, errors = resource.FromFiles(ctx, cfg.Files, cfg.IgnoreFilenamePatterns) } @@ -171,12 +158,8 @@ func realMain() int { o.Flush() if !success { - return 1 + return fmt.Errorf("failed to process results") } - return 0 -} - -func main() { - os.Exit(realMain()) + return nil } diff --git a/main.go b/main.go new file mode 100644 index 0000000..29e6a22 --- /dev/null +++ b/main.go @@ -0,0 +1,24 @@ +package main + +import ( + "fmt" + "os" + + "github.com/yannh/kubeconform/cmd/kubeconform" + "github.com/yannh/kubeconform/pkg/config" +) + +var version = "development" + +func main() { + cfg, out, err := config.FromFlags(os.Args[0], os.Args[1:]) + if err != nil { + fmt.Fprintf(os.Stderr, "failed parsing command line: %s\n", err.Error()) + os.Exit(1) + } + + if err = kubeconform.Validate(cfg, out); err != nil { + fmt.Fprintf(os.Stderr, "failed validating resources: %s - %s\n", err.Error(), out) + os.Exit(1) + } +} diff --git a/pkg/config/config.go b/pkg/config/config.go index b64a3c1..4b834d9 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -4,9 +4,17 @@ import ( "bytes" "flag" "fmt" + "io" + "os" "strings" ) +type Stream struct { + Input io.Reader + Output io.Writer + Error io.Writer +} + type Config struct { Cache string Debug bool @@ -26,6 +34,7 @@ type Config struct { IgnoreFilenamePatterns []string Help bool Version bool + Stream *Stream } type arrayParam []string @@ -62,7 +71,9 @@ func FromFlags(progName string, args []string) (Config, string, error) { c := Config{} c.Files = []string{} + c.Stream = &Stream{os.Stdin, os.Stdout, os.Stderr} + flags.SetOutput(c.Stream.Output) flags.StringVar(&c.KubernetesVersion, "kubernetes-version", "master", "version of Kubernetes to validate against, e.g.: 1.18.0") flags.Var(&schemaLocationsParam, "schema-location", "override schemas location search path (can be specified multiple times)") flags.StringVar(&skipKindsCSV, "skip", "", "comma-separated list of kinds or GVKs to ignore") diff --git a/pkg/output/output.go b/pkg/output/output.go index 7612873..488aadd 100644 --- a/pkg/output/output.go +++ b/pkg/output/output.go @@ -2,7 +2,7 @@ package output import ( "fmt" - "os" + "io" "github.com/yannh/kubeconform/pkg/validator" ) @@ -12,9 +12,7 @@ type Output interface { Flush() error } -func New(outputFormat string, printSummary, isStdin, verbose bool) (Output, error) { - w := os.Stdout - +func New(w io.Writer, outputFormat string, printSummary, isStdin, verbose bool) (Output, error) { switch { case outputFormat == "json": return jsonOutput(w, printSummary, isStdin, verbose), nil