From 59e01b725ad1b62fb810f8ff1e4d305d9c662797 Mon Sep 17 00:00:00 2001 From: Matthis Holleville Date: Thu, 30 Mar 2023 22:02:17 +0200 Subject: [PATCH] feat: add & remove default filter(s) to analyze. Signed-off-by: Matthis Holleville --- README.md | 38 +++++++++++++++++---- cmd/filters/filters.go | 2 ++ cmd/filters/filtersAdd.go | 65 ++++++++++++++++++++++++++++++++++++ cmd/filters/filtersRemove.go | 62 ++++++++++++++++++++++++++++++++++ pkg/analyzer/analyzer.go | 21 ++++++++++-- pkg/util/util.go | 17 ++++++++++ 6 files changed, 195 insertions(+), 10 deletions(-) create mode 100644 cmd/filters/filtersAdd.go create mode 100644 cmd/filters/filtersRemove.go diff --git a/README.md b/README.md index de9c76350f..e99faaa142 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ If you install gcc as suggested, the problem will persist. Therefore, you need t * Currently the default AI provider is OpenAI, you will need to generate an API key from [OpenAI](https://openai.com) * You can do this by running `k8sgpt generate` to open a browser link to generate it * Run `k8sgpt auth` to set it in k8sgpt. -* Run `k8sgpt filters` to manage filters. +* Run `k8sgpt filters` to manage the default filters used by the analyzer. By default, all filters are executed during analysis. * Run `k8sgpt analyze` to run a scan. * And use `k8sgpt analyze --explain` to get a more detailed explanation of the issues. @@ -92,18 +92,42 @@ Flags: Use "k8sgpt [command] --help" for more information about a command. ``` -_Run a scan with the default analyzers_ +_Manage filters_ + +_List filters_ ``` -k8sgpt generate -k8sgpt auth -k8sgpt analyze --explain +k8sgpt filters list ``` -_List filters_ +_Add default filters_ ``` -k8sgpt filters list +k8sgpt filters add [filter(s)] +``` + +### Example : + +- Simple filter : `k8sgpt filters add Service` +- Multiple filters : `k8sgpt filters add Ingress Pod` + +_Add default filters_ + +``` +k8sgpt filters remove [filter(s)] +``` + +### Example : + +- Simple filter : `k8sgpt filters remove Service` +- Multiple filters : `k8sgpt filters remove Ingress Pod` + +_Run a scan with the default analyzers_ + +``` +k8sgpt generate +k8sgpt auth +k8sgpt analyze --explain ``` _Filter on resource_ diff --git a/cmd/filters/filters.go b/cmd/filters/filters.go index f2146ffff4..5edcdb2a86 100644 --- a/cmd/filters/filters.go +++ b/cmd/filters/filters.go @@ -20,4 +20,6 @@ var FiltersCmd = &cobra.Command{ func init() { FiltersCmd.AddCommand(filterListCmd) + FiltersCmd.AddCommand(filtersAddCmd) + FiltersCmd.AddCommand(filtersRemoveCmd) } diff --git a/cmd/filters/filtersAdd.go b/cmd/filters/filtersAdd.go new file mode 100644 index 0000000000..bada0943d6 --- /dev/null +++ b/cmd/filters/filtersAdd.go @@ -0,0 +1,65 @@ +package filters + +import ( + "os" + "strings" + + "github.com/fatih/color" + "github.com/k8sgpt-ai/k8sgpt/pkg/analyzer" + "github.com/k8sgpt-ai/k8sgpt/pkg/util" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +var filtersAddCmd = &cobra.Command{ + Use: "add [filter(s)]", + Short: "Adds one or more new filters.", + Long: `The add command adds one or more new filters to the default set of filters used by the analyze.`, + Args: cobra.MinimumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + + // Verify filter exist + invalidFilters := []string{} + for _, f := range args { + foundFilter := false + for _, filter := range analyzer.ListFilters() { + if filter == f { + foundFilter = true + break + } + } + if !foundFilter { + invalidFilters = append(invalidFilters, f) + } + } + + if len(invalidFilters) != 0 { + color.Red("Filter %s does not exist. Please use k8sgpt filters list", strings.Join(invalidFilters, ", ")) + os.Exit(1) + } + + // Get defined default_filters + defaultFilters := viper.GetStringSlice("default_filters") + if len(defaultFilters) == 0 { + defaultFilters = []string{} + } + + mergedFilters := append(defaultFilters, args...) + + uniqueFilters, dupplicateFilters := util.RemoveDuplicates(mergedFilters) + + // Verify dupplicate + if len(dupplicateFilters) != 0 { + color.Red("Duplicate filters found: %s", strings.Join(dupplicateFilters, ", ")) + os.Exit(1) + } + + viper.Set("default_filters", uniqueFilters) + + if err := viper.WriteConfig(); err != nil { + color.Red("Error writing config file: %s", err.Error()) + os.Exit(1) + } + color.Green("Filter %s added", strings.Join(args, ", ")) + }, +} diff --git a/cmd/filters/filtersRemove.go b/cmd/filters/filtersRemove.go new file mode 100644 index 0000000000..7e15387535 --- /dev/null +++ b/cmd/filters/filtersRemove.go @@ -0,0 +1,62 @@ +package filters + +import ( + "os" + "strings" + + "github.com/fatih/color" + "github.com/k8sgpt-ai/k8sgpt/pkg/util" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +var filtersRemoveCmd = &cobra.Command{ + Use: "remove [filter(s)]", + Short: "Remove one or more filters.", + Long: `The add command remove one or more filters to the default set of filters used by the analyze.`, + Args: cobra.MinimumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + + // Get defined default_filters + defaultFilters := viper.GetStringSlice("default_filters") + if len(defaultFilters) == 0 { + defaultFilters = []string{} + } + + // verify dupplicate filters example: k8sgpt filters remove Pod Pod + uniqueFilters, dupplicateFilters := util.RemoveDuplicates(args) + if len(dupplicateFilters) != 0 { + color.Red("Duplicate filters found: %s", strings.Join(dupplicateFilters, ", ")) + os.Exit(1) + } + + // Verify if filter exist in config file and update default_filter + filterNotFound := []string{} + for _, filter := range uniqueFilters { + foundFilter := false + for i, f := range defaultFilters { + if f == filter { + foundFilter = true + defaultFilters = append(defaultFilters[:i], defaultFilters[i+1:]...) + break + } + } + if !foundFilter { + filterNotFound = append(filterNotFound, filter) + } + } + + if len(filterNotFound) != 0 { + color.Red("Filter(s) %s does not exist in configuration file. Please use k8sgpt filters add.", strings.Join(filterNotFound, ", ")) + os.Exit(1) + } + + viper.Set("default_filters", defaultFilters) + + if err := viper.WriteConfig(); err != nil { + color.Red("Error writing config file: %s", err.Error()) + os.Exit(1) + } + color.Green("Filter(s) %s removed", strings.Join(args, ", ")) + }, +} diff --git a/pkg/analyzer/analyzer.go b/pkg/analyzer/analyzer.go index e5a7d954d9..325ace230f 100644 --- a/pkg/analyzer/analyzer.go +++ b/pkg/analyzer/analyzer.go @@ -24,8 +24,10 @@ func RunAnalysis(ctx context.Context, filters []string, config *AnalysisConfigur client *kubernetes.Client, aiClient ai.IAI, analysisResults *[]Analysis) error { - // if there are no filters selected then run all of them - if len(filters) == 0 { + defaultFilters := viper.GetStringSlice("default_filters") + + // if there are no filters selected and no default_filters then run all of them + if len(filters) == 0 && len(defaultFilters) == 0 { for _, analyzer := range analyzerMap { if err := analyzer(ctx, config, client, aiClient, analysisResults); err != nil { return err @@ -34,7 +36,20 @@ func RunAnalysis(ctx context.Context, filters []string, config *AnalysisConfigur return nil } - for _, filter := range filters { + // if the filters flag is specified + if len(filters) != 0 { + for _, filter := range filters { + if analyzer, ok := analyzerMap[filter]; ok { + if err := analyzer(ctx, config, client, aiClient, analysisResults); err != nil { + return err + } + } + } + return nil + } + + // use default_filters + for _, filter := range defaultFilters { if analyzer, ok := analyzerMap[filter]; ok { if err := analyzer(ctx, config, client, aiClient, analysisResults); err != nil { return err diff --git a/pkg/util/util.go b/pkg/util/util.go index 598a8c73c3..79a7223f37 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -65,3 +65,20 @@ func GetParent(client *kubernetes.Client, meta metav1.ObjectMeta) (string, bool) } return meta.Name, false } + +func RemoveDuplicates(slice []string) ([]string, []string) { + set := make(map[string]bool) + duplicates := []string{} + for _, val := range slice { + if _, ok := set[val]; !ok { + set[val] = true + } else { + duplicates = append(duplicates, val) + } + } + uniqueSlice := make([]string, 0, len(set)) + for val := range set { + uniqueSlice = append(uniqueSlice, val) + } + return uniqueSlice, duplicates +}