mirror of
https://github.com/k8sgpt-ai/k8sgpt.git
synced 2025-09-27 07:55:00 +00:00
@@ -1,12 +1,10 @@
|
|||||||
package analyze
|
package analyze
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
|
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/analysis"
|
"github.com/k8sgpt-ai/k8sgpt/pkg/analysis"
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"os"
|
"os"
|
||||||
@@ -49,38 +47,24 @@ var AnalyzeCmd = &cobra.Command{
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
var aiClient ai.IAI
|
// AnalysisResult configuration
|
||||||
switch backendType {
|
|
||||||
case "openai":
|
aiClient, err := ai.NewAIClient("openai")
|
||||||
aiClient = &ai.OpenAIClient{}
|
if err != nil {
|
||||||
|
color.Red("Error: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
if err := aiClient.Configure(token, language); err != nil {
|
if err := aiClient.Configure(token, language); err != nil {
|
||||||
color.Red("Error: %v", err)
|
color.Red("Error: %v", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
default:
|
|
||||||
color.Red("Backend not supported")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := context.Background()
|
analysis := analysis.NewAnalysis(namespace, nocache, explain, filters, backend)
|
||||||
// Get kubernetes client from viper
|
|
||||||
client := viper.Get("kubernetesClient").(*kubernetes.Client)
|
|
||||||
// AnalysisResult configuration
|
|
||||||
|
|
||||||
analysis := &analysis.Analysis{
|
|
||||||
Context: ctx,
|
|
||||||
Namespace: namespace,
|
|
||||||
NoCache: nocache,
|
|
||||||
Explain: explain,
|
|
||||||
AIClient: aiClient,
|
|
||||||
Filters: filters,
|
|
||||||
Client: client,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run analysis
|
// Run analysis
|
||||||
_ = analysis.RunAnalysis()
|
_ = analysis.RunAnalysis()
|
||||||
|
|
||||||
analysis.PrintAnalysisResult()
|
analysis.PrintJsonResult()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,8 +1,25 @@
|
|||||||
package ai
|
package ai
|
||||||
|
|
||||||
import "context"
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"github.com/k8sgpt-ai/k8sgpt/pkg/ai/openai"
|
||||||
|
)
|
||||||
|
|
||||||
|
var AIProviderMap = map[string]IAI{
|
||||||
|
"openai": &openai.OpenAIClient{},
|
||||||
|
}
|
||||||
|
|
||||||
type IAI interface {
|
type IAI interface {
|
||||||
Configure(token string, language string) error
|
Configure(token string, language string) error
|
||||||
GetCompletion(ctx context.Context, prompt string) (string, error)
|
GetCompletion(ctx context.Context, prompt string) (string, error)
|
||||||
|
Parse(text string, prompt []string, nocache bool) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAIClient(provider string) (IAI, error) {
|
||||||
|
ai, ok := AIProviderMap[provider]
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("AI provider not found")
|
||||||
|
}
|
||||||
|
return ai, nil
|
||||||
}
|
}
|
||||||
|
@@ -1,9 +1,13 @@
|
|||||||
package ai
|
package openai
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/fatih/color"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/sashabaranov/go-openai"
|
"github.com/sashabaranov/go-openai"
|
||||||
)
|
)
|
||||||
@@ -16,8 +20,10 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type OpenAIClient struct {
|
type OpenAIClient struct {
|
||||||
|
context context.Context
|
||||||
client *openai.Client
|
client *openai.Client
|
||||||
language string
|
language string
|
||||||
|
nocache bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *OpenAIClient) Configure(token string, language string) error {
|
func (c *OpenAIClient) Configure(token string, language string) error {
|
||||||
@@ -46,3 +52,41 @@ func (c *OpenAIClient) GetCompletion(ctx context.Context, prompt string) (string
|
|||||||
}
|
}
|
||||||
return resp.Choices[0].Message.Content, nil
|
return resp.Choices[0].Message.Content, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c OpenAIClient) Parse(text string, prompt []string, nocache bool) (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) && !c.nocache {
|
||||||
|
// 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 := c.GetCompletion(c.context, inputKey)
|
||||||
|
if err != nil {
|
||||||
|
color.Red("error getting completion: %v", err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
@@ -2,6 +2,7 @@ package analysis
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
|
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer"
|
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer"
|
||||||
@@ -21,15 +22,39 @@ type Analysis struct {
|
|||||||
analysisResults []common.Result
|
analysisResults []common.Result
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Analysis) RunAnalysis() error {
|
func NewAnalysis(namespace string, noCache bool, explain bool, filters []string, aiProvider string) *Analysis {
|
||||||
|
var aiClient ai.IAI
|
||||||
|
var err error
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
client := viper.Get("kubernetesClient").(*kubernetes.Client)
|
||||||
|
|
||||||
|
if explain {
|
||||||
|
aiClient, err = ai.NewAIClient(aiProvider)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error creating AI client: ", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Analysis{
|
||||||
|
Context: ctx,
|
||||||
|
Namespace: namespace,
|
||||||
|
NoCache: noCache,
|
||||||
|
Explain: explain,
|
||||||
|
Filters: filters,
|
||||||
|
Client: client,
|
||||||
|
AIClient: aiClient,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Analysis) RunAnalysis() error {
|
||||||
activeFilters := viper.GetStringSlice("active_filters")
|
activeFilters := viper.GetStringSlice("active_filters")
|
||||||
analyzerList := analyzer.GetAnalyzerList()
|
analyzerList := analyzer.GetAnalyzerList()
|
||||||
|
|
||||||
// if there are no filters selected and no active_filters then run all of them
|
// if there are no filters selected and no active_filters then run all of them
|
||||||
if len(a.Filters) == 0 && len(activeFilters) == 0 {
|
if len(a.Filters) == 0 && len(activeFilters) == 0 {
|
||||||
for _, al := range analyzerList {
|
for _, al := range analyzerList {
|
||||||
thisanalysis, _ := analyzer.NewAnalyzer(al, a.Client, a.Context, a.Namespace)
|
thisanalysis, _ := analyzer.NewAnalyzer(al, a.Client, a.Context, a.Namespace, a.AIClient, a.Explain)
|
||||||
err := thisanalysis.Analyze()
|
err := thisanalysis.Analyze()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Error running analysis: ", err)
|
fmt.Println("Error running analysis: ", err)
|
||||||
@@ -44,7 +69,7 @@ func (a *Analysis) RunAnalysis() error {
|
|||||||
for _, filter := range a.Filters {
|
for _, filter := range a.Filters {
|
||||||
for _, ali := range analyzerList {
|
for _, ali := range analyzerList {
|
||||||
if filter == ali {
|
if filter == ali {
|
||||||
thisanalysis, _ := analyzer.NewAnalyzer(ali, a.Client, a.Context, a.Namespace)
|
thisanalysis, _ := analyzer.NewAnalyzer(ali, a.Client, a.Context, a.Namespace, a.AIClient, a.Explain)
|
||||||
err := thisanalysis.Analyze()
|
err := thisanalysis.Analyze()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Error running analysis: ", err)
|
fmt.Println("Error running analysis: ", err)
|
||||||
@@ -63,3 +88,11 @@ func (a *Analysis) PrintAnalysisResult() {
|
|||||||
fmt.Println(result)
|
fmt.Println(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *Analysis) PrintJsonResult() {
|
||||||
|
output, err := json.MarshalIndent(a.analysisResults, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error marshalling json: ", err)
|
||||||
|
}
|
||||||
|
fmt.Println(string(output))
|
||||||
|
}
|
||||||
|
@@ -2,7 +2,7 @@ package analyzer
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer/common"
|
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer/common"
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer/hpa"
|
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer/hpa"
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer/ingress"
|
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer/ingress"
|
||||||
@@ -18,109 +18,31 @@ type IAnalyzer interface {
|
|||||||
GetResult() []common.Result
|
GetResult() []common.Result
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
var AnalyzerMap = map[string]IAnalyzer{
|
||||||
PodAnalyzerName = "Pod"
|
"Pod": &pod.PodAnalyzer{},
|
||||||
ReplicaSetAnalyzerName = "ReplicaSet"
|
"ReplicaSet": &rs.ReplicaSetAnalyzer{},
|
||||||
PersistentVolumeClaimAnalyzerName = "PersistentVolumeClaim"
|
"PersistentVolumeClaim": &pvc.PvcAnalyzer{},
|
||||||
ServiceAnalyzerName = "Service"
|
"Service": &service.ServiceAnalyzer{},
|
||||||
IngressAnalyzerName = "Ingress"
|
"Ingress": &ingress.IngressAnalyzer{},
|
||||||
HPAAnalyzerName = "HorizontalPodAutoScaler"
|
"HPA": &hpa.HPAAnalyzer{},
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
coreAnalyzerList = []string{
|
|
||||||
PodAnalyzerName,
|
|
||||||
ReplicaSetAnalyzerName,
|
|
||||||
PersistentVolumeClaimAnalyzerName,
|
|
||||||
ServiceAnalyzerName,
|
|
||||||
IngressAnalyzerName,
|
|
||||||
HPAAnalyzerName,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
additionalAnalyzers = []string{
|
var coreAnalyzerList = []string{"Pod", "ReplicaSet", "PersistentVolumeClaim", "Service", "Ingress"}
|
||||||
HPAAnalyzerName,
|
var additionalAnalyzerList = []string{"HPA"}
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewAnalyzer(analyzer string, client *kubernetes.Client, context context.Context, namespace string) (IAnalyzer, error) {
|
func NewAnalyzer(analyzer string, client *kubernetes.Client, context context.Context, namespace string, aiClient ai.IAI, explain bool) (IAnalyzer, error) {
|
||||||
analyzerConfig := common.Analyzer{
|
analyzerConfig := common.Analyzer{
|
||||||
|
AIClient: aiClient,
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
Context: context,
|
Context: context,
|
||||||
Client: client,
|
Client: client,
|
||||||
|
Explain: explain,
|
||||||
}
|
}
|
||||||
|
|
||||||
analyzerConfig.PreAnalysis = make(map[string]common.PreAnalysis)
|
analyzerConfig.PreAnalysis = make(map[string]common.PreAnalysis)
|
||||||
|
return AnalyzerMap[analyzer], nil
|
||||||
switch analyzer {
|
|
||||||
case PodAnalyzerName:
|
|
||||||
return &pod.PodAnalyzer{
|
|
||||||
Analyzer: analyzerConfig,
|
|
||||||
}, nil
|
|
||||||
case ReplicaSetAnalyzerName:
|
|
||||||
return &rs.ReplicaSetAnalyzer{
|
|
||||||
Analyzer: analyzerConfig,
|
|
||||||
}, nil
|
|
||||||
case IngressAnalyzerName:
|
|
||||||
return &ingress.IngressAnalyzer{
|
|
||||||
Analyzer: analyzerConfig,
|
|
||||||
}, nil
|
|
||||||
case HPAAnalyzerName:
|
|
||||||
return &hpa.HPAAnalyzer{
|
|
||||||
Analyzer: analyzerConfig,
|
|
||||||
}, nil
|
|
||||||
case PersistentVolumeClaimAnalyzerName:
|
|
||||||
return &pvc.PvcAnalyzer{
|
|
||||||
Analyzer: analyzerConfig,
|
|
||||||
}, nil
|
|
||||||
case ServiceAnalyzerName:
|
|
||||||
return &service.ServiceAnalyzer{
|
|
||||||
Analyzer: analyzerConfig,
|
|
||||||
}, nil
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("Analyzer %s not supported", analyzer)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
func ParseViaAI(ctx context.Context, config *analysis.Analysis,
|
|
||||||
|
|
||||||
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) && !config.NoCache {
|
|
||||||
// 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 "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
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 ListFilters() ([]string, []string) {
|
func ListFilters() ([]string, []string) {
|
||||||
coreKeys := []string{}
|
coreKeys := []string{}
|
||||||
for _, filter := range coreAnalyzerList {
|
for _, filter := range coreAnalyzerList {
|
||||||
@@ -128,7 +50,7 @@ func ListFilters() ([]string, []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
additionalKeys := []string{}
|
additionalKeys := []string{}
|
||||||
for _, filter := range additionalAnalyzers {
|
for _, filter := range coreAnalyzerList {
|
||||||
coreKeys = append(additionalKeys, filter)
|
coreKeys = append(additionalKeys, filter)
|
||||||
}
|
}
|
||||||
return coreKeys, additionalKeys
|
return coreKeys, additionalKeys
|
||||||
@@ -138,13 +60,12 @@ func GetAnalyzerList() []string {
|
|||||||
list := []string{}
|
list := []string{}
|
||||||
|
|
||||||
list = append(list, coreAnalyzerList...)
|
list = append(list, coreAnalyzerList...)
|
||||||
list = append(list, additionalAnalyzers...)
|
list = append(list, additionalAnalyzerList...)
|
||||||
|
|
||||||
list = removeDuplicateStr(list)
|
list = removeDuplicateStr(list)
|
||||||
|
|
||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeDuplicateStr(strSlice []string) []string {
|
func removeDuplicateStr(strSlice []string) []string {
|
||||||
allKeys := make(map[string]bool)
|
allKeys := make(map[string]bool)
|
||||||
list := []string{}
|
list := []string{}
|
||||||
|
@@ -2,6 +2,7 @@ package common
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
||||||
appsv1 "k8s.io/api/apps/v1"
|
appsv1 "k8s.io/api/apps/v1"
|
||||||
autov1 "k8s.io/api/autoscaling/v1"
|
autov1 "k8s.io/api/autoscaling/v1"
|
||||||
@@ -11,9 +12,12 @@ import (
|
|||||||
|
|
||||||
type Analyzer struct {
|
type Analyzer struct {
|
||||||
Client *kubernetes.Client
|
Client *kubernetes.Client
|
||||||
|
AIClient ai.IAI
|
||||||
Context context.Context
|
Context context.Context
|
||||||
Namespace string
|
Namespace string
|
||||||
PreAnalysis map[string]PreAnalysis
|
PreAnalysis map[string]PreAnalysis
|
||||||
|
Explain bool
|
||||||
|
NoCache bool
|
||||||
Result []Result
|
Result []Result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -59,7 +59,6 @@ func (a *HPAAnalyzer) Analyze() error {
|
|||||||
FailureDetails: failures,
|
FailureDetails: failures,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for key, value := range a.PreAnalysis {
|
for key, value := range a.PreAnalysis {
|
||||||
@@ -68,7 +67,6 @@ func (a *HPAAnalyzer) Analyze() error {
|
|||||||
Name: key,
|
Name: key,
|
||||||
Error: value.FailureDetails,
|
Error: value.FailureDetails,
|
||||||
}
|
}
|
||||||
|
|
||||||
parent, _ := util.GetParent(a.Client, value.Ingress.ObjectMeta)
|
parent, _ := util.GetParent(a.Client, value.Ingress.ObjectMeta)
|
||||||
currentAnalysis.ParentObject = parent
|
currentAnalysis.ParentObject = parent
|
||||||
a.Result = append(a.Result, currentAnalysis)
|
a.Result = append(a.Result, currentAnalysis)
|
||||||
|
Reference in New Issue
Block a user