k8sgpt/cmd/analyze/analyze.go
Alex Jones b78ab3d9b5
feat: added namespace filter (#127)
* feat: added namespace filter

Signed-off-by: AlexsJones <alexsimonjones@gmail.com>

* chore: Moved the explain bool out

Signed-off-by: AlexsJones <alexsimonjones@gmail.com>

---------

Signed-off-by: AlexsJones <alexsimonjones@gmail.com>
2023-03-28 21:10:42 +02:00

167 lines
4.8 KiB
Go

package analyze
import (
"context"
"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"
)
var (
explain bool
backend string
output string
filters []string
language string
nocache bool
namespace string
)
// AnalyzeCmd represents the problems command
var AnalyzeCmd = &cobra.Command{
Use: "analyze",
Aliases: []string{"analyse"},
Short: "This command will find problems within your Kubernetes cluster",
Long: `This command will find problems within your Kubernetes cluster and
provide you with a list of issues that need to be resolved`,
Run: func(cmd *cobra.Command, args []string) {
// get backend from file
backendType := viper.GetString("backend_type")
if backendType == "" {
color.Red("No backend set. Please run k8sgpt auth")
os.Exit(1)
}
// override the default backend if a flag is provided
if backend != "" {
backendType = backend
}
// get the token with viper
token := viper.GetString(fmt.Sprintf("%s_key", backendType))
// check if nil
if token == "" {
color.Red("No %s key set. Please run k8sgpt auth", backendType)
os.Exit(1)
}
var aiClient ai.IAI
switch backendType {
case "openai":
aiClient = &ai.OpenAIClient{}
if err := aiClient.Configure(token, language); err != nil {
color.Red("Error: %v", err)
os.Exit(1)
}
default:
color.Red("Backend not supported")
os.Exit(1)
}
ctx := context.Background()
// Get kubernetes client from viper
client := viper.Get("kubernetesClient").(*kubernetes.Client)
// Analysis configuration
config := &analyzer.AnalysisConfiguration{
Namespace: namespace,
NoCache: nocache,
Explain: explain,
}
var analysisResults *[]analyzer.Analysis = &[]analyzer.Analysis{}
if err := analyzer.RunAnalysis(ctx, config, client,
aiClient, analysisResults); err != nil {
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)))
} else {
color.Green("{ \"status\": \"OK\" }")
os.Exit(0)
}
// This variable is used to store the results that will be printed
// It's necessary because the heap memory is lost when the function returns
var printOutput []analyzer.Analysis
for _, analysis := range *analysisResults {
if explain {
parsedText, err := analyzer.ParseViaAI(ctx, config, aiClient, analysis.Error)
if err != nil {
// Check for exhaustion
if strings.Contains(err.Error(), "status code: 429") {
color.Red("Exhausted API quota. Please try again later")
os.Exit(1)
}
color.Red("Error: %v", err)
continue
}
analysis.Details = parsedText
bar.Add(1)
}
printOutput = append(printOutput, analysis)
}
// print results
for n, analysis := range printOutput {
switch output {
case "json":
analysis.Error = analysis.Error[0:]
j, err := json.Marshal(analysis)
if err != nil {
color.Red("Error: %v", err)
os.Exit(1)
}
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[0]), color.GreenString(analysis.Details))
}
}
},
}
func init() {
// namespace flag
AnalyzeCmd.Flags().StringVarP(&namespace, "namespace", "n", "", "Namespace to analyze")
// no cache flag
AnalyzeCmd.Flags().BoolVarP(&nocache, "no-cache", "c", false, "Do not use cached data")
// array of strings flag
AnalyzeCmd.Flags().StringSliceVarP(&filters, "filter", "f", []string{}, "Filter for these analzyers (e.g. Pod,PersistentVolumeClaim,Service,ReplicaSet)")
// explain flag
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")
// output as json
AnalyzeCmd.Flags().StringVarP(&output, "output", "o", "text", "Output format (text, json)")
// add language options for output
AnalyzeCmd.Flags().StringVarP(&language, "language", "l", "english", "Languages to use for AI (e.g. 'English', 'Spanish', 'French', 'German', 'Italian', 'Portuguese', 'Dutch', 'Russian', 'Chinese', 'Japanese', 'Korean')")
}