Skip to content

Commit

Permalink
Merge pull request k8sgpt-ai#161 from matthisholleville/feature/add-a…
Browse files Browse the repository at this point in the history
…nd-remove-filters

feat: add & remove default filter(s) to analyze.
  • Loading branch information
AlexsJones authored Mar 31, 2023
2 parents 9e9cd70 + 3ed545f commit c2b8732
Show file tree
Hide file tree
Showing 7 changed files with 248 additions and 15 deletions.
38 changes: 31 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 active 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.

Expand Down Expand Up @@ -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)]
```

### Examples :

- Simple filter : `k8sgpt filters add Service`
- Multiple filters : `k8sgpt filters add Ingress,Pod`

_Add default filters_

```
k8sgpt filters remove [filter(s)]
```

### Examples :

- 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_
Expand Down
71 changes: 71 additions & 0 deletions cmd/filters/add.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
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 addCmd = &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.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
inputFilters := strings.Split(args[0], ",")
availableFilters := analyzer.ListFilters()

// Verify filter exist
invalidFilters := []string{}
for _, f := range inputFilters {
if f == "" {
color.Red("Filter cannot be empty. Please use correct syntax.")
os.Exit(1)
}
foundFilter := false
for _, filter := range availableFilters {
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 active_filters
activeFilters := viper.GetStringSlice("active_filters")
if len(activeFilters) == 0 {
activeFilters = availableFilters
}

mergedFilters := append(activeFilters, inputFilters...)

uniqueFilters, dupplicatedFilters := util.RemoveDuplicates(mergedFilters)

// Verify dupplicate
if len(dupplicatedFilters) != 0 {
color.Red("Duplicate filters found: %s", strings.Join(dupplicatedFilters, ", "))
os.Exit(1)
}

viper.Set("active_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(inputFilters, ", "))
},
}
4 changes: 3 additions & 1 deletion cmd/filters/filters.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,7 @@ var FiltersCmd = &cobra.Command{
}

func init() {
FiltersCmd.AddCommand(filterListCmd)
FiltersCmd.AddCommand(listCmd)
FiltersCmd.AddCommand(addCmd)
FiltersCmd.AddCommand(removeCmd)
}
26 changes: 22 additions & 4 deletions cmd/filters/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,35 @@ import (

"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 filterListCmd = &cobra.Command{
var listCmd = &cobra.Command{
Use: "list",
Short: "List available filters",
Long: `The list command displays a list of available filters that can be used to analyze Kubernetes resources.`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("Available filters : \n")
for _, analyzer := range analyzer.ListFilters() {
fmt.Printf("> %s\n", color.GreenString(analyzer))
activeFilters := viper.GetStringSlice("active_filters")
availableFilters := analyzer.ListFilters()

if len(activeFilters) == 0 {
activeFilters = availableFilters
}

inactiveFilters := util.SliceDiff(availableFilters, activeFilters)
fmt.Printf(color.YellowString("Active: \n"))
for _, filter := range activeFilters {
fmt.Printf("> %s\n", color.GreenString(filter))
}
// display inactive filters
if len(inactiveFilters) != 0 {
fmt.Printf(color.YellowString("Unused: \n"))
for _, filter := range inactiveFilters {
fmt.Printf("> %s\n", color.RedString(filter))
}
}

},
}
72 changes: 72 additions & 0 deletions cmd/filters/remove.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
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 removeCmd = &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.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
inputFilters := strings.Split(args[0], ",")

// Get defined active_filters
activeFilters := viper.GetStringSlice("active_filters")
if len(activeFilters) == 0 {
activeFilters = analyzer.ListFilters()
}

// Check if input input filters is not empty
for _, f := range inputFilters {
if f == "" {
color.Red("Filter cannot be empty. Please use correct syntax.")
os.Exit(1)
}
}

// verify dupplicate filters example: k8sgpt filters remove Pod Pod
uniqueFilters, dupplicatedFilters := util.RemoveDuplicates(inputFilters)
if len(dupplicatedFilters) != 0 {
color.Red("Duplicate filters found: %s", strings.Join(dupplicatedFilters, ", "))
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 activeFilters {
if f == filter {
foundFilter = true
activeFilters = append(activeFilters[:i], activeFilters[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("active_filters", activeFilters)

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(inputFilters, ", "))
},
}
21 changes: 18 additions & 3 deletions pkg/analyzer/analyzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
activeFilters := viper.GetStringSlice("active_filters")

// if there are no filters selected and no active_filters then run all of them
if len(filters) == 0 && len(activeFilters) == 0 {
for _, analyzer := range analyzerMap {
if err := analyzer(ctx, config, client, aiClient, analysisResults); err != nil {
return err
Expand All @@ -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 active_filters
for _, filter := range activeFilters {
if analyzer, ok := analyzerMap[filter]; ok {
if err := analyzer(ctx, config, client, aiClient, analysisResults); err != nil {
return err
Expand Down
31 changes: 31 additions & 0 deletions pkg/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,34 @@ 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
}

func SliceDiff(source, dest []string) []string {
mb := make(map[string]struct{}, len(dest))
for _, x := range dest {
mb[x] = struct{}{}
}
var diff []string
for _, x := range source {
if _, found := mb[x]; !found {
diff = append(diff, x)
}
}
return diff
}

0 comments on commit c2b8732

Please sign in to comment.