mirror of
https://github.com/k8sgpt-ai/k8sgpt.git
synced 2025-07-30 22:46:57 +00:00
chore: analyzer and ai interfacing (#200)
* chore: analyzer and ai interfacing Signed-off-by: Thomas Schuetz <thomas.schuetz@t-sc.eu> * fix: backend variable for aiProvider in cmd Signed-off-by: Thomas Schuetz <thomas.schuetz@t-sc.eu> * fix: changed folders for analyzers Signed-off-by: Thomas Schuetz <thomas.schuetz@t-sc.eu> * chore: renamed analyzers Signed-off-by: Thomas Schuetz <thomas.schuetz@t-sc.eu> * fix: fixed ingress tests after rebase Signed-off-by: Thomas Schuetz <thomas.schuetz@t-sc.eu> * fix: fixed ingress tests after rebase Signed-off-by: Thomas Schuetz <thomas.schuetz@t-sc.eu> --------- Signed-off-by: Thomas Schuetz <thomas.schuetz@t-sc.eu>
This commit is contained in:
parent
eeac731858
commit
0195bfab30
@ -4,12 +4,13 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/analysis"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer"
|
||||
"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"
|
||||
@ -53,50 +54,44 @@ var AnalyzeCmd = &cobra.Command{
|
||||
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")
|
||||
aiClient := ai.NewClient(backendType)
|
||||
if err := aiClient.Configure(token, language); err != nil {
|
||||
color.Red("Error: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
// Get kubernetes client from viper
|
||||
client := viper.Get("kubernetesClient").(*kubernetes.Client)
|
||||
// Analysis configuration
|
||||
config := &analyzer.AnalysisConfiguration{
|
||||
// AnalysisResult configuration
|
||||
config := &analysis.Analysis{
|
||||
Namespace: namespace,
|
||||
NoCache: nocache,
|
||||
Explain: explain,
|
||||
AIClient: aiClient,
|
||||
Client: client,
|
||||
Context: ctx,
|
||||
}
|
||||
|
||||
var analysisResults *[]analyzer.Analysis = &[]analyzer.Analysis{}
|
||||
if err := analyzer.RunAnalysis(ctx, filters, config, client,
|
||||
aiClient, analysisResults); err != nil {
|
||||
err := config.RunAnalysis()
|
||||
if err != nil {
|
||||
color.Red("Error: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if len(*analysisResults) == 0 {
|
||||
if len(config.Results) == 0 {
|
||||
color.Green("{ \"status\": \"OK\" }")
|
||||
os.Exit(0)
|
||||
}
|
||||
var bar = progressbar.Default(int64(len(*analysisResults)))
|
||||
var bar = progressbar.Default(int64(len(config.Results)))
|
||||
if !explain {
|
||||
bar.Clear()
|
||||
}
|
||||
var printOutput []analyzer.Analysis
|
||||
|
||||
for _, analysis := range *analysisResults {
|
||||
var printOutput []analyzer.Result
|
||||
|
||||
for _, analysis := range config.Results {
|
||||
if explain {
|
||||
parsedText, err := analyzer.ParseViaAI(ctx, config, aiClient, analysis.Error)
|
||||
parsedText, err := aiClient.Parse(ctx, analysis.Error, nocache)
|
||||
if err != nil {
|
||||
// Check for exhaustion
|
||||
if strings.Contains(err.Error(), "status code: 429") {
|
||||
|
41
pkg/ai/ai.go
41
pkg/ai/ai.go
@ -2,8 +2,12 @@ package ai
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/fatih/color"
|
||||
"github.com/spf13/viper"
|
||||
"strings"
|
||||
|
||||
"github.com/sashabaranov/go-openai"
|
||||
)
|
||||
@ -46,3 +50,40 @@ func (c *OpenAIClient) GetCompletion(ctx context.Context, prompt string) (string
|
||||
}
|
||||
return resp.Choices[0].Message.Content, nil
|
||||
}
|
||||
|
||||
func (a *OpenAIClient) Parse(ctx context.Context, 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) && !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 := a.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
|
||||
}
|
||||
|
@ -1,8 +1,20 @@
|
||||
package ai
|
||||
|
||||
import "context"
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type IAI interface {
|
||||
Configure(token string, language string) error
|
||||
GetCompletion(ctx context.Context, prompt string) (string, error)
|
||||
Parse(ctx context.Context, prompt []string, nocache bool) (string, error)
|
||||
}
|
||||
|
||||
func NewClient(provider string) IAI {
|
||||
switch provider {
|
||||
case "openai":
|
||||
return &OpenAIClient{}
|
||||
default:
|
||||
return &OpenAIClient{}
|
||||
}
|
||||
}
|
||||
|
72
pkg/analysis/analysis.go
Normal file
72
pkg/analysis/analysis.go
Normal file
@ -0,0 +1,72 @@
|
||||
package analysis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type Analysis struct {
|
||||
Context context.Context
|
||||
Filters []string
|
||||
Client *kubernetes.Client
|
||||
AIClient ai.IAI
|
||||
Results []analyzer.Result
|
||||
Namespace string
|
||||
NoCache bool
|
||||
Explain bool
|
||||
}
|
||||
|
||||
func (a *Analysis) RunAnalysis() error {
|
||||
|
||||
activeFilters := viper.GetStringSlice("active_filters")
|
||||
|
||||
analyzerMap := analyzer.GetAnalyzerMap()
|
||||
|
||||
analyzerConfig := analyzer.Analyzer{
|
||||
Client: a.Client,
|
||||
Context: a.Context,
|
||||
Namespace: a.Namespace,
|
||||
AIClient: a.AIClient,
|
||||
}
|
||||
|
||||
// if there are no filters selected and no active_filters then run all of them
|
||||
if len(a.Filters) == 0 && len(activeFilters) == 0 {
|
||||
for _, analyzer := range analyzerMap {
|
||||
results, err := analyzer.Analyze(analyzerConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.Results = append(a.Results, results...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// if the filters flag is specified
|
||||
if len(a.Filters) != 0 {
|
||||
for _, filter := range a.Filters {
|
||||
if analyzer, ok := analyzerMap[filter]; ok {
|
||||
results, err := analyzer.Analyze(analyzerConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.Results = append(a.Results, results...)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// use active_filters
|
||||
for _, filter := range activeFilters {
|
||||
if analyzer, ok := analyzerMap[filter]; ok {
|
||||
results, err := analyzer.Analyze(analyzerConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.Results = append(a.Results, results...)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
@ -1,15 +1,8 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
type IAnalyzer interface {
|
||||
Analyze(analysis Analyzer) ([]Result, error)
|
||||
}
|
||||
|
||||
var coreAnalyzerMap = map[string]IAnalyzer{
|
||||
"Pod": PodAnalyzer{},
|
||||
@ -24,85 +17,6 @@ var additionalAnalyzerMap = map[string]IAnalyzer{
|
||||
"PodDisruptionBudget": PdbAnalyzer{},
|
||||
}
|
||||
|
||||
func RunAnalysis(ctx context.Context, filters []string, config *AnalysisConfiguration,
|
||||
client *kubernetes.Client,
|
||||
aiClient ai.IAI, analysisResults *[]Analysis) error {
|
||||
|
||||
activeFilters := viper.GetStringSlice("active_filters")
|
||||
|
||||
analyzerMap := getAnalyzerMap()
|
||||
|
||||
// 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.RunAnalysis(ctx, config, client, aiClient, analysisResults); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// if the filters flag is specified
|
||||
if len(filters) != 0 {
|
||||
for _, filter := range filters {
|
||||
if analyzer, ok := analyzerMap[filter]; ok {
|
||||
if err := analyzer.RunAnalysis(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.RunAnalysis(ctx, config, client, aiClient, analysisResults); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ParseViaAI(ctx context.Context, config *AnalysisConfiguration,
|
||||
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) {
|
||||
coreKeys := make([]string, 0, len(coreAnalyzerMap))
|
||||
for k := range coreAnalyzerMap {
|
||||
@ -116,7 +30,7 @@ func ListFilters() ([]string, []string) {
|
||||
return coreKeys, additionalKeys
|
||||
}
|
||||
|
||||
func getAnalyzerMap() map[string]IAnalyzer {
|
||||
func GetAnalyzerMap() map[string]IAnalyzer {
|
||||
|
||||
mergedMap := make(map[string]IAnalyzer)
|
||||
|
||||
|
@ -2,7 +2,6 @@ package analyzer
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
@ -1,23 +1,18 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
type HpaAnalyzer struct{}
|
||||
|
||||
func (HpaAnalyzer) RunAnalysis(ctx context.Context, config *AnalysisConfiguration, client *kubernetes.Client, aiClient ai.IAI,
|
||||
analysisResults *[]Analysis) error {
|
||||
func (HpaAnalyzer) Analyze(a Analyzer) ([]Result, error) {
|
||||
|
||||
list, err := client.GetClient().AutoscalingV1().HorizontalPodAutoscalers(config.Namespace).List(ctx, metav1.ListOptions{})
|
||||
list, err := a.Client.GetClient().AutoscalingV1().HorizontalPodAutoscalers(a.Namespace).List(a.Context, metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var preAnalysis = map[string]PreAnalysis{}
|
||||
@ -31,22 +26,22 @@ func (HpaAnalyzer) RunAnalysis(ctx context.Context, config *AnalysisConfiguratio
|
||||
|
||||
switch scaleTargetRef.Kind {
|
||||
case "Deployment":
|
||||
_, err := client.GetClient().AppsV1().Deployments(config.Namespace).Get(ctx, scaleTargetRef.Name, metav1.GetOptions{})
|
||||
_, err := a.Client.GetClient().AppsV1().Deployments(a.Namespace).Get(a.Context, scaleTargetRef.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
scaleTargetRefNotFound = true
|
||||
}
|
||||
case "ReplicationController":
|
||||
_, err := client.GetClient().CoreV1().ReplicationControllers(config.Namespace).Get(ctx, scaleTargetRef.Name, metav1.GetOptions{})
|
||||
_, err := a.Client.GetClient().CoreV1().ReplicationControllers(a.Namespace).Get(a.Context, scaleTargetRef.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
scaleTargetRefNotFound = true
|
||||
}
|
||||
case "ReplicaSet":
|
||||
_, err := client.GetClient().AppsV1().ReplicaSets(config.Namespace).Get(ctx, scaleTargetRef.Name, metav1.GetOptions{})
|
||||
_, err := a.Client.GetClient().AppsV1().ReplicaSets(a.Namespace).Get(a.Context, scaleTargetRef.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
scaleTargetRefNotFound = true
|
||||
}
|
||||
case "StatefulSet":
|
||||
_, err := client.GetClient().AppsV1().StatefulSets(config.Namespace).Get(ctx, scaleTargetRef.Name, metav1.GetOptions{})
|
||||
_, err := a.Client.GetClient().AppsV1().StatefulSets(a.Namespace).Get(a.Context, scaleTargetRef.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
scaleTargetRefNotFound = true
|
||||
}
|
||||
@ -68,16 +63,16 @@ func (HpaAnalyzer) RunAnalysis(ctx context.Context, config *AnalysisConfiguratio
|
||||
}
|
||||
|
||||
for key, value := range preAnalysis {
|
||||
var currentAnalysis = Analysis{
|
||||
var currentAnalysis = Result{
|
||||
Kind: "HorizontalPodAutoscaler",
|
||||
Name: key,
|
||||
Error: value.FailureDetails,
|
||||
}
|
||||
|
||||
parent, _ := util.GetParent(client, value.HorizontalPodAutoscalers.ObjectMeta)
|
||||
parent, _ := util.GetParent(a.Client, value.HorizontalPodAutoscalers.ObjectMeta)
|
||||
currentAnalysis.ParentObject = parent
|
||||
*analysisResults = append(*analysisResults, currentAnalysis)
|
||||
a.Results = append(a.Results, currentAnalysis)
|
||||
}
|
||||
|
||||
return nil
|
||||
return a.Results, nil
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
||||
)
|
||||
|
||||
type IAnalyzer interface {
|
||||
RunAnalysis(ctx context.Context, config *AnalysisConfiguration, client *kubernetes.Client, aiClient ai.IAI,
|
||||
analysisResults *[]Analysis) error
|
||||
}
|
@ -1,23 +1,18 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
type IngressAnalyzer struct{}
|
||||
|
||||
func (IngressAnalyzer) RunAnalysis(ctx context.Context, config *AnalysisConfiguration, client *kubernetes.Client, aiClient ai.IAI,
|
||||
analysisResults *[]Analysis) error {
|
||||
func (IngressAnalyzer) Analyze(a Analyzer) ([]Result, error) {
|
||||
|
||||
list, err := client.GetClient().NetworkingV1().Ingresses(config.Namespace).List(ctx, metav1.ListOptions{})
|
||||
list, err := a.Client.GetClient().NetworkingV1().Ingresses(a.Namespace).List(a.Context, metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var preAnalysis = map[string]PreAnalysis{}
|
||||
@ -38,7 +33,7 @@ func (IngressAnalyzer) RunAnalysis(ctx context.Context, config *AnalysisConfigur
|
||||
|
||||
// check if ingressclass exist
|
||||
if ingressClassName != nil {
|
||||
_, err := client.GetClient().NetworkingV1().IngressClasses().Get(ctx, *ingressClassName, metav1.GetOptions{})
|
||||
_, err := a.Client.GetClient().NetworkingV1().IngressClasses().Get(a.Context, *ingressClassName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
failures = append(failures, fmt.Sprintf("Ingress uses the ingress class %s which does not exist.", *ingressClassName))
|
||||
}
|
||||
@ -48,7 +43,7 @@ func (IngressAnalyzer) RunAnalysis(ctx context.Context, config *AnalysisConfigur
|
||||
for _, rule := range ing.Spec.Rules {
|
||||
// loop over paths
|
||||
for _, path := range rule.HTTP.Paths {
|
||||
_, err := client.GetClient().CoreV1().Services(ing.Namespace).Get(ctx, path.Backend.Service.Name, metav1.GetOptions{})
|
||||
_, err := a.Client.GetClient().CoreV1().Services(ing.Namespace).Get(a.Context, path.Backend.Service.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
failures = append(failures, fmt.Sprintf("Ingress uses the service %s/%s which does not exist.", ing.Namespace, path.Backend.Service.Name))
|
||||
}
|
||||
@ -56,7 +51,7 @@ func (IngressAnalyzer) RunAnalysis(ctx context.Context, config *AnalysisConfigur
|
||||
}
|
||||
|
||||
for _, tls := range ing.Spec.TLS {
|
||||
_, err := client.GetClient().CoreV1().Secrets(ing.Namespace).Get(ctx, tls.SecretName, metav1.GetOptions{})
|
||||
_, err := a.Client.GetClient().CoreV1().Secrets(ing.Namespace).Get(a.Context, tls.SecretName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
failures = append(failures, fmt.Sprintf("Ingress uses the secret %s/%s as a TLS certificate which does not exist.", ing.Namespace, tls.SecretName))
|
||||
}
|
||||
@ -71,16 +66,16 @@ func (IngressAnalyzer) RunAnalysis(ctx context.Context, config *AnalysisConfigur
|
||||
}
|
||||
|
||||
for key, value := range preAnalysis {
|
||||
var currentAnalysis = Analysis{
|
||||
var currentAnalysis = Result{
|
||||
Kind: "Ingress",
|
||||
Name: key,
|
||||
Error: value.FailureDetails,
|
||||
}
|
||||
|
||||
parent, _ := util.GetParent(client, value.Ingress.ObjectMeta)
|
||||
parent, _ := util.GetParent(a.Client, value.Ingress.ObjectMeta)
|
||||
currentAnalysis.ParentObject = parent
|
||||
*analysisResults = append(*analysisResults, currentAnalysis)
|
||||
a.Results = append(a.Results, currentAnalysis)
|
||||
}
|
||||
|
||||
return nil
|
||||
return a.Results, nil
|
||||
}
|
@ -22,16 +22,17 @@ func TestIngressAnalyzer(t *testing.T) {
|
||||
},
|
||||
})
|
||||
ingressAnalyzer := IngressAnalyzer{}
|
||||
var analysisResults []Analysis
|
||||
err := ingressAnalyzer.RunAnalysis(context.Background(),
|
||||
&AnalysisConfiguration{
|
||||
Namespace: "default",
|
||||
},
|
||||
&kubernetes.Client{
|
||||
|
||||
config := Analyzer{
|
||||
Client: &kubernetes.Client{
|
||||
Client: clientset,
|
||||
}, nil, &analysisResults)
|
||||
},
|
||||
Context: context.Background(),
|
||||
Namespace: "default",
|
||||
}
|
||||
analysisResults, err := ingressAnalyzer.Analyze(config)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
t.Error(err)
|
||||
}
|
||||
assert.Equal(t, len(analysisResults), 1)
|
||||
}
|
||||
@ -54,16 +55,18 @@ func TestIngressAnalyzerWithMultipleIngresses(t *testing.T) {
|
||||
},
|
||||
)
|
||||
ingressAnalyzer := IngressAnalyzer{}
|
||||
var analysisResults []Analysis
|
||||
err := ingressAnalyzer.RunAnalysis(context.Background(),
|
||||
&AnalysisConfiguration{
|
||||
Namespace: "default",
|
||||
},
|
||||
&kubernetes.Client{
|
||||
|
||||
config := Analyzer{
|
||||
Client: &kubernetes.Client{
|
||||
Client: clientset,
|
||||
}, nil, &analysisResults)
|
||||
},
|
||||
Context: context.Background(),
|
||||
Namespace: "default",
|
||||
}
|
||||
|
||||
analysisResults, err := ingressAnalyzer.Analyze(config)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
t.Error(err)
|
||||
}
|
||||
assert.Equal(t, len(analysisResults), 2)
|
||||
}
|
||||
@ -80,16 +83,17 @@ func TestIngressAnalyzerWithoutIngressClassAnnotation(t *testing.T) {
|
||||
})
|
||||
ingressAnalyzer := IngressAnalyzer{}
|
||||
|
||||
var analysisResults []Analysis
|
||||
err := ingressAnalyzer.RunAnalysis(context.Background(),
|
||||
&AnalysisConfiguration{
|
||||
Namespace: "default",
|
||||
},
|
||||
&kubernetes.Client{
|
||||
config := Analyzer{
|
||||
Client: &kubernetes.Client{
|
||||
Client: clientset,
|
||||
}, nil, &analysisResults)
|
||||
},
|
||||
Context: context.Background(),
|
||||
Namespace: "default",
|
||||
}
|
||||
|
||||
analysisResults, err := ingressAnalyzer.Analyze(config)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
var errorFound bool
|
@ -1,23 +1,18 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
type PdbAnalyzer struct{}
|
||||
|
||||
func (PdbAnalyzer) RunAnalysis(ctx context.Context, config *AnalysisConfiguration, client *kubernetes.Client, aiClient ai.IAI,
|
||||
analysisResults *[]Analysis) error {
|
||||
func (PdbAnalyzer) Analyze(a Analyzer) ([]Result, error) {
|
||||
|
||||
list, err := client.GetClient().PolicyV1().PodDisruptionBudgets(config.Namespace).List(ctx, metav1.ListOptions{})
|
||||
list, err := a.Client.GetClient().PolicyV1().PodDisruptionBudgets(a.Namespace).List(a.Context, metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var preAnalysis = map[string]PreAnalysis{}
|
||||
@ -25,7 +20,7 @@ func (PdbAnalyzer) RunAnalysis(ctx context.Context, config *AnalysisConfiguratio
|
||||
for _, pdb := range list.Items {
|
||||
var failures []string
|
||||
|
||||
evt, err := FetchLatestEvent(ctx, client, pdb.Namespace, pdb.Name)
|
||||
evt, err := FetchLatestEvent(a.Context, a.Client, pdb.Namespace, pdb.Name)
|
||||
if err != nil || evt == nil {
|
||||
continue
|
||||
}
|
||||
@ -52,16 +47,16 @@ func (PdbAnalyzer) RunAnalysis(ctx context.Context, config *AnalysisConfiguratio
|
||||
}
|
||||
|
||||
for key, value := range preAnalysis {
|
||||
var currentAnalysis = Analysis{
|
||||
var currentAnalysis = Result{
|
||||
Kind: "PodDisruptionBudget",
|
||||
Name: key,
|
||||
Error: value.FailureDetails,
|
||||
}
|
||||
|
||||
parent, _ := util.GetParent(client, value.PodDisruptionBudget.ObjectMeta)
|
||||
parent, _ := util.GetParent(a.Client, value.PodDisruptionBudget.ObjectMeta)
|
||||
currentAnalysis.ParentObject = parent
|
||||
*analysisResults = append(*analysisResults, currentAnalysis)
|
||||
a.Results = append(a.Results, currentAnalysis)
|
||||
}
|
||||
|
||||
return nil
|
||||
return a.Results, err
|
||||
}
|
@ -1,24 +1,19 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
type PodAnalyzer struct{}
|
||||
|
||||
func (PodAnalyzer) RunAnalysis(ctx context.Context, config *AnalysisConfiguration,
|
||||
client *kubernetes.Client, aiClient ai.IAI, analysisResults *[]Analysis) error {
|
||||
type PodAnalyzer struct {
|
||||
}
|
||||
|
||||
func (PodAnalyzer) Analyze(a Analyzer) ([]Result, error) {
|
||||
// search all namespaces for pods that are not running
|
||||
list, err := client.GetClient().CoreV1().Pods(config.Namespace).List(ctx, metav1.ListOptions{})
|
||||
list, err := a.Client.GetClient().CoreV1().Pods(a.Namespace).List(a.Context, metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
var preAnalysis = map[string]PreAnalysis{}
|
||||
|
||||
@ -49,7 +44,7 @@ func (PodAnalyzer) RunAnalysis(ctx context.Context, config *AnalysisConfiguratio
|
||||
if containerStatus.State.Waiting.Reason == "ContainerCreating" && pod.Status.Phase == "Pending" {
|
||||
|
||||
// parse the event log and append details
|
||||
evt, err := FetchLatestEvent(ctx, client, pod.Namespace, pod.Name)
|
||||
evt, err := FetchLatestEvent(a.Context, a.Client, pod.Namespace, pod.Name)
|
||||
if err != nil || evt == nil {
|
||||
continue
|
||||
}
|
||||
@ -68,16 +63,16 @@ func (PodAnalyzer) RunAnalysis(ctx context.Context, config *AnalysisConfiguratio
|
||||
}
|
||||
|
||||
for key, value := range preAnalysis {
|
||||
var currentAnalysis = Analysis{
|
||||
var currentAnalysis = Result{
|
||||
Kind: "Pod",
|
||||
Name: key,
|
||||
Error: value.FailureDetails,
|
||||
}
|
||||
|
||||
parent, _ := util.GetParent(client, value.Pod.ObjectMeta)
|
||||
parent, _ := util.GetParent(a.Client, value.Pod.ObjectMeta)
|
||||
currentAnalysis.ParentObject = parent
|
||||
*analysisResults = append(*analysisResults, currentAnalysis)
|
||||
a.Results = append(a.Results, currentAnalysis)
|
||||
}
|
||||
|
||||
return nil
|
||||
return a.Results, nil
|
||||
}
|
@ -11,7 +11,7 @@ import (
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
)
|
||||
|
||||
func TestPodAnalzyer(t *testing.T) {
|
||||
func TestPodAnalyzer(t *testing.T) {
|
||||
|
||||
clientset := fake.NewSimpleClientset(&v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@ -31,17 +31,18 @@ func TestPodAnalzyer(t *testing.T) {
|
||||
},
|
||||
})
|
||||
|
||||
podAnalyzer := PodAnalyzer{}
|
||||
var analysisResults []Analysis
|
||||
err := podAnalyzer.RunAnalysis(context.Background(),
|
||||
&AnalysisConfiguration{
|
||||
Namespace: "default",
|
||||
},
|
||||
&kubernetes.Client{
|
||||
config := Analyzer{
|
||||
Client: &kubernetes.Client{
|
||||
Client: clientset,
|
||||
}, nil, &analysisResults)
|
||||
},
|
||||
Context: context.Background(),
|
||||
Namespace: "default",
|
||||
}
|
||||
podAnalyzer := PodAnalyzer{}
|
||||
var analysisResults []Result
|
||||
analysisResults, err := podAnalyzer.Analyze(config)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
t.Error(err)
|
||||
}
|
||||
assert.Equal(t, len(analysisResults), 1)
|
||||
}
|
@ -1,23 +1,19 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
type PvcAnalyzer struct{}
|
||||
|
||||
func (PvcAnalyzer) RunAnalysis(ctx context.Context, config *AnalysisConfiguration, client *kubernetes.Client, aiClient ai.IAI, analysisResults *[]Analysis) error {
|
||||
func (PvcAnalyzer) Analyze(a Analyzer) ([]Result, error) {
|
||||
|
||||
// search all namespaces for pods that are not running
|
||||
list, err := client.GetClient().CoreV1().PersistentVolumeClaims(config.Namespace).List(ctx, metav1.ListOptions{})
|
||||
list, err := a.Client.GetClient().CoreV1().PersistentVolumeClaims(a.Namespace).List(a.Context, metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var preAnalysis = map[string]PreAnalysis{}
|
||||
@ -29,7 +25,7 @@ func (PvcAnalyzer) RunAnalysis(ctx context.Context, config *AnalysisConfiguratio
|
||||
if pvc.Status.Phase == "Pending" {
|
||||
|
||||
// parse the event log and append details
|
||||
evt, err := FetchLatestEvent(ctx, client, pvc.Namespace, pvc.Name)
|
||||
evt, err := FetchLatestEvent(a.Context, a.Client, pvc.Namespace, pvc.Name)
|
||||
if err != nil || evt == nil {
|
||||
continue
|
||||
}
|
||||
@ -46,16 +42,16 @@ func (PvcAnalyzer) RunAnalysis(ctx context.Context, config *AnalysisConfiguratio
|
||||
}
|
||||
|
||||
for key, value := range preAnalysis {
|
||||
var currentAnalysis = Analysis{
|
||||
var currentAnalysis = Result{
|
||||
Kind: "PersistentVolumeClaim",
|
||||
Name: key,
|
||||
Error: value.FailureDetails,
|
||||
}
|
||||
|
||||
parent, _ := util.GetParent(client, value.PersistentVolumeClaim.ObjectMeta)
|
||||
parent, _ := util.GetParent(a.Client, value.PersistentVolumeClaim.ObjectMeta)
|
||||
currentAnalysis.ParentObject = parent
|
||||
*analysisResults = append(*analysisResults, currentAnalysis)
|
||||
a.Results = append(a.Results, currentAnalysis)
|
||||
}
|
||||
|
||||
return nil
|
||||
return a.Results, nil
|
||||
}
|
@ -1,24 +1,19 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
type ReplicaSetAnalyzer struct{}
|
||||
|
||||
func (ReplicaSetAnalyzer) RunAnalysis(ctx context.Context, config *AnalysisConfiguration,
|
||||
client *kubernetes.Client, aiClient ai.IAI, analysisResults *[]Analysis) error {
|
||||
func (ReplicaSetAnalyzer) Analyze(a Analyzer) ([]Result, error) {
|
||||
|
||||
// search all namespaces for pods that are not running
|
||||
list, err := client.GetClient().AppsV1().ReplicaSets(config.Namespace).List(ctx, metav1.ListOptions{})
|
||||
list, err := a.Client.GetClient().AppsV1().ReplicaSets(a.Namespace).List(a.Context, metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var preAnalysis = map[string]PreAnalysis{}
|
||||
@ -45,16 +40,15 @@ func (ReplicaSetAnalyzer) RunAnalysis(ctx context.Context, config *AnalysisConfi
|
||||
}
|
||||
|
||||
for key, value := range preAnalysis {
|
||||
var currentAnalysis = Analysis{
|
||||
var currentAnalysis = Result{
|
||||
Kind: "ReplicaSet",
|
||||
Name: key,
|
||||
Error: value.FailureDetails,
|
||||
}
|
||||
|
||||
parent, _ := util.GetParent(client, value.ReplicaSet.ObjectMeta)
|
||||
parent, _ := util.GetParent(a.Client, value.ReplicaSet.ObjectMeta)
|
||||
currentAnalysis.ParentObject = parent
|
||||
*analysisResults = append(*analysisResults, currentAnalysis)
|
||||
a.Results = append(a.Results, currentAnalysis)
|
||||
}
|
||||
|
||||
return nil
|
||||
return a.Results, nil
|
||||
}
|
@ -1,25 +1,20 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
type ServiceAnalyzer struct{}
|
||||
|
||||
func (ServiceAnalyzer) RunAnalysis(ctx context.Context, config *AnalysisConfiguration, client *kubernetes.Client, aiClient ai.IAI,
|
||||
analysisResults *[]Analysis) error {
|
||||
func (ServiceAnalyzer) Analyze(a Analyzer) ([]Result, error) {
|
||||
|
||||
// search all namespaces for pods that are not running
|
||||
list, err := client.GetClient().CoreV1().Endpoints(config.Namespace).List(ctx, metav1.ListOptions{})
|
||||
list, err := a.Client.GetClient().CoreV1().Endpoints(a.Namespace).List(a.Context, metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var preAnalysis = map[string]PreAnalysis{}
|
||||
@ -29,7 +24,7 @@ func (ServiceAnalyzer) RunAnalysis(ctx context.Context, config *AnalysisConfigur
|
||||
|
||||
// Check for empty service
|
||||
if len(ep.Subsets) == 0 {
|
||||
svc, err := client.GetClient().CoreV1().Services(ep.Namespace).Get(ctx, ep.Name, metav1.GetOptions{})
|
||||
svc, err := a.Client.GetClient().CoreV1().Services(ep.Namespace).Get(a.Context, ep.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
color.Yellow("Service %s/%s does not exist", ep.Namespace, ep.Name)
|
||||
continue
|
||||
@ -63,15 +58,15 @@ func (ServiceAnalyzer) RunAnalysis(ctx context.Context, config *AnalysisConfigur
|
||||
}
|
||||
|
||||
for key, value := range preAnalysis {
|
||||
var currentAnalysis = Analysis{
|
||||
var currentAnalysis = Result{
|
||||
Kind: "Service",
|
||||
Name: key,
|
||||
Error: value.FailureDetails,
|
||||
}
|
||||
|
||||
parent, _ := util.GetParent(client, value.Endpoint.ObjectMeta)
|
||||
parent, _ := util.GetParent(a.Client, value.Endpoint.ObjectMeta)
|
||||
currentAnalysis.ParentObject = parent
|
||||
*analysisResults = append(*analysisResults, currentAnalysis)
|
||||
a.Results = append(a.Results, currentAnalysis)
|
||||
}
|
||||
return nil
|
||||
return a.Results, nil
|
||||
}
|
@ -11,7 +11,7 @@ import (
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
)
|
||||
|
||||
func TestServiceAnalzyer(t *testing.T) {
|
||||
func TestServiceAnalyzer(t *testing.T) {
|
||||
|
||||
clientset := fake.NewSimpleClientset(&v1.Endpoints{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@ -32,17 +32,18 @@ func TestServiceAnalzyer(t *testing.T) {
|
||||
},
|
||||
}})
|
||||
|
||||
serviceAnalyzer := ServiceAnalyzer{}
|
||||
var analysisResults []Analysis
|
||||
err := serviceAnalyzer.RunAnalysis(context.Background(),
|
||||
&AnalysisConfiguration{
|
||||
Namespace: "default",
|
||||
},
|
||||
&kubernetes.Client{
|
||||
config := Analyzer{
|
||||
Client: &kubernetes.Client{
|
||||
Client: clientset,
|
||||
}, nil, &analysisResults)
|
||||
},
|
||||
Context: context.Background(),
|
||||
Namespace: "default",
|
||||
}
|
||||
|
||||
serviceAnalyzer := ServiceAnalyzer{}
|
||||
analysisResults, err := serviceAnalyzer.Analyze(config)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
t.Error(err)
|
||||
}
|
||||
assert.Equal(t, len(analysisResults), 1)
|
||||
}
|
@ -1,17 +1,23 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
autoscalingv1 "k8s.io/api/autoscaling/v1"
|
||||
autov1 "k8s.io/api/autoscaling/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
networkv1 "k8s.io/api/networking/v1"
|
||||
policyv1 "k8s.io/api/policy/v1"
|
||||
)
|
||||
|
||||
type AnalysisConfiguration struct {
|
||||
Namespace string
|
||||
NoCache bool
|
||||
Explain bool
|
||||
type Analyzer struct {
|
||||
Client *kubernetes.Client
|
||||
Context context.Context
|
||||
Namespace string
|
||||
AIClient ai.IAI
|
||||
PreAnalysis map[string]PreAnalysis
|
||||
Results []Result
|
||||
}
|
||||
|
||||
type PreAnalysis struct {
|
||||
@ -20,12 +26,12 @@ type PreAnalysis struct {
|
||||
ReplicaSet appsv1.ReplicaSet
|
||||
PersistentVolumeClaim v1.PersistentVolumeClaim
|
||||
Endpoint v1.Endpoints
|
||||
Ingress networkingv1.Ingress
|
||||
HorizontalPodAutoscalers autoscalingv1.HorizontalPodAutoscaler
|
||||
Ingress networkv1.Ingress
|
||||
HorizontalPodAutoscalers autov1.HorizontalPodAutoscaler
|
||||
PodDisruptionBudget policyv1.PodDisruptionBudget
|
||||
}
|
||||
|
||||
type Analysis struct {
|
||||
type Result struct {
|
||||
Kind string `json:"kind"`
|
||||
Name string `json:"name"`
|
||||
Error []string `json:"error"`
|
Loading…
Reference in New Issue
Block a user