k8sgpt/cmd/analyze/analyze.go
Yicheng a79224e2bf
feat: add verbose flag to enable detailed output (#1420)
* feat: add verbose flag to enable detailed output

Signed-off-by: Yicheng <36285652+zyc140345@users.noreply.github.com>

* test: add verbose output tests for analysis.go and root.go

Signed-off-by: Yicheng <36285652+zyc140345@users.noreply.github.com>

---------

Signed-off-by: Yicheng <36285652+zyc140345@users.noreply.github.com>
2025-04-14 13:06:24 +01:00

181 lines
5.9 KiB
Go

/*
Copyright 2023 The K8sGPT Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package analyze
import (
"fmt"
"os"
"os/signal"
"syscall"
"github.com/fatih/color"
"github.com/k8sgpt-ai/k8sgpt/pkg/ai/interactive"
"github.com/k8sgpt-ai/k8sgpt/pkg/analysis"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var (
explain bool
backend string
output string
filters []string
language string
nocache bool
namespace string
labelSelector string
anonymize bool
maxConcurrency int
withDoc bool
interactiveMode bool
customAnalysis bool
customHeaders []string
withStats bool
)
// 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) {
// Create analysis configuration first.
config, err := analysis.NewAnalysis(
backend,
language,
filters,
namespace,
labelSelector,
nocache,
explain,
maxConcurrency,
withDoc,
interactiveMode,
customHeaders,
withStats,
)
verbose := viper.GetBool("verbose")
if verbose {
fmt.Println("Debug: Checking analysis configuration.")
}
if err != nil {
color.Red("Error: %v", err)
os.Exit(1)
}
if verbose {
fmt.Println("Debug: Analysis initialized.")
}
defer config.Close()
if customAnalysis {
config.RunCustomAnalysis()
if verbose {
fmt.Println("Debug: All custom analyzers completed.")
}
}
config.RunAnalysis()
if verbose {
fmt.Println("Debug: All core analyzers completed.")
}
if explain {
err := config.GetAIResults(output, anonymize)
if verbose {
fmt.Println("Debug: Checking AI results.")
}
if err != nil {
color.Red("Error: %v", err)
os.Exit(1)
}
}
// print results
output_data, err := config.PrintOutput(output)
if verbose {
fmt.Println("Debug: Checking output.")
}
if err != nil {
color.Red("Error: %v", err)
os.Exit(1)
}
if withStats {
statsData := config.PrintStats()
fmt.Println(string(statsData))
}
fmt.Println(string(output_data))
if interactiveMode && explain {
if output == "json" {
color.Yellow("Caution: interactive mode using --json enabled may use additional tokens.")
}
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
interactiveClient := interactive.NewInteractionRunner(config, output_data)
go interactiveClient.StartInteraction()
for {
select {
case res := <-sigs:
switch res {
default:
os.Exit(0)
}
case res := <-interactiveClient.State:
switch res {
case interactive.E_EXITED:
os.Exit(0)
}
}
}
}
},
}
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")
// anonymize flag
AnalyzeCmd.Flags().BoolVarP(&anonymize, "anonymize", "a", false, "Anonymize data before sending it to the AI backend. This flag masks sensitive data, such as Kubernetes object names and labels, by replacing it with a key. However, please note that this flag does not currently apply to events.")
// array of strings flag
AnalyzeCmd.Flags().StringSliceVarP(&filters, "filter", "f", []string{}, "Filter for these analyzers (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", "", "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')")
// add max concurrency
AnalyzeCmd.Flags().IntVarP(&maxConcurrency, "max-concurrency", "m", 10, "Maximum number of concurrent requests to the Kubernetes API server")
// kubernetes doc flag
AnalyzeCmd.Flags().BoolVarP(&withDoc, "with-doc", "d", false, "Give me the official documentation of the involved field")
// interactive mode flag
AnalyzeCmd.Flags().BoolVarP(&interactiveMode, "interactive", "i", false, "Enable interactive mode that allows further conversation with LLM about the problem. Works only with --explain flag")
// custom analysis flag
AnalyzeCmd.Flags().BoolVarP(&customAnalysis, "custom-analysis", "z", false, "Enable custom analyzers")
// add custom headers flag
AnalyzeCmd.Flags().StringSliceVarP(&customHeaders, "custom-headers", "r", []string{}, "Custom Headers, <key>:<value> (e.g CustomHeaderKey:CustomHeaderValue AnotherHeader:AnotherValue)")
// label selector flag
AnalyzeCmd.Flags().StringVarP(&labelSelector, "selector", "L", "", "Label selector (label query) to filter on, supports '=', '==', and '!='. (e.g. -L key1=value1,key2=value2). Matching objects must satisfy all of the specified label constraints.")
// print stats
AnalyzeCmd.Flags().BoolVarP(&withStats, "with-stat", "s", false, "Print analysis stats. This option disables errors display.")
}