diff --git a/cmd/analyze/analyze.go b/cmd/analyze/analyze.go index c46953fc17..22c48bff54 100644 --- a/cmd/analyze/analyze.go +++ b/cmd/analyze/analyze.go @@ -2,14 +2,17 @@ package analyze import ( "context" + "encoding/base64" "encoding/json" "fmt" "os" + "strings" "github.com/fatih/color" "github.com/k8sgpt-ai/k8sgpt/pkg/ai" "github.com/k8sgpt-ai/k8sgpt/pkg/analyzer" "github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes" + "github.com/schollz/progressbar/v3" "github.com/spf13/cobra" "github.com/spf13/viper" ) @@ -18,6 +21,7 @@ var ( explain bool backend string output string + filters []string ) // AnalyzeCmd represents the problems command @@ -68,10 +72,42 @@ var AnalyzeCmd = &cobra.Command{ color.Red("Error: %v", err) os.Exit(1) } + // Removed filtered results from slice + if len(filters) > 0 { + var filteredResults []analyzer.Analysis + for _, analysis := range *analysisResults { + for _, filter := range filters { + if strings.Contains(analysis.Kind, filter) { + filteredResults = append(filteredResults, analysis) + } + } + } + analysisResults = &filteredResults + } + + var bar *progressbar.ProgressBar + if len(*analysisResults) > 0 { + bar = progressbar.Default(int64(len(*analysisResults))) + } + for _, analysis := range *analysisResults { + + if explain { + parsedText, err := parseViaAI(ctx, aiClient, analysis.Error) + if err != nil { + color.Red("Error: %v", err) + continue + } + analysis.Details = parsedText + bar.Add(1) + } + } + + // print results for n, analysis := range *analysisResults { switch output { case "json": + analysis.Error = analysis.Error[0:] j, err := json.Marshal(analysis) if err != nil { color.Red("Error: %v", err) @@ -79,15 +115,57 @@ var AnalyzeCmd = &cobra.Command{ } fmt.Println(string(j)) default: - fmt.Printf("%s %s(%s): %s \n%s\n", color.CyanString("%d", n), color.YellowString(analysis.Name), color.CyanString(analysis.ParentObject), color.RedString(analysis.Error), color.GreenString(analysis.Details)) + fmt.Printf("%s %s(%s): %s \n%s\n", color.CyanString("%d", n), + color.YellowString(analysis.Name), color.CyanString(analysis.ParentObject), + color.RedString(analysis.Error[0]), color.GreenString(analysis.Details)) } } - }, } +func parseViaAI(ctx context.Context, aiClient ai.IAI, prompt []string) (string, error) { + // parse the text with the AI backend + + inputKey := strings.Join(prompt, " ") + // Check for cached data + sEnc := base64.StdEncoding.EncodeToString([]byte(inputKey)) + // find in viper cache + if viper.IsSet(sEnc) { + // retrieve data from cache + response := viper.GetString(sEnc) + if response == "" { + color.Red("error retrieving cached data") + return "", nil + } + output, err := base64.StdEncoding.DecodeString(response) + if err != nil { + color.Red("error decoding cached data: %v", err) + return "", nil + } + return string(output), nil + } + + response, err := aiClient.GetCompletion(ctx, inputKey) + if err != nil { + color.Red("error getting completion: %v", err) + return "", nil + } + + if !viper.IsSet(sEnc) { + viper.Set(sEnc, base64.StdEncoding.EncodeToString([]byte(response))) + if err := viper.WriteConfig(); err != nil { + color.Red("error writing config: %v", err) + return "", nil + } + } + return response, nil +} + func init() { + // array of strings flag + AnalyzeCmd.Flags().StringSliceVarP(&filters, "filter", "f", []string{}, "Filter for these analzyers (e.g. Pod,PersistentVolumeClaim,Service,ReplicaSet)") + AnalyzeCmd.Flags().BoolVarP(&explain, "explain", "e", false, "Explain the problem to me") // add flag for backend AnalyzeCmd.Flags().StringVarP(&backend, "backend", "b", "openai", "Backend AI provider") diff --git a/go.mod b/go.mod index d4fdcd0321..326ae62d68 100644 --- a/go.mod +++ b/go.mod @@ -36,20 +36,24 @@ require ( github.com/mailru/easyjson v0.7.6 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.17 // indirect + github.com/mattn/go-runewidth v0.0.14 // indirect + github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pelletier/go-toml/v2 v2.0.6 // indirect + github.com/rivo/uniseg v0.4.4 // indirect + github.com/schollz/progressbar/v3 v3.13.1 // indirect github.com/spf13/afero v1.9.3 // indirect github.com/spf13/cast v1.5.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.4.2 // indirect - golang.org/x/net v0.7.0 // indirect + golang.org/x/net v0.8.0 // indirect golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 // indirect golang.org/x/sys v0.6.0 // indirect - golang.org/x/text v0.7.0 // indirect + golang.org/x/text v0.8.0 // indirect golang.org/x/time v0.1.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.28.1 // indirect diff --git a/go.sum b/go.sum index 082db82654..8ba179cfbf 100644 --- a/go.sum +++ b/go.sum @@ -164,6 +164,7 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= @@ -185,6 +186,10 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= +github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= +github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -205,17 +210,16 @@ github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qR github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= +github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sashabaranov/go-openai v1.5.4 h1:I2K7JMIx/EC/mwT2fbypBzJ3OtwKNxaFg4jf3KOvXuc= -github.com/sashabaranov/go-openai v1.5.4/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg= -github.com/sashabaranov/go-openai v1.5.5 h1:VYdzEGVk4zV04ZNqNb1DT8w7JCzWM77h3h6pBH27B1k= -github.com/sashabaranov/go-openai v1.5.5/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg= -github.com/sashabaranov/go-openai v1.5.6 h1:i/DI9y1kzlPqKA0KeTYezJJSy01sqpOdUIm2BV7vgtA= -github.com/sashabaranov/go-openai v1.5.6/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg= github.com/sashabaranov/go-openai v1.5.7 h1:8DGgRG+P7yWixte5j720y6yiXgY3Hlgcd0gcpHdltfo= github.com/sashabaranov/go-openai v1.5.7/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg= +github.com/schollz/progressbar/v3 v3.13.1 h1:o8rySDYiQ59Mwzy2FELeHY5ZARXZTVJC7iHD6PEFUiE= +github.com/schollz/progressbar/v3 v3.13.1/go.mod h1:xvrbki8kfT1fzWzBT/UZd9L6GA+jdL7HAgq2RFnO6fQ= github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk= github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= @@ -325,8 +329,8 @@ golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -395,8 +399,8 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/images/Screenshot from 2023-03-27 09-27-51.png b/images/Screenshot from 2023-03-27 09-27-51.png new file mode 100644 index 0000000000..537630af35 Binary files /dev/null and b/images/Screenshot from 2023-03-27 09-27-51.png differ diff --git a/pkg/analyzer/analysis.go b/pkg/analyzer/analysis.go index 1a48eca3ce..99237df3aa 100644 --- a/pkg/analyzer/analysis.go +++ b/pkg/analyzer/analysis.go @@ -10,13 +10,13 @@ type PreAnalysis struct { FailureDetails []string ReplicaSet appsv1.ReplicaSet PersistentVolumeClaim v1.PersistentVolumeClaim - Endpoint v1.Endpoints + Endpoint v1.Endpoints } type Analysis struct { - Kind string `json:"kind"` - Name string `json:"name"` - Error string `json:"error"` - Details string `json:"details"` - ParentObject string `json:"parentObject"` + Kind string `json:"kind"` + Name string `json:"name"` + Error []string `json:"error"` + Details string `json:"details"` + ParentObject string `json:"parentObject"` } diff --git a/pkg/analyzer/podAnalyzer.go b/pkg/analyzer/podAnalyzer.go index 5b5b839c12..d12ea1c9e9 100644 --- a/pkg/analyzer/podAnalyzer.go +++ b/pkg/analyzer/podAnalyzer.go @@ -2,16 +2,11 @@ package analyzer import ( "context" - "encoding/base64" "fmt" - "strings" - "time" - "github.com/briandowns/spinner" - "github.com/fatih/color" "github.com/k8sgpt-ai/k8sgpt/pkg/ai" "github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes" - "github.com/spf13/viper" + "github.com/k8sgpt-ai/k8sgpt/pkg/util" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -70,108 +65,16 @@ func AnalyzePod(ctx context.Context, client *kubernetes.Client, aiClient ai.IAI, } for key, value := range preAnalysis { - inputValue := strings.Join(value.FailureDetails, " ") var currentAnalysis = Analysis{ Kind: "Pod", Name: key, - Error: value.FailureDetails[0], + Error: value.FailureDetails, } - parent, _ := getParent(client, value.Pod.ObjectMeta) - - if explain { - s := spinner.New(spinner.CharSets[35], 100*time.Millisecond) // Build our new spinner - s.Start() - - // Check for cached data - sEnc := base64.StdEncoding.EncodeToString([]byte(inputValue)) - // find in viper cache - if viper.IsSet(sEnc) { - s.Stop() - // retrieve data from cache - response := viper.GetString(sEnc) - if response == "" { - color.Red("error retrieving cached data") - continue - } - output, err := base64.StdEncoding.DecodeString(response) - if err != nil { - color.Red("error decoding cached data: %v", err) - continue - } - currentAnalysis.Details = string(output) - currentAnalysis.ParentObject = parent - *analysisResults = append(*analysisResults, currentAnalysis) - continue - } - - response, err := aiClient.GetCompletion(ctx, inputValue) - s.Stop() - if err != nil { - color.Red("error getting completion: %v", err) - continue - } - - if !viper.IsSet(sEnc) { - viper.Set(sEnc, base64.StdEncoding.EncodeToString([]byte(response))) - if err := viper.WriteConfig(); err != nil { - return err - } - } - currentAnalysis.Details = response - } + parent, _ := util.GetParent(client, value.Pod.ObjectMeta) currentAnalysis.ParentObject = parent *analysisResults = append(*analysisResults, currentAnalysis) } return nil } - -func getParent(client *kubernetes.Client, meta metav1.ObjectMeta) (string, bool) { - if meta.OwnerReferences != nil { - for _, owner := range meta.OwnerReferences { - switch owner.Kind { - case "ReplicaSet": - rs, err := client.GetClient().AppsV1().ReplicaSets(meta.Namespace).Get(context.Background(), owner.Name, metav1.GetOptions{}) - if err != nil { - return "", false - } - if rs.OwnerReferences != nil { - return getParent(client, rs.ObjectMeta) - } - return "ReplicaSet/" + rs.Name, false - - case "Deployment": - dep, err := client.GetClient().AppsV1().Deployments(meta.Namespace).Get(context.Background(), owner.Name, metav1.GetOptions{}) - if err != nil { - return "", false - } - if dep.OwnerReferences != nil { - return getParent(client, dep.ObjectMeta) - } - return "Deployment/" + dep.Name, false - - case "StatefulSet": - sts, err := client.GetClient().AppsV1().StatefulSets(meta.Namespace).Get(context.Background(), owner.Name, metav1.GetOptions{}) - if err != nil { - return "", false - } - if sts.OwnerReferences != nil { - return getParent(client, sts.ObjectMeta) - } - return "StatefulSet/" + sts.Name, false - - case "DaemonSet": - ds, err := client.GetClient().AppsV1().DaemonSets(meta.Namespace).Get(context.Background(), owner.Name, metav1.GetOptions{}) - if err != nil { - return "", false - } - if ds.OwnerReferences != nil { - return getParent(client, ds.ObjectMeta) - } - return "DaemonSet/" + ds.Name, false - } - } - } - return meta.Name, false -} diff --git a/pkg/analyzer/pvcAnalyzer.go b/pkg/analyzer/pvcAnalyzer.go index 5eedb99b50..791ae6ba6f 100644 --- a/pkg/analyzer/pvcAnalyzer.go +++ b/pkg/analyzer/pvcAnalyzer.go @@ -2,16 +2,11 @@ package analyzer import ( "context" - "encoding/base64" "fmt" - "strings" - "time" - "github.com/briandowns/spinner" - "github.com/fatih/color" "github.com/k8sgpt-ai/k8sgpt/pkg/ai" "github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes" - "github.com/spf13/viper" + "github.com/k8sgpt-ai/k8sgpt/pkg/util" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -52,54 +47,10 @@ func AnalyzePersistentVolumeClaim(ctx context.Context, client *kubernetes.Client var currentAnalysis = Analysis{ Kind: "PersistentVolumeClaim", Name: key, - Error: value.FailureDetails[0], + Error: value.FailureDetails, } - parent, _ := getParent(client, value.PersistentVolumeClaim.ObjectMeta) - - if explain { - s := spinner.New(spinner.CharSets[35], 100*time.Millisecond) // Build our new spinner - s.Start() - - inputValue := strings.Join(value.FailureDetails, " ") - - // Check for cached data - sEnc := base64.StdEncoding.EncodeToString([]byte(inputValue)) - // find in viper cache - if viper.IsSet(sEnc) { - s.Stop() - // retrieve data from cache - response := viper.GetString(sEnc) - if response == "" { - color.Red("error retrieving cached data") - continue - } - output, err := base64.StdEncoding.DecodeString(response) - if err != nil { - color.Red("error decoding cached data: %v", err) - continue - } - currentAnalysis.Details = string(output) - currentAnalysis.ParentObject = parent - *analysisResults = append(*analysisResults, currentAnalysis) - continue - } - - response, err := aiClient.GetCompletion(ctx, inputValue) - s.Stop() - if err != nil { - color.Red("error getting completion: %v", err) - continue - } - - if !viper.IsSet(sEnc) { - viper.Set(sEnc, base64.StdEncoding.EncodeToString([]byte(response))) - if err := viper.WriteConfig(); err != nil { - return err - } - } - currentAnalysis.Details = response - } + parent, _ := util.GetParent(client, value.PersistentVolumeClaim.ObjectMeta) currentAnalysis.ParentObject = parent *analysisResults = append(*analysisResults, currentAnalysis) } diff --git a/pkg/analyzer/rsAnalyzer.go b/pkg/analyzer/rsAnalyzer.go index 94e5bcea56..61fa7c799f 100644 --- a/pkg/analyzer/rsAnalyzer.go +++ b/pkg/analyzer/rsAnalyzer.go @@ -2,16 +2,11 @@ package analyzer import ( "context" - "encoding/base64" "fmt" - "strings" - "time" - "github.com/briandowns/spinner" - "github.com/fatih/color" "github.com/k8sgpt-ai/k8sgpt/pkg/ai" "github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes" - "github.com/spf13/viper" + "github.com/k8sgpt-ai/k8sgpt/pkg/util" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -50,54 +45,10 @@ func AnalyzeReplicaSet(ctx context.Context, client *kubernetes.Client, aiClient var currentAnalysis = Analysis{ Kind: "ReplicaSet", Name: key, - Error: value.FailureDetails[0], + Error: value.FailureDetails, } - parent, _ := getParent(client, value.ReplicaSet.ObjectMeta) - - if explain { - s := spinner.New(spinner.CharSets[35], 100*time.Millisecond) // Build our new spinner - s.Start() - - inputValue := strings.Join(value.FailureDetails, " ") - - // Check for cached data - sEnc := base64.StdEncoding.EncodeToString([]byte(inputValue)) - // find in viper cache - if viper.IsSet(sEnc) { - s.Stop() - // retrieve data from cache - response := viper.GetString(sEnc) - if response == "" { - color.Red("error retrieving cached data") - continue - } - output, err := base64.StdEncoding.DecodeString(response) - if err != nil { - color.Red("error decoding cached data: %v", err) - continue - } - currentAnalysis.Details = string(output) - currentAnalysis.ParentObject = parent - *analysisResults = append(*analysisResults, currentAnalysis) - continue - } - - response, err := aiClient.GetCompletion(ctx, inputValue) - s.Stop() - if err != nil { - color.Red("error getting completion: %v", err) - continue - } - - if !viper.IsSet(sEnc) { - viper.Set(sEnc, base64.StdEncoding.EncodeToString([]byte(response))) - if err := viper.WriteConfig(); err != nil { - return err - } - } - currentAnalysis.Details = response - } + parent, _ := util.GetParent(client, value.ReplicaSet.ObjectMeta) currentAnalysis.ParentObject = parent *analysisResults = append(*analysisResults, currentAnalysis) } diff --git a/pkg/analyzer/serviceAnalyzer.go b/pkg/analyzer/serviceAnalyzer.go index 085bb8ffc9..bc75a5fbd3 100644 --- a/pkg/analyzer/serviceAnalyzer.go +++ b/pkg/analyzer/serviceAnalyzer.go @@ -2,16 +2,11 @@ package analyzer import ( "context" - "encoding/base64" "fmt" - "strings" - "time" - "github.com/briandowns/spinner" - "github.com/fatih/color" "github.com/k8sgpt-ai/k8sgpt/pkg/ai" "github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes" - "github.com/spf13/viper" + "github.com/k8sgpt-ai/k8sgpt/pkg/util" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -66,54 +61,10 @@ func AnalyzeEndpoints(ctx context.Context, client *kubernetes.Client, aiClient a var currentAnalysis = Analysis{ Kind: "Service", Name: key, - Error: value.FailureDetails[0], + Error: value.FailureDetails, } - parent, _ := getParent(client, value.Endpoint.ObjectMeta) - - if explain { - s := spinner.New(spinner.CharSets[35], 100*time.Millisecond) // Build our new spinner - s.Start() - - inputValue := strings.Join(value.FailureDetails, " ") - - // Check for cached data - sEnc := base64.StdEncoding.EncodeToString([]byte(inputValue)) - // find in viper cache - if viper.IsSet(sEnc) { - s.Stop() - // retrieve data from cache - response := viper.GetString(sEnc) - if response == "" { - color.Red("error retrieving cached data") - continue - } - output, err := base64.StdEncoding.DecodeString(response) - if err != nil { - color.Red("error decoding cached data: %v", err) - continue - } - currentAnalysis.Details = string(output) - currentAnalysis.ParentObject = parent - *analysisResults = append(*analysisResults, currentAnalysis) - continue - } - - response, err := aiClient.GetCompletion(ctx, inputValue) - s.Stop() - if err != nil { - color.Red("error getting completion: %v", err) - continue - } - - if !viper.IsSet(sEnc) { - viper.Set(sEnc, base64.StdEncoding.EncodeToString([]byte(response))) - if err := viper.WriteConfig(); err != nil { - return err - } - } - currentAnalysis.Details = response - } + parent, _ := util.GetParent(client, value.Endpoint.ObjectMeta) currentAnalysis.ParentObject = parent *analysisResults = append(*analysisResults, currentAnalysis) } diff --git a/pkg/util/util.go b/pkg/util/util.go new file mode 100644 index 0000000000..2ad74c8af1 --- /dev/null +++ b/pkg/util/util.go @@ -0,0 +1,57 @@ +package util + +import ( + "context" + + "github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func GetParent(client *kubernetes.Client, meta metav1.ObjectMeta) (string, bool) { + if meta.OwnerReferences != nil { + for _, owner := range meta.OwnerReferences { + switch owner.Kind { + case "ReplicaSet": + rs, err := client.GetClient().AppsV1().ReplicaSets(meta.Namespace).Get(context.Background(), owner.Name, metav1.GetOptions{}) + if err != nil { + return "", false + } + if rs.OwnerReferences != nil { + return GetParent(client, rs.ObjectMeta) + } + return "ReplicaSet/" + rs.Name, false + + case "Deployment": + dep, err := client.GetClient().AppsV1().Deployments(meta.Namespace).Get(context.Background(), owner.Name, metav1.GetOptions{}) + if err != nil { + return "", false + } + if dep.OwnerReferences != nil { + return GetParent(client, dep.ObjectMeta) + } + return "Deployment/" + dep.Name, false + + case "StatefulSet": + sts, err := client.GetClient().AppsV1().StatefulSets(meta.Namespace).Get(context.Background(), owner.Name, metav1.GetOptions{}) + if err != nil { + return "", false + } + if sts.OwnerReferences != nil { + return GetParent(client, sts.ObjectMeta) + } + return "StatefulSet/" + sts.Name, false + + case "DaemonSet": + ds, err := client.GetClient().AppsV1().DaemonSets(meta.Namespace).Get(context.Background(), owner.Name, metav1.GetOptions{}) + if err != nil { + return "", false + } + if ds.OwnerReferences != nil { + return GetParent(client, ds.ObjectMeta) + } + return "DaemonSet/" + ds.Name, false + } + } + } + return meta.Name, false +}