diff --git a/pkg/analyzer/analyzer.go b/pkg/analyzer/analyzer.go index 0db2b96c0a..a773bac3aa 100644 --- a/pkg/analyzer/analyzer.go +++ b/pkg/analyzer/analyzer.go @@ -10,6 +10,7 @@ var coreAnalyzerMap = map[string]IAnalyzer{ "PersistentVolumeClaim": PvcAnalyzer{}, "Service": ServiceAnalyzer{}, "Ingress": IngressAnalyzer{}, + "StatefulSet": StatefulSetAnalyzer{}, } var additionalAnalyzerMap = map[string]IAnalyzer{ diff --git a/pkg/analyzer/statefulset.go b/pkg/analyzer/statefulset.go new file mode 100644 index 0000000000..1f3910a94e --- /dev/null +++ b/pkg/analyzer/statefulset.go @@ -0,0 +1,49 @@ +package analyzer + +import ( + "fmt" + + "github.com/k8sgpt-ai/k8sgpt/pkg/util" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type StatefulSetAnalyzer struct{} + +func (StatefulSetAnalyzer) Analyze(a Analyzer) ([]Result, error) { + list, err := a.Client.GetClient().AppsV1().StatefulSets(a.Namespace).List(a.Context, metav1.ListOptions{}) + if err != nil { + return nil, err + } + var preAnalysis = map[string]PreAnalysis{} + + for _, sts := range list.Items { + var failures []string + + // get serviceName + serviceName := sts.Spec.ServiceName + _, err := a.Client.GetClient().CoreV1().Services(sts.Namespace).Get(a.Context, serviceName, metav1.GetOptions{}) + if err != nil { + failures = append(failures, fmt.Sprintf("StatefulSet uses the service %s/%s which does not exist.", sts.Namespace, serviceName)) + } + if len(failures) > 0 { + preAnalysis[fmt.Sprintf("%s/%s", sts.Namespace, sts.Name)] = PreAnalysis{ + StatefulSet: sts, + FailureDetails: failures, + } + } + } + + for key, value := range preAnalysis { + var currentAnalysis = Result{ + Kind: "StatefulSet", + Name: key, + Error: value.FailureDetails, + } + + parent, _ := util.GetParent(a.Client, value.StatefulSet.ObjectMeta) + currentAnalysis.ParentObject = parent + a.Results = append(a.Results, currentAnalysis) + } + + return a.Results, nil +} diff --git a/pkg/analyzer/statefulset_test.go b/pkg/analyzer/statefulset_test.go new file mode 100644 index 0000000000..d3b3e6afa3 --- /dev/null +++ b/pkg/analyzer/statefulset_test.go @@ -0,0 +1,78 @@ +package analyzer + +import ( + "context" + "testing" + + "github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes" + "github.com/magiconair/properties/assert" + appsv1 "k8s.io/api/apps/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes/fake" +) + +func TestStatefulSetAnalyzer(t *testing.T) { + clientset := fake.NewSimpleClientset( + &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example", + Namespace: "default", + }, + }) + statefulSetAnalyzer := StatefulSetAnalyzer{} + + config := Analyzer{ + Client: &kubernetes.Client{ + Client: clientset, + }, + Context: context.Background(), + Namespace: "default", + } + analysisResults, err := statefulSetAnalyzer.Analyze(config) + if err != nil { + t.Error(err) + } + assert.Equal(t, len(analysisResults), 1) +} + +func TestStatefulSetAnalyzerWithoutService(t *testing.T) { + clientset := fake.NewSimpleClientset( + &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example", + Namespace: "default", + }, + Spec: appsv1.StatefulSetSpec{ + ServiceName: "example-svc", + }, + }) + statefulSetAnalyzer := StatefulSetAnalyzer{} + + config := Analyzer{ + Client: &kubernetes.Client{ + Client: clientset, + }, + Context: context.Background(), + Namespace: "default", + } + analysisResults, err := statefulSetAnalyzer.Analyze(config) + if err != nil { + t.Error(err) + } + var errorFound bool + want := "StatefulSet uses the service default/example-svc which does not exist." + + for _, analysis := range analysisResults { + for _, got := range analysis.Error { + if want == got { + errorFound = true + } + } + if errorFound { + break + } + } + if !errorFound { + t.Errorf("Error expected: '%v', not found in StatefulSet's analysis results", want) + } +} diff --git a/pkg/analyzer/types.go b/pkg/analyzer/types.go index 42e5e0ab81..efe4bd8463 100644 --- a/pkg/analyzer/types.go +++ b/pkg/analyzer/types.go @@ -29,6 +29,7 @@ type PreAnalysis struct { Ingress networkv1.Ingress HorizontalPodAutoscalers autov1.HorizontalPodAutoscaler PodDisruptionBudget policyv1.PodDisruptionBudget + StatefulSet appsv1.StatefulSet } type Result struct {