refactor: first try with more interfaces

Signed-off-by: Thomas Schuetz <thomas.schuetz@t-sc.eu>
This commit is contained in:
Thomas Schuetz
2023-04-02 13:55:06 +02:00
parent 071ee560f3
commit d246fc1ec2
13 changed files with 331 additions and 297 deletions

View File

@@ -2,18 +2,14 @@ package analyze
import (
"context"
"encoding/json"
"fmt"
"os"
"strings"
"github.com/fatih/color"
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer"
"github.com/k8sgpt-ai/k8sgpt/pkg/analysis"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
"github.com/schollz/progressbar/v3"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"os"
)
var (
@@ -69,75 +65,26 @@ var AnalyzeCmd = &cobra.Command{
ctx := context.Background()
// Get kubernetes client from viper
client := viper.Get("kubernetesClient").(*kubernetes.Client)
// Analysis configuration
config := &analyzer.AnalysisConfiguration{
// AnalysisResult configuration
analysis := &analysis.Analysis{
Context: ctx,
Namespace: namespace,
NoCache: nocache,
Explain: explain,
AIClient: aiClient,
Filters: filters,
Client: client,
}
var analysisResults *[]analyzer.Analysis = &[]analyzer.Analysis{}
if err := analyzer.RunAnalysis(ctx, filters, config, client,
aiClient, analysisResults); err != nil {
color.Red("Error: %v", err)
os.Exit(1)
}
// Run analysis
_ = analysis.RunAnalysis()
if len(*analysisResults) == 0 {
color.Green("{ \"status\": \"OK\" }")
os.Exit(0)
}
var bar = progressbar.Default(int64(len(*analysisResults)))
if !explain {
bar.Clear()
}
var printOutput []analyzer.Analysis
for _, analysis := range *analysisResults {
if explain {
parsedText, err := analyzer.ParseViaAI(ctx, config, aiClient, analysis.Error)
if err != nil {
// Check for exhaustion
if strings.Contains(err.Error(), "status code: 429") {
color.Red("Exhausted API quota. Please try again later")
os.Exit(1)
}
color.Red("Error: %v", err)
continue
}
analysis.Details = parsedText
bar.Add(1)
}
printOutput = append(printOutput, analysis)
}
// print results
for n, analysis := range printOutput {
switch output {
case "json":
analysis.Error = analysis.Error[0:]
j, err := json.Marshal(analysis)
if err != nil {
color.Red("Error: %v", err)
os.Exit(1)
}
fmt.Println(string(j))
default:
fmt.Printf("%s %s(%s)\n", color.CyanString("%d", n),
color.YellowString(analysis.Name), color.CyanString(analysis.ParentObject))
for _, err := range analysis.Error {
fmt.Printf("- %s %s\n", color.RedString("Error:"), color.RedString(err))
}
fmt.Println(color.GreenString(analysis.Details + "\n"))
}
}
analysis.PrintAnalysisResult()
},
}
func init() {
// namespace flag
AnalyzeCmd.Flags().StringVarP(&namespace, "namespace", "n", "", "Namespace to analyze")
// no cache flag

4
go.mod
View File

@@ -5,7 +5,6 @@ go 1.20
require (
github.com/fatih/color v1.15.0
github.com/sashabaranov/go-openai v1.5.7
github.com/schollz/progressbar/v3 v3.13.1
github.com/spf13/cobra v1.6.1
github.com/spf13/viper v1.15.0
golang.org/x/term v0.6.0
@@ -37,14 +36,11 @@ require (
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.18 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pelletier/go-toml/v2 v2.0.7 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/spf13/afero v1.9.5 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect

11
go.sum
View File

@@ -173,7 +173,6 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
@@ -192,13 +191,8 @@ github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJ
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -217,17 +211,12 @@ github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qR
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sashabaranov/go-openai v1.5.7 h1:8DGgRG+P7yWixte5j720y6yiXgY3Hlgcd0gcpHdltfo=
github.com/sashabaranov/go-openai v1.5.7/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg=
github.com/schollz/progressbar/v3 v3.13.1 h1:o8rySDYiQ59Mwzy2FELeHY5ZARXZTVJC7iHD6PEFUiE=
github.com/schollz/progressbar/v3 v3.13.1/go.mod h1:xvrbki8kfT1fzWzBT/UZd9L6GA+jdL7HAgq2RFnO6fQ=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM=
github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=

65
pkg/analysis/analysis.go Normal file
View File

@@ -0,0 +1,65 @@
package analysis
import (
"context"
"fmt"
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer"
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
"github.com/spf13/viper"
)
type Analysis struct {
Context context.Context
Namespace string
NoCache bool
Explain bool
AIClient ai.IAI
Filters []string
Client *kubernetes.Client
analysisResults []common.Result
}
func (a *Analysis) RunAnalysis() error {
activeFilters := viper.GetStringSlice("active_filters")
analyzerList := analyzer.GetAnalyzerList()
// if there are no filters selected and no active_filters then run all of them
if len(a.Filters) == 0 && len(activeFilters) == 0 {
for _, al := range analyzerList {
thisanalysis, _ := analyzer.NewAnalyzer(al, a.Client, a.Context, a.Namespace)
err := thisanalysis.Analyze()
if err != nil {
fmt.Println("Error running analysis: ", err)
}
a.analysisResults = append(a.analysisResults, thisanalysis.GetResult()...)
}
return nil
}
// if the filters flag is specified
if len(a.Filters) != 0 {
for _, filter := range a.Filters {
for _, ali := range analyzerList {
if filter == ali {
thisanalysis, _ := analyzer.NewAnalyzer(ali, a.Client, a.Context, a.Namespace)
err := thisanalysis.Analyze()
if err != nil {
fmt.Println("Error running analysis: ", err)
}
a.analysisResults = append(a.analysisResults, thisanalysis.GetResult()...)
}
}
}
return nil
}
return nil
}
func (a *Analysis) PrintAnalysisResult() {
for _, result := range a.analysisResults {
fmt.Println(result)
}
}

View File

@@ -2,71 +2,88 @@ package analyzer
import (
"context"
"encoding/base64"
"strings"
"github.com/fatih/color"
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
"fmt"
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer/hpa"
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer/ingress"
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer/pod"
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer/pvc"
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer/rs"
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer/service"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
"github.com/spf13/viper"
)
var coreAnalyzerMap = map[string]func(ctx context.Context, config *AnalysisConfiguration,
client *kubernetes.Client, aiClient ai.IAI, analysisResults *[]Analysis) error{
"Pod": AnalyzePod,
"ReplicaSet": AnalyzeReplicaSet,
"PersistentVolumeClaim": AnalyzePersistentVolumeClaim,
"Service": AnalyzeEndpoints,
"Ingress": AnalyzeIngress,
type IAnalyzer interface {
Analyze() error
GetResult() []common.Result
}
var additionalAnalyzerMap = map[string]func(ctx context.Context, config *AnalysisConfiguration,
client *kubernetes.Client, aiClient ai.IAI, analysisResults *[]Analysis) error{
"HorizontalPodAutoScaler": AnalyzeHpa,
const (
PodAnalyzerName = "Pod"
ReplicaSetAnalyzerName = "ReplicaSet"
PersistentVolumeClaimAnalyzerName = "PersistentVolumeClaim"
ServiceAnalyzerName = "Service"
IngressAnalyzerName = "Ingress"
HPAAnalyzerName = "HorizontalPodAutoScaler"
)
var (
coreAnalyzerList = []string{
PodAnalyzerName,
ReplicaSetAnalyzerName,
PersistentVolumeClaimAnalyzerName,
ServiceAnalyzerName,
IngressAnalyzerName,
HPAAnalyzerName,
}
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(ctx, config, client, aiClient, analysisResults); err != nil {
return err
additionalAnalyzers = []string{
HPAAnalyzerName,
}
}
return nil
)
func NewAnalyzer(analyzer string, client *kubernetes.Client, context context.Context, namespace string) (IAnalyzer, error) {
analyzerConfig := common.Analyzer{
Namespace: namespace,
Context: context,
Client: client,
}
// if the filters flag is specified
if len(filters) != 0 {
for _, filter := range filters {
if analyzer, ok := analyzerMap[filter]; ok {
if err := analyzer(ctx, config, client, aiClient, analysisResults); err != nil {
return err
analyzerConfig.PreAnalysis = make(map[string]common.PreAnalysis)
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)
}
}
}
return nil
}
// use active_filters
for _, filter := range activeFilters {
if analyzer, ok := analyzerMap[filter]; ok {
if err := analyzer(ctx, config, client, aiClient, analysisResults); err != nil {
return err
}
}
}
return nil
}
/*
func ParseViaAI(ctx context.Context, config *analysis.Analysis,
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, " ")
@@ -103,35 +120,39 @@ func ParseViaAI(ctx context.Context, config *AnalysisConfiguration,
}
return response, nil
}
*/
func ListFilters() ([]string, []string) {
coreKeys := make([]string, 0, len(coreAnalyzerMap))
for k := range coreAnalyzerMap {
coreKeys = append(coreKeys, k)
coreKeys := []string{}
for _, filter := range coreAnalyzerList {
coreKeys = append(coreKeys, filter)
}
additionalKeys := make([]string, 0, len(additionalAnalyzerMap))
for k := range additionalAnalyzerMap {
additionalKeys = append(additionalKeys, k)
additionalKeys := []string{}
for _, filter := range additionalAnalyzers {
coreKeys = append(additionalKeys, filter)
}
return coreKeys, additionalKeys
}
func getAnalyzerMap() map[string]func(ctx context.Context, config *AnalysisConfiguration,
client *kubernetes.Client, aiClient ai.IAI, analysisResults *[]Analysis) error {
func GetAnalyzerList() []string {
list := []string{}
mergedMap := make(map[string]func(ctx context.Context, config *AnalysisConfiguration,
client *kubernetes.Client, aiClient ai.IAI, analysisResults *[]Analysis) error)
list = append(list, coreAnalyzerList...)
list = append(list, additionalAnalyzers...)
// add core analyzer
for key, value := range coreAnalyzerMap {
mergedMap[key] = value
list = removeDuplicateStr(list)
return list
}
// add additional analyzer
for key, value := range additionalAnalyzerMap {
mergedMap[key] = value
func removeDuplicateStr(strSlice []string) []string {
allKeys := make(map[string]bool)
list := []string{}
for _, item := range strSlice {
if _, value := allKeys[item]; !value {
allKeys[item] = true
list = append(list, item)
}
return mergedMap
}
return list
}

View File

@@ -1,4 +1,4 @@
package analyzer
package common
import (
"context"

View File

@@ -1,16 +1,28 @@
package analyzer
package common
import (
"context"
"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"
)
type AnalysisConfiguration struct {
type Analyzer struct {
Client *kubernetes.Client
Context context.Context
Namespace string
NoCache bool
Explain bool
PreAnalysis map[string]PreAnalysis
Result []Result
}
type Result struct {
Kind string `json:"kind"`
Name string `json:"name"`
Error []string `json:"error"`
Details string `json:"details"`
ParentObject string `json:"parentObject"`
}
type PreAnalysis struct {
@@ -19,14 +31,6 @@ type PreAnalysis struct {
ReplicaSet appsv1.ReplicaSet
PersistentVolumeClaim v1.PersistentVolumeClaim
Endpoint v1.Endpoints
Ingress networkingv1.Ingress
HorizontalPodAutoscalers autoscalingv1.HorizontalPodAutoscaler
}
type Analysis struct {
Kind string `json:"kind"`
Name string `json:"name"`
Error []string `json:"error"`
Details string `json:"details"`
ParentObject string `json:"parentObject"`
Ingress networkv1.Ingress
HorizontalPodAutoscalers autov1.HorizontalPodAutoscaler
}

View File

@@ -1,25 +1,22 @@
package analyzer
package hpa
import (
"context"
"fmt"
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func AnalyzeHpa(ctx context.Context, config *AnalysisConfiguration, client *kubernetes.Client, aiClient ai.IAI,
analysisResults *[]Analysis) error {
type HPAAnalyzer struct {
common.Analyzer
}
list, err := client.GetClient().AutoscalingV1().HorizontalPodAutoscalers(config.Namespace).List(ctx, metav1.ListOptions{})
func (a *HPAAnalyzer) Analyze() error {
list, err := a.Client.GetClient().AutoscalingV1().HorizontalPodAutoscalers(a.Namespace).List(a.Context, metav1.ListOptions{})
if err != nil {
return err
}
var preAnalysis = map[string]PreAnalysis{}
for _, hpa := range list.Items {
var failures []string
@@ -29,22 +26,22 @@ func AnalyzeHpa(ctx context.Context, config *AnalysisConfiguration, client *kube
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
}
@@ -57,7 +54,7 @@ func AnalyzeHpa(ctx context.Context, config *AnalysisConfiguration, client *kube
}
if len(failures) > 0 {
preAnalysis[fmt.Sprintf("%s/%s", hpa.Namespace, hpa.Name)] = PreAnalysis{
a.PreAnalysis[fmt.Sprintf("%s/%s", hpa.Namespace, hpa.Name)] = common.PreAnalysis{
HorizontalPodAutoscalers: hpa,
FailureDetails: failures,
}
@@ -65,17 +62,20 @@ func AnalyzeHpa(ctx context.Context, config *AnalysisConfiguration, client *kube
}
for key, value := range preAnalysis {
var currentAnalysis = Analysis{
for key, value := range a.PreAnalysis {
var currentAnalysis = common.Result{
Kind: "HorizontalPodAutoscaler",
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.Result = append(a.Result, currentAnalysis)
}
return nil
}
func (a *HPAAnalyzer) GetResult() []common.Result {
return a.Result
}

View File

@@ -1,25 +1,23 @@
package analyzer
package ingress
import (
"context"
"fmt"
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer/common"
"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"
)
func AnalyzeIngress(ctx context.Context, config *AnalysisConfiguration, client *kubernetes.Client, aiClient ai.IAI,
analysisResults *[]Analysis) error {
type IngressAnalyzer struct {
common.Analyzer
}
list, err := client.GetClient().NetworkingV1().Ingresses(config.Namespace).List(ctx, metav1.ListOptions{})
func (a *IngressAnalyzer) Analyze() error {
list, err := a.Client.GetClient().NetworkingV1().Ingresses(a.Namespace).List(a.Context, metav1.ListOptions{})
if err != nil {
return err
}
var preAnalysis = map[string]PreAnalysis{}
for _, ing := range list.Items {
var failures []string
@@ -36,7 +34,7 @@ func AnalyzeIngress(ctx context.Context, config *AnalysisConfiguration, client *
// 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))
}
@@ -46,7 +44,7 @@ func AnalyzeIngress(ctx context.Context, config *AnalysisConfiguration, client *
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))
}
@@ -54,13 +52,13 @@ func AnalyzeIngress(ctx context.Context, config *AnalysisConfiguration, client *
}
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))
}
}
if len(failures) > 0 {
preAnalysis[fmt.Sprintf("%s/%s", ing.Namespace, ing.Name)] = PreAnalysis{
a.PreAnalysis[fmt.Sprintf("%s/%s", ing.Namespace, ing.Name)] = common.PreAnalysis{
Ingress: ing,
FailureDetails: failures,
}
@@ -68,17 +66,20 @@ func AnalyzeIngress(ctx context.Context, config *AnalysisConfiguration, client *
}
for key, value := range preAnalysis {
var currentAnalysis = Analysis{
for key, value := range a.PreAnalysis {
var currentAnalysis = common.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.Result = append(a.Result, currentAnalysis)
}
return nil
}
func (a *IngressAnalyzer) GetResult() []common.Result {
return a.Result
}

View File

@@ -1,25 +1,22 @@
package analyzer
package pod
import (
"context"
"fmt"
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func AnalyzePod(ctx context.Context, config *AnalysisConfiguration,
client *kubernetes.Client, aiClient ai.IAI, analysisResults *[]Analysis) error {
type PodAnalyzer struct {
common.Analyzer ", inline"
}
func (a *PodAnalyzer) Analyze() 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
}
var preAnalysis = map[string]PreAnalysis{}
for _, pod := range list.Items {
var failures []string
// Check for pending pods
@@ -47,7 +44,7 @@ func AnalyzePod(ctx context.Context, config *AnalysisConfiguration,
if containerStatus.State.Waiting.Reason == "ContainerCreating" && pod.Status.Phase == "Pending" {
// parse the event log and append details
evt, err := FetchLatestPodEvent(ctx, client, &pod)
evt, err := common.FetchLatestPodEvent(a.Context, a.Client, &pod)
if err != nil || evt == nil {
continue
}
@@ -58,24 +55,27 @@ func AnalyzePod(ctx context.Context, config *AnalysisConfiguration,
}
}
if len(failures) > 0 {
preAnalysis[fmt.Sprintf("%s/%s", pod.Namespace, pod.Name)] = PreAnalysis{
a.PreAnalysis[fmt.Sprintf("%s/%s", pod.Namespace, pod.Name)] = common.PreAnalysis{
Pod: pod,
FailureDetails: failures,
}
}
}
for key, value := range preAnalysis {
var currentAnalysis = Analysis{
for key, value := range a.PreAnalysis {
var currentAnalysis = common.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.Result = append(a.Result, currentAnalysis)
}
return nil
}
func (a *PodAnalyzer) GetResult() []common.Result {
return a.Result
}

View File

@@ -1,24 +1,25 @@
package analyzer
package pvc
import (
"context"
"fmt"
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer/common"
"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"
)
func AnalyzePersistentVolumeClaim(ctx context.Context, config *AnalysisConfiguration, client *kubernetes.Client, aiClient ai.IAI, analysisResults *[]Analysis) error {
type PvcAnalyzer struct {
common.Analyzer
}
func (a *PvcAnalyzer) Analyze() 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
}
var preAnalysis = map[string]PreAnalysis{}
var preAnalysis = map[string]common.PreAnalysis{}
for _, pvc := range list.Items {
var failures []string
@@ -27,7 +28,7 @@ func AnalyzePersistentVolumeClaim(ctx context.Context, config *AnalysisConfigura
if pvc.Status.Phase == "Pending" {
// parse the event log and append details
evt, err := FetchLatestPvcEvent(ctx, client, &pvc)
evt, err := common.FetchLatestPvcEvent(a.Context, a.Client, &pvc)
if err != nil || evt == nil {
continue
}
@@ -36,7 +37,7 @@ func AnalyzePersistentVolumeClaim(ctx context.Context, config *AnalysisConfigura
}
}
if len(failures) > 0 {
preAnalysis[fmt.Sprintf("%s/%s", pvc.Namespace, pvc.Name)] = PreAnalysis{
preAnalysis[fmt.Sprintf("%s/%s", pvc.Namespace, pvc.Name)] = common.PreAnalysis{
PersistentVolumeClaim: pvc,
FailureDetails: failures,
}
@@ -44,16 +45,19 @@ func AnalyzePersistentVolumeClaim(ctx context.Context, config *AnalysisConfigura
}
for key, value := range preAnalysis {
var currentAnalysis = Analysis{
var currentAnalysis = common.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.Result = append(a.Result, currentAnalysis)
}
return nil
}
func (a *PvcAnalyzer) GetResult() []common.Result {
return a.Result
}

View File

@@ -1,25 +1,25 @@
package analyzer
package rs
import (
"context"
"fmt"
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer/common"
"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"
)
func AnalyzeReplicaSet(ctx context.Context, config *AnalysisConfiguration,
client *kubernetes.Client, aiClient ai.IAI, analysisResults *[]Analysis) error {
type ReplicaSetAnalyzer struct {
common.Analyzer
}
func (a *ReplicaSetAnalyzer) Analyze() 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
}
var preAnalysis = map[string]PreAnalysis{}
var preAnalysis = map[string]common.PreAnalysis{}
for _, rs := range list.Items {
var failures []string
@@ -35,7 +35,7 @@ func AnalyzeReplicaSet(ctx context.Context, config *AnalysisConfiguration,
}
}
if len(failures) > 0 {
preAnalysis[fmt.Sprintf("%s/%s", rs.Namespace, rs.Name)] = PreAnalysis{
preAnalysis[fmt.Sprintf("%s/%s", rs.Namespace, rs.Name)] = common.PreAnalysis{
ReplicaSet: rs,
FailureDetails: failures,
}
@@ -43,16 +43,19 @@ func AnalyzeReplicaSet(ctx context.Context, config *AnalysisConfiguration,
}
for key, value := range preAnalysis {
var currentAnalysis = Analysis{
var currentAnalysis = common.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.Result = append(a.Result, currentAnalysis)
}
return nil
}
func (a *ReplicaSetAnalyzer) GetResult() []common.Result {
return a.Result
}

View File

@@ -1,33 +1,33 @@
package analyzer
package service
import (
"context"
"fmt"
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer/common"
"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"
)
func AnalyzeEndpoints(ctx context.Context, config *AnalysisConfiguration, client *kubernetes.Client, aiClient ai.IAI,
analysisResults *[]Analysis) error {
type ServiceAnalyzer struct {
common.Analyzer
}
func (a *ServiceAnalyzer) Analyze() 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
}
var preAnalysis = map[string]PreAnalysis{}
var preAnalysis = map[string]common.PreAnalysis{}
for _, ep := range list.Items {
var failures []string
// 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
@@ -53,7 +53,7 @@ func AnalyzeEndpoints(ctx context.Context, config *AnalysisConfiguration, client
}
if len(failures) > 0 {
preAnalysis[fmt.Sprintf("%s/%s", ep.Namespace, ep.Name)] = PreAnalysis{
preAnalysis[fmt.Sprintf("%s/%s", ep.Namespace, ep.Name)] = common.PreAnalysis{
Endpoint: ep,
FailureDetails: failures,
}
@@ -61,15 +61,19 @@ func AnalyzeEndpoints(ctx context.Context, config *AnalysisConfiguration, client
}
for key, value := range preAnalysis {
var currentAnalysis = Analysis{
var currentAnalysis = common.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.Result = append(a.Result, currentAnalysis)
}
return nil
}
func (a *ServiceAnalyzer) GetResult() []common.Result {
return a.Result
}