mirror of
https://github.com/k8sgpt-ai/k8sgpt.git
synced 2025-08-10 12:11:41 +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"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/k8sgpt-ai/k8sgpt/pkg/analysis"
|
||||||
|
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"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/analyzer"
|
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
||||||
"github.com/schollz/progressbar/v3"
|
"github.com/schollz/progressbar/v3"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@ -53,50 +54,44 @@ var AnalyzeCmd = &cobra.Command{
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
var aiClient ai.IAI
|
aiClient := ai.NewClient(backendType)
|
||||||
switch backendType {
|
if err := aiClient.Configure(token, language); err != nil {
|
||||||
case "openai":
|
color.Red("Error: %v", err)
|
||||||
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)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
// Get kubernetes client from viper
|
// Get kubernetes client from viper
|
||||||
client := viper.Get("kubernetesClient").(*kubernetes.Client)
|
client := viper.Get("kubernetesClient").(*kubernetes.Client)
|
||||||
// Analysis configuration
|
// AnalysisResult configuration
|
||||||
config := &analyzer.AnalysisConfiguration{
|
config := &analysis.Analysis{
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
NoCache: nocache,
|
NoCache: nocache,
|
||||||
Explain: explain,
|
Explain: explain,
|
||||||
|
AIClient: aiClient,
|
||||||
|
Client: client,
|
||||||
|
Context: ctx,
|
||||||
}
|
}
|
||||||
|
|
||||||
var analysisResults *[]analyzer.Analysis = &[]analyzer.Analysis{}
|
err := config.RunAnalysis()
|
||||||
if err := analyzer.RunAnalysis(ctx, filters, config, client,
|
if err != nil {
|
||||||
aiClient, analysisResults); err != nil {
|
|
||||||
color.Red("Error: %v", err)
|
color.Red("Error: %v", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(*analysisResults) == 0 {
|
if len(config.Results) == 0 {
|
||||||
color.Green("{ \"status\": \"OK\" }")
|
color.Green("{ \"status\": \"OK\" }")
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
var bar = progressbar.Default(int64(len(*analysisResults)))
|
var bar = progressbar.Default(int64(len(config.Results)))
|
||||||
if !explain {
|
if !explain {
|
||||||
bar.Clear()
|
bar.Clear()
|
||||||
}
|
}
|
||||||
var printOutput []analyzer.Analysis
|
var printOutput []analyzer.Result
|
||||||
|
|
||||||
for _, analysis := range *analysisResults {
|
|
||||||
|
|
||||||
|
for _, analysis := range config.Results {
|
||||||
if explain {
|
if explain {
|
||||||
parsedText, err := analyzer.ParseViaAI(ctx, config, aiClient, analysis.Error)
|
parsedText, err := aiClient.Parse(ctx, analysis.Error, nocache)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Check for exhaustion
|
// Check for exhaustion
|
||||||
if strings.Contains(err.Error(), "status code: 429") {
|
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 (
|
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"
|
||||||
)
|
)
|
||||||
@ -46,3 +50,40 @@ 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 (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
|
package ai
|
||||||
|
|
||||||
import "context"
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
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(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
|
package analyzer
|
||||||
|
|
||||||
import (
|
type IAnalyzer interface {
|
||||||
"context"
|
Analyze(analysis Analyzer) ([]Result, error)
|
||||||
"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"
|
|
||||||
)
|
|
||||||
|
|
||||||
var coreAnalyzerMap = map[string]IAnalyzer{
|
var coreAnalyzerMap = map[string]IAnalyzer{
|
||||||
"Pod": PodAnalyzer{},
|
"Pod": PodAnalyzer{},
|
||||||
@ -24,85 +17,6 @@ var additionalAnalyzerMap = map[string]IAnalyzer{
|
|||||||
"PodDisruptionBudget": PdbAnalyzer{},
|
"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) {
|
func ListFilters() ([]string, []string) {
|
||||||
coreKeys := make([]string, 0, len(coreAnalyzerMap))
|
coreKeys := make([]string, 0, len(coreAnalyzerMap))
|
||||||
for k := range coreAnalyzerMap {
|
for k := range coreAnalyzerMap {
|
||||||
@ -116,7 +30,7 @@ func ListFilters() ([]string, []string) {
|
|||||||
return coreKeys, additionalKeys
|
return coreKeys, additionalKeys
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAnalyzerMap() map[string]IAnalyzer {
|
func GetAnalyzerMap() map[string]IAnalyzer {
|
||||||
|
|
||||||
mergedMap := make(map[string]IAnalyzer)
|
mergedMap := make(map[string]IAnalyzer)
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@ package analyzer
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
@ -1,23 +1,18 @@
|
|||||||
package analyzer
|
package analyzer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
|
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
|
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type HpaAnalyzer struct{}
|
type HpaAnalyzer struct{}
|
||||||
|
|
||||||
func (HpaAnalyzer) RunAnalysis(ctx context.Context, config *AnalysisConfiguration, client *kubernetes.Client, aiClient ai.IAI,
|
func (HpaAnalyzer) Analyze(a Analyzer) ([]Result, error) {
|
||||||
analysisResults *[]Analysis) 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 {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var preAnalysis = map[string]PreAnalysis{}
|
var preAnalysis = map[string]PreAnalysis{}
|
||||||
@ -31,22 +26,22 @@ func (HpaAnalyzer) RunAnalysis(ctx context.Context, config *AnalysisConfiguratio
|
|||||||
|
|
||||||
switch scaleTargetRef.Kind {
|
switch scaleTargetRef.Kind {
|
||||||
case "Deployment":
|
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 {
|
if err != nil {
|
||||||
scaleTargetRefNotFound = true
|
scaleTargetRefNotFound = true
|
||||||
}
|
}
|
||||||
case "ReplicationController":
|
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 {
|
if err != nil {
|
||||||
scaleTargetRefNotFound = true
|
scaleTargetRefNotFound = true
|
||||||
}
|
}
|
||||||
case "ReplicaSet":
|
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 {
|
if err != nil {
|
||||||
scaleTargetRefNotFound = true
|
scaleTargetRefNotFound = true
|
||||||
}
|
}
|
||||||
case "StatefulSet":
|
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 {
|
if err != nil {
|
||||||
scaleTargetRefNotFound = true
|
scaleTargetRefNotFound = true
|
||||||
}
|
}
|
||||||
@ -68,16 +63,16 @@ func (HpaAnalyzer) RunAnalysis(ctx context.Context, config *AnalysisConfiguratio
|
|||||||
}
|
}
|
||||||
|
|
||||||
for key, value := range preAnalysis {
|
for key, value := range preAnalysis {
|
||||||
var currentAnalysis = Analysis{
|
var currentAnalysis = Result{
|
||||||
Kind: "HorizontalPodAutoscaler",
|
Kind: "HorizontalPodAutoscaler",
|
||||||
Name: key,
|
Name: key,
|
||||||
Error: value.FailureDetails,
|
Error: value.FailureDetails,
|
||||||
}
|
}
|
||||||
|
|
||||||
parent, _ := util.GetParent(client, value.HorizontalPodAutoscalers.ObjectMeta)
|
parent, _ := util.GetParent(a.Client, value.HorizontalPodAutoscalers.ObjectMeta)
|
||||||
currentAnalysis.ParentObject = parent
|
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
|
package analyzer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
|
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
|
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type IngressAnalyzer struct{}
|
type IngressAnalyzer struct{}
|
||||||
|
|
||||||
func (IngressAnalyzer) RunAnalysis(ctx context.Context, config *AnalysisConfiguration, client *kubernetes.Client, aiClient ai.IAI,
|
func (IngressAnalyzer) Analyze(a Analyzer) ([]Result, error) {
|
||||||
analysisResults *[]Analysis) 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 {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var preAnalysis = map[string]PreAnalysis{}
|
var preAnalysis = map[string]PreAnalysis{}
|
||||||
@ -38,7 +33,7 @@ func (IngressAnalyzer) RunAnalysis(ctx context.Context, config *AnalysisConfigur
|
|||||||
|
|
||||||
// check if ingressclass exist
|
// check if ingressclass exist
|
||||||
if ingressClassName != nil {
|
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 {
|
if err != nil {
|
||||||
failures = append(failures, fmt.Sprintf("Ingress uses the ingress class %s which does not exist.", *ingressClassName))
|
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 {
|
for _, rule := range ing.Spec.Rules {
|
||||||
// loop over paths
|
// loop over paths
|
||||||
for _, path := range rule.HTTP.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 {
|
if err != nil {
|
||||||
failures = append(failures, fmt.Sprintf("Ingress uses the service %s/%s which does not exist.", ing.Namespace, path.Backend.Service.Name))
|
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 {
|
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 {
|
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))
|
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 {
|
for key, value := range preAnalysis {
|
||||||
var currentAnalysis = Analysis{
|
var currentAnalysis = Result{
|
||||||
Kind: "Ingress",
|
Kind: "Ingress",
|
||||||
Name: key,
|
Name: key,
|
||||||
Error: value.FailureDetails,
|
Error: value.FailureDetails,
|
||||||
}
|
}
|
||||||
|
|
||||||
parent, _ := util.GetParent(client, value.Ingress.ObjectMeta)
|
parent, _ := util.GetParent(a.Client, value.Ingress.ObjectMeta)
|
||||||
currentAnalysis.ParentObject = parent
|
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{}
|
ingressAnalyzer := IngressAnalyzer{}
|
||||||
var analysisResults []Analysis
|
|
||||||
err := ingressAnalyzer.RunAnalysis(context.Background(),
|
config := Analyzer{
|
||||||
&AnalysisConfiguration{
|
Client: &kubernetes.Client{
|
||||||
Namespace: "default",
|
|
||||||
},
|
|
||||||
&kubernetes.Client{
|
|
||||||
Client: clientset,
|
Client: clientset,
|
||||||
}, nil, &analysisResults)
|
},
|
||||||
|
Context: context.Background(),
|
||||||
|
Namespace: "default",
|
||||||
|
}
|
||||||
|
analysisResults, err := ingressAnalyzer.Analyze(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
assert.Equal(t, len(analysisResults), 1)
|
assert.Equal(t, len(analysisResults), 1)
|
||||||
}
|
}
|
||||||
@ -54,16 +55,18 @@ func TestIngressAnalyzerWithMultipleIngresses(t *testing.T) {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
ingressAnalyzer := IngressAnalyzer{}
|
ingressAnalyzer := IngressAnalyzer{}
|
||||||
var analysisResults []Analysis
|
|
||||||
err := ingressAnalyzer.RunAnalysis(context.Background(),
|
config := Analyzer{
|
||||||
&AnalysisConfiguration{
|
Client: &kubernetes.Client{
|
||||||
Namespace: "default",
|
|
||||||
},
|
|
||||||
&kubernetes.Client{
|
|
||||||
Client: clientset,
|
Client: clientset,
|
||||||
}, nil, &analysisResults)
|
},
|
||||||
|
Context: context.Background(),
|
||||||
|
Namespace: "default",
|
||||||
|
}
|
||||||
|
|
||||||
|
analysisResults, err := ingressAnalyzer.Analyze(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
assert.Equal(t, len(analysisResults), 2)
|
assert.Equal(t, len(analysisResults), 2)
|
||||||
}
|
}
|
||||||
@ -80,16 +83,17 @@ func TestIngressAnalyzerWithoutIngressClassAnnotation(t *testing.T) {
|
|||||||
})
|
})
|
||||||
ingressAnalyzer := IngressAnalyzer{}
|
ingressAnalyzer := IngressAnalyzer{}
|
||||||
|
|
||||||
var analysisResults []Analysis
|
config := Analyzer{
|
||||||
err := ingressAnalyzer.RunAnalysis(context.Background(),
|
Client: &kubernetes.Client{
|
||||||
&AnalysisConfiguration{
|
|
||||||
Namespace: "default",
|
|
||||||
},
|
|
||||||
&kubernetes.Client{
|
|
||||||
Client: clientset,
|
Client: clientset,
|
||||||
}, nil, &analysisResults)
|
},
|
||||||
|
Context: context.Background(),
|
||||||
|
Namespace: "default",
|
||||||
|
}
|
||||||
|
|
||||||
|
analysisResults, err := ingressAnalyzer.Analyze(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var errorFound bool
|
var errorFound bool
|
@ -1,23 +1,18 @@
|
|||||||
package analyzer
|
package analyzer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
|
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
|
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PdbAnalyzer struct{}
|
type PdbAnalyzer struct{}
|
||||||
|
|
||||||
func (PdbAnalyzer) RunAnalysis(ctx context.Context, config *AnalysisConfiguration, client *kubernetes.Client, aiClient ai.IAI,
|
func (PdbAnalyzer) Analyze(a Analyzer) ([]Result, error) {
|
||||||
analysisResults *[]Analysis) 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 {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var preAnalysis = map[string]PreAnalysis{}
|
var preAnalysis = map[string]PreAnalysis{}
|
||||||
@ -25,7 +20,7 @@ func (PdbAnalyzer) RunAnalysis(ctx context.Context, config *AnalysisConfiguratio
|
|||||||
for _, pdb := range list.Items {
|
for _, pdb := range list.Items {
|
||||||
var failures []string
|
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 {
|
if err != nil || evt == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -52,16 +47,16 @@ func (PdbAnalyzer) RunAnalysis(ctx context.Context, config *AnalysisConfiguratio
|
|||||||
}
|
}
|
||||||
|
|
||||||
for key, value := range preAnalysis {
|
for key, value := range preAnalysis {
|
||||||
var currentAnalysis = Analysis{
|
var currentAnalysis = Result{
|
||||||
Kind: "PodDisruptionBudget",
|
Kind: "PodDisruptionBudget",
|
||||||
Name: key,
|
Name: key,
|
||||||
Error: value.FailureDetails,
|
Error: value.FailureDetails,
|
||||||
}
|
}
|
||||||
|
|
||||||
parent, _ := util.GetParent(client, value.PodDisruptionBudget.ObjectMeta)
|
parent, _ := util.GetParent(a.Client, value.PodDisruptionBudget.ObjectMeta)
|
||||||
currentAnalysis.ParentObject = parent
|
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
|
package analyzer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
|
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
|
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PodAnalyzer struct{}
|
type PodAnalyzer struct {
|
||||||
|
}
|
||||||
func (PodAnalyzer) RunAnalysis(ctx context.Context, config *AnalysisConfiguration,
|
|
||||||
client *kubernetes.Client, aiClient ai.IAI, analysisResults *[]Analysis) error {
|
|
||||||
|
|
||||||
|
func (PodAnalyzer) Analyze(a Analyzer) ([]Result, error) {
|
||||||
// search all namespaces for pods that are not running
|
// 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 {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
var preAnalysis = map[string]PreAnalysis{}
|
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" {
|
if containerStatus.State.Waiting.Reason == "ContainerCreating" && pod.Status.Phase == "Pending" {
|
||||||
|
|
||||||
// parse the event log and append details
|
// 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 {
|
if err != nil || evt == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -68,16 +63,16 @@ func (PodAnalyzer) RunAnalysis(ctx context.Context, config *AnalysisConfiguratio
|
|||||||
}
|
}
|
||||||
|
|
||||||
for key, value := range preAnalysis {
|
for key, value := range preAnalysis {
|
||||||
var currentAnalysis = Analysis{
|
var currentAnalysis = Result{
|
||||||
Kind: "Pod",
|
Kind: "Pod",
|
||||||
Name: key,
|
Name: key,
|
||||||
Error: value.FailureDetails,
|
Error: value.FailureDetails,
|
||||||
}
|
}
|
||||||
|
|
||||||
parent, _ := util.GetParent(client, value.Pod.ObjectMeta)
|
parent, _ := util.GetParent(a.Client, value.Pod.ObjectMeta)
|
||||||
currentAnalysis.ParentObject = parent
|
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"
|
"k8s.io/client-go/kubernetes/fake"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPodAnalzyer(t *testing.T) {
|
func TestPodAnalyzer(t *testing.T) {
|
||||||
|
|
||||||
clientset := fake.NewSimpleClientset(&v1.Pod{
|
clientset := fake.NewSimpleClientset(&v1.Pod{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
@ -31,17 +31,18 @@ func TestPodAnalzyer(t *testing.T) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
podAnalyzer := PodAnalyzer{}
|
config := Analyzer{
|
||||||
var analysisResults []Analysis
|
Client: &kubernetes.Client{
|
||||||
err := podAnalyzer.RunAnalysis(context.Background(),
|
|
||||||
&AnalysisConfiguration{
|
|
||||||
Namespace: "default",
|
|
||||||
},
|
|
||||||
&kubernetes.Client{
|
|
||||||
Client: clientset,
|
Client: clientset,
|
||||||
}, nil, &analysisResults)
|
},
|
||||||
|
Context: context.Background(),
|
||||||
|
Namespace: "default",
|
||||||
|
}
|
||||||
|
podAnalyzer := PodAnalyzer{}
|
||||||
|
var analysisResults []Result
|
||||||
|
analysisResults, err := podAnalyzer.Analyze(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
assert.Equal(t, len(analysisResults), 1)
|
assert.Equal(t, len(analysisResults), 1)
|
||||||
}
|
}
|
@ -1,23 +1,19 @@
|
|||||||
package analyzer
|
package analyzer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
|
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
|
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PvcAnalyzer struct{}
|
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
|
// 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 {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var preAnalysis = map[string]PreAnalysis{}
|
var preAnalysis = map[string]PreAnalysis{}
|
||||||
@ -29,7 +25,7 @@ func (PvcAnalyzer) RunAnalysis(ctx context.Context, config *AnalysisConfiguratio
|
|||||||
if pvc.Status.Phase == "Pending" {
|
if pvc.Status.Phase == "Pending" {
|
||||||
|
|
||||||
// parse the event log and append details
|
// 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 {
|
if err != nil || evt == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -46,16 +42,16 @@ func (PvcAnalyzer) RunAnalysis(ctx context.Context, config *AnalysisConfiguratio
|
|||||||
}
|
}
|
||||||
|
|
||||||
for key, value := range preAnalysis {
|
for key, value := range preAnalysis {
|
||||||
var currentAnalysis = Analysis{
|
var currentAnalysis = Result{
|
||||||
Kind: "PersistentVolumeClaim",
|
Kind: "PersistentVolumeClaim",
|
||||||
Name: key,
|
Name: key,
|
||||||
Error: value.FailureDetails,
|
Error: value.FailureDetails,
|
||||||
}
|
}
|
||||||
|
|
||||||
parent, _ := util.GetParent(client, value.PersistentVolumeClaim.ObjectMeta)
|
parent, _ := util.GetParent(a.Client, value.PersistentVolumeClaim.ObjectMeta)
|
||||||
currentAnalysis.ParentObject = parent
|
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
|
package analyzer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
|
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
|
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ReplicaSetAnalyzer struct{}
|
type ReplicaSetAnalyzer struct{}
|
||||||
|
|
||||||
func (ReplicaSetAnalyzer) RunAnalysis(ctx context.Context, config *AnalysisConfiguration,
|
func (ReplicaSetAnalyzer) Analyze(a Analyzer) ([]Result, error) {
|
||||||
client *kubernetes.Client, aiClient ai.IAI, analysisResults *[]Analysis) error {
|
|
||||||
|
|
||||||
// search all namespaces for pods that are not running
|
// 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 {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var preAnalysis = map[string]PreAnalysis{}
|
var preAnalysis = map[string]PreAnalysis{}
|
||||||
@ -45,16 +40,15 @@ func (ReplicaSetAnalyzer) RunAnalysis(ctx context.Context, config *AnalysisConfi
|
|||||||
}
|
}
|
||||||
|
|
||||||
for key, value := range preAnalysis {
|
for key, value := range preAnalysis {
|
||||||
var currentAnalysis = Analysis{
|
var currentAnalysis = Result{
|
||||||
Kind: "ReplicaSet",
|
Kind: "ReplicaSet",
|
||||||
Name: key,
|
Name: key,
|
||||||
Error: value.FailureDetails,
|
Error: value.FailureDetails,
|
||||||
}
|
}
|
||||||
|
|
||||||
parent, _ := util.GetParent(client, value.ReplicaSet.ObjectMeta)
|
parent, _ := util.GetParent(a.Client, value.ReplicaSet.ObjectMeta)
|
||||||
currentAnalysis.ParentObject = parent
|
currentAnalysis.ParentObject = parent
|
||||||
*analysisResults = append(*analysisResults, currentAnalysis)
|
a.Results = append(a.Results, currentAnalysis)
|
||||||
}
|
}
|
||||||
|
return a.Results, nil
|
||||||
return nil
|
|
||||||
}
|
}
|
@ -1,25 +1,20 @@
|
|||||||
package analyzer
|
package analyzer
|
||||||
|
|
||||||
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/kubernetes"
|
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
|
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ServiceAnalyzer struct{}
|
type ServiceAnalyzer struct{}
|
||||||
|
|
||||||
func (ServiceAnalyzer) RunAnalysis(ctx context.Context, config *AnalysisConfiguration, client *kubernetes.Client, aiClient ai.IAI,
|
func (ServiceAnalyzer) Analyze(a Analyzer) ([]Result, error) {
|
||||||
analysisResults *[]Analysis) error {
|
|
||||||
|
|
||||||
// search all namespaces for pods that are not running
|
// 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 {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var preAnalysis = map[string]PreAnalysis{}
|
var preAnalysis = map[string]PreAnalysis{}
|
||||||
@ -29,7 +24,7 @@ func (ServiceAnalyzer) RunAnalysis(ctx context.Context, config *AnalysisConfigur
|
|||||||
|
|
||||||
// Check for empty service
|
// Check for empty service
|
||||||
if len(ep.Subsets) == 0 {
|
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 {
|
if err != nil {
|
||||||
color.Yellow("Service %s/%s does not exist", ep.Namespace, ep.Name)
|
color.Yellow("Service %s/%s does not exist", ep.Namespace, ep.Name)
|
||||||
continue
|
continue
|
||||||
@ -63,15 +58,15 @@ func (ServiceAnalyzer) RunAnalysis(ctx context.Context, config *AnalysisConfigur
|
|||||||
}
|
}
|
||||||
|
|
||||||
for key, value := range preAnalysis {
|
for key, value := range preAnalysis {
|
||||||
var currentAnalysis = Analysis{
|
var currentAnalysis = Result{
|
||||||
Kind: "Service",
|
Kind: "Service",
|
||||||
Name: key,
|
Name: key,
|
||||||
Error: value.FailureDetails,
|
Error: value.FailureDetails,
|
||||||
}
|
}
|
||||||
|
|
||||||
parent, _ := util.GetParent(client, value.Endpoint.ObjectMeta)
|
parent, _ := util.GetParent(a.Client, value.Endpoint.ObjectMeta)
|
||||||
currentAnalysis.ParentObject = parent
|
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"
|
"k8s.io/client-go/kubernetes/fake"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestServiceAnalzyer(t *testing.T) {
|
func TestServiceAnalyzer(t *testing.T) {
|
||||||
|
|
||||||
clientset := fake.NewSimpleClientset(&v1.Endpoints{
|
clientset := fake.NewSimpleClientset(&v1.Endpoints{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
@ -32,17 +32,18 @@ func TestServiceAnalzyer(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}})
|
}})
|
||||||
|
|
||||||
serviceAnalyzer := ServiceAnalyzer{}
|
config := Analyzer{
|
||||||
var analysisResults []Analysis
|
Client: &kubernetes.Client{
|
||||||
err := serviceAnalyzer.RunAnalysis(context.Background(),
|
|
||||||
&AnalysisConfiguration{
|
|
||||||
Namespace: "default",
|
|
||||||
},
|
|
||||||
&kubernetes.Client{
|
|
||||||
Client: clientset,
|
Client: clientset,
|
||||||
}, nil, &analysisResults)
|
},
|
||||||
|
Context: context.Background(),
|
||||||
|
Namespace: "default",
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceAnalyzer := ServiceAnalyzer{}
|
||||||
|
analysisResults, err := serviceAnalyzer.Analyze(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
assert.Equal(t, len(analysisResults), 1)
|
assert.Equal(t, len(analysisResults), 1)
|
||||||
}
|
}
|
@ -1,17 +1,23 @@
|
|||||||
package analyzer
|
package analyzer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
|
||||||
|
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
||||||
appsv1 "k8s.io/api/apps/v1"
|
appsv1 "k8s.io/api/apps/v1"
|
||||||
autoscalingv1 "k8s.io/api/autoscaling/v1"
|
autov1 "k8s.io/api/autoscaling/v1"
|
||||||
v1 "k8s.io/api/core/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"
|
policyv1 "k8s.io/api/policy/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AnalysisConfiguration struct {
|
type Analyzer struct {
|
||||||
Namespace string
|
Client *kubernetes.Client
|
||||||
NoCache bool
|
Context context.Context
|
||||||
Explain bool
|
Namespace string
|
||||||
|
AIClient ai.IAI
|
||||||
|
PreAnalysis map[string]PreAnalysis
|
||||||
|
Results []Result
|
||||||
}
|
}
|
||||||
|
|
||||||
type PreAnalysis struct {
|
type PreAnalysis struct {
|
||||||
@ -20,12 +26,12 @@ type PreAnalysis struct {
|
|||||||
ReplicaSet appsv1.ReplicaSet
|
ReplicaSet appsv1.ReplicaSet
|
||||||
PersistentVolumeClaim v1.PersistentVolumeClaim
|
PersistentVolumeClaim v1.PersistentVolumeClaim
|
||||||
Endpoint v1.Endpoints
|
Endpoint v1.Endpoints
|
||||||
Ingress networkingv1.Ingress
|
Ingress networkv1.Ingress
|
||||||
HorizontalPodAutoscalers autoscalingv1.HorizontalPodAutoscaler
|
HorizontalPodAutoscalers autov1.HorizontalPodAutoscaler
|
||||||
PodDisruptionBudget policyv1.PodDisruptionBudget
|
PodDisruptionBudget policyv1.PodDisruptionBudget
|
||||||
}
|
}
|
||||||
|
|
||||||
type Analysis struct {
|
type Result struct {
|
||||||
Kind string `json:"kind"`
|
Kind string `json:"kind"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Error []string `json:"error"`
|
Error []string `json:"error"`
|
Loading…
Reference in New Issue
Block a user