From 4fdebb22d34f105365dcaf0587e71038959dfb1c Mon Sep 17 00:00:00 2001 From: Varsha Varadarajan Date: Fri, 28 Jun 2019 13:40:52 -0400 Subject: [PATCH] Use check filter to store groups and checks to run/ignore --- checks/check_filter.go | 81 +++++++++++++++++++ checks/commands.go | 175 ---------------------------------------- cmd/clusterlint/main.go | 112 ++++++++++++++++++++++++- 3 files changed, 191 insertions(+), 177 deletions(-) create mode 100644 checks/check_filter.go delete mode 100644 checks/commands.go diff --git a/checks/check_filter.go b/checks/check_filter.go new file mode 100644 index 0000000..49e19dd --- /dev/null +++ b/checks/check_filter.go @@ -0,0 +1,81 @@ +package checks + +import ( + "fmt" +) + +// CheckFilter stores names of checks and groups that needs to be included or excluded while running checks +type CheckFilter struct { + IncludeGroups []string + ExcludeGroups []string + IncludeChecks []string + ExcludeChecks []string +} + +// FilterChecks filters all to return set of checks based on the CheckFilter +func (c CheckFilter) FilterChecks() ([]Check, error) { + if len(c.IncludeChecks) > 0 && len(c.ExcludeChecks) > 0 { + return nil, fmt.Errorf("cannot specify both c and C flags") + } + + all, err := c.filterGroups() + + if err != nil { + return nil, err + } + + var ret []Check + + if len(c.IncludeChecks) > 0 { + for _, check := range all { + if c.contains(c.IncludeChecks, check.Name()) { + ret = append(ret, check) + } + } + return ret, nil + } else if len(c.ExcludeChecks) > 0 { + for _, check := range all { + if !c.contains(c.ExcludeChecks, check.Name()) { + ret = append(ret, check) + } + } + return ret, nil + } else { + return all, nil + } + +} + +func (c CheckFilter) filterGroups() ([]Check, error) { + if len(c.IncludeGroups) > 0 && len(c.ExcludeGroups) > 0 { + return nil, fmt.Errorf("cannot specify both g and G flags") + } + + if len(c.IncludeGroups) > 0 { + return GetGroups(c.IncludeGroups), nil + } else if len(c.ExcludeGroups) > 0 { + return c.getChecksNotInGroups(c.ExcludeGroups), nil + } else { + return List(), nil + } +} + +func (c CheckFilter) getChecksNotInGroups(groups []string) []Check { + allGroups := ListGroups() + var ret []Check + for _, group := range allGroups { + if !c.contains(groups, group) { + ret = append(ret, GetGroup(group)...) + } + } + return ret +} + +func (c CheckFilter) contains(list []string, name string) bool { + for _, l := range list { + if l == name { + return true + } + } + return false +} diff --git a/checks/commands.go b/checks/commands.go deleted file mode 100644 index 26a4bd5..0000000 --- a/checks/commands.go +++ /dev/null @@ -1,175 +0,0 @@ -package checks - -import ( - "encoding/json" - "fmt" - "os" - "sync" - - "github.com/digitalocean/clusterlint/kube" - "github.com/urfave/cli" - "golang.org/x/sync/errgroup" -) - -// ListChecks lists the names and desc of all checks in the group if found -// lists all checks in the registry if group is not specified -func ListChecks(c *cli.Context) error { - allChecks, err := filterGroups(c) - if err != nil { - return err - } - for _, check := range allChecks { - fmt.Printf("%s : %s\n", check.Name(), check.Description()) - } - - return nil -} - -// RunChecks runs all the checks based on the flags passed. -func RunChecks(c *cli.Context) error { - client, err := kube.NewClient(c.GlobalString("kubeconfig"), c.GlobalString("context")) - if err != nil { - return err - } - - objects, err := client.FetchObjects() - if err != nil { - return err - } - - return runChecks(objects, c) -} - -func runChecks(objects *kube.Objects, c *cli.Context) error { - all, err := filterGroups(c) - if err != nil { - return err - } - all, err = filterChecks(all, c) - if err != nil { - return err - } - if len(all) == 0 { - return fmt.Errorf("No checks to run. Are you sure that you provided the right names for groups and checks?") - } - var diagnostics []Diagnostic - var mu sync.Mutex - var g errgroup.Group - - for _, check := range all { - check := check - g.Go(func() error { - fmt.Println("Running check: ", check.Name()) - d, err := check.Run(objects) - if err != nil { - return err - } - mu.Lock() - diagnostics = append(diagnostics, d...) - mu.Unlock() - return nil - }) - } - err = g.Wait() - write(diagnostics, c) - - return err -} - -func write(diagnostics []Diagnostic, c *cli.Context) error { - output := c.String("output") - level := Severity(c.String("level")) - filtered := filterSeverity(level, diagnostics) - switch output { - case "json": - err := json.NewEncoder(os.Stdout).Encode(filtered) - if err != nil { - return err - } - default: - for _, diagnostic := range filtered { - fmt.Printf("%s\n", diagnostic) - } - } - - return nil -} - -func filterSeverity(level Severity, diagnostics []Diagnostic) []Diagnostic { - if level == "" { - return diagnostics - } - var filtered []Diagnostic - for _, d := range diagnostics { - if d.Severity == level { - filtered = append(filtered, d) - } - } - return filtered -} - -func filterGroups(c *cli.Context) ([]Check, error) { - whitelist := c.StringSlice("g") - blacklist := c.StringSlice("G") - if len(whitelist) > 0 && len(blacklist) > 0 { - return nil, fmt.Errorf("cannot specify both g and G flags") - } - - if len(whitelist) > 0 { - return GetGroups(whitelist), nil - } else if len(blacklist) > 0 { - return getChecksNotInGroups(blacklist), nil - } else { - return List(), nil - } -} - -func filterChecks(all []Check, c *cli.Context) ([]Check, error) { - whitelist := c.StringSlice("c") - blacklist := c.StringSlice("C") - - if len(whitelist) > 0 && len(blacklist) > 0 { - return nil, fmt.Errorf("cannot specify both c and C flags") - } - - var ret []Check - - if len(whitelist) > 0 { - for _, c := range all { - if contains(whitelist, c.Name()) { - ret = append(ret, c) - } - } - return ret, nil - } else if len(blacklist) > 0 { - for _, c := range all { - if !contains(blacklist, c.Name()) { - ret = append(ret, c) - } - } - return ret, nil - } else { - return all, nil - } - -} - -func getChecksNotInGroups(groups []string) []Check { - allGroups := ListGroups() - var ret []Check - for _, group := range allGroups { - if !contains(groups, group) { - ret = append(ret, GetGroup(group)...) - } - } - return ret -} - -func contains(list []string, name string) bool { - for _, l := range list { - if l == name { - return true - } - } - return false -} diff --git a/cmd/clusterlint/main.go b/cmd/clusterlint/main.go index 3cf89d4..99d1ce2 100644 --- a/cmd/clusterlint/main.go +++ b/cmd/clusterlint/main.go @@ -1,12 +1,16 @@ package main import ( + "encoding/json" "fmt" "os" "path/filepath" + "sync" "github.com/digitalocean/clusterlint/checks" + "github.com/digitalocean/clusterlint/kube" "github.com/urfave/cli" + "golang.org/x/sync/errgroup" // Side-effect import to get all the checks registered. _ "github.com/digitalocean/clusterlint/checks/all" @@ -41,7 +45,7 @@ func main() { Usage: "list all checks not in groups `GROUP1, GROUP2`", }, }, - Action: checks.ListChecks, + Action: listChecks, }, { Name: "run", @@ -72,7 +76,7 @@ func main() { Usage: "Filter output messages based on severity [error|warning|suggestion]. Default: all", }, }, - Action: checks.RunChecks, + Action: runChecks, }, } err := app.Run(os.Args) @@ -81,3 +85,107 @@ func main() { os.Exit(1) } } + +// listChecks lists the names and desc of all checks in the group if found +// lists all checks in the registry if group is not specified +func listChecks(c *cli.Context) error { + filter := checks.CheckFilter{ + IncludeGroups: c.StringSlice("g"), + ExcludeGroups: c.StringSlice("G"), + } + allChecks, err := filter.FilterChecks() + if err != nil { + return err + } + for _, check := range allChecks { + fmt.Printf("%s : %s\n", check.Name(), check.Description()) + } + + return nil +} + +// runChecks runs all the checks based on the flags passed. +func runChecks(c *cli.Context) error { + client, err := kube.NewClient(c.GlobalString("kubeconfig"), c.GlobalString("context")) + if err != nil { + return err + } + + objects, err := client.FetchObjects() + if err != nil { + return err + } + + return run(objects, c) +} + +func run(objects *kube.Objects, c *cli.Context) error { + filter := checks.CheckFilter{ + IncludeGroups: c.StringSlice("g"), + ExcludeGroups: c.StringSlice("G"), + IncludeChecks: c.StringSlice("c"), + ExcludeChecks: c.StringSlice("C"), + } + + all, err := filter.FilterChecks() + if err != nil { + return err + } + if len(all) == 0 { + return fmt.Errorf("No checks to run. Are you sure that you provided the right names for groups and checks?") + } + var diagnostics []checks.Diagnostic + var mu sync.Mutex + var g errgroup.Group + + for _, check := range all { + check := check + g.Go(func() error { + fmt.Println("Running check: ", check.Name()) + d, err := check.Run(objects) + if err != nil { + return err + } + mu.Lock() + diagnostics = append(diagnostics, d...) + mu.Unlock() + return nil + }) + } + err = g.Wait() + write(diagnostics, c) + + return err +} + +func write(diagnostics []checks.Diagnostic, c *cli.Context) error { + output := c.String("output") + level := checks.Severity(c.String("level")) + filtered := filterSeverity(level, diagnostics) + switch output { + case "json": + err := json.NewEncoder(os.Stdout).Encode(filtered) + if err != nil { + return err + } + default: + for _, diagnostic := range filtered { + fmt.Printf("%s\n", diagnostic) + } + } + + return nil +} + +func filterSeverity(level checks.Severity, diagnostics []checks.Diagnostic) []checks.Diagnostic { + if level == "" { + return diagnostics + } + var ret []checks.Diagnostic + for _, d := range diagnostics { + if d.Severity == level { + ret = append(ret, d) + } + } + return ret +}