diff --git a/pkg/analyzer/pvc_test.go b/pkg/analyzer/pvc_test.go index e8e05ead9d..86ae48f738 100644 --- a/pkg/analyzer/pvc_test.go +++ b/pkg/analyzer/pvc_test.go @@ -15,6 +15,7 @@ package analyzer import ( "context" + "sort" "testing" "github.com/k8sgpt-ai/k8sgpt/pkg/common" @@ -205,6 +206,12 @@ func TestPersistentVolumeClaimAnalyzer(t *testing.T) { if tt.expectations == nil { require.Equal(t, 0, len(results)) } else { + sort.Slice(results, func(i, j int) bool { + return results[i].Name < results[j].Name + }) + + require.Equal(t, len(tt.expectations), len(results)) + for i, expectation := range tt.expectations { require.Equal(t, expectation, results[i].Name) for _, failure := range results[i].Error { diff --git a/pkg/analyzer/rs_test.go b/pkg/analyzer/rs_test.go index 42d6d3df84..467c19d295 100644 --- a/pkg/analyzer/rs_test.go +++ b/pkg/analyzer/rs_test.go @@ -142,6 +142,8 @@ func TestReplicaSetAnalyzer(t *testing.T) { }, } + require.Equal(t, len(expectations), len(results)) + for i, expectation := range expectations { require.Equal(t, expectation.name, results[i].Name) for j, failure := range results[i].Error { diff --git a/pkg/analyzer/service.go b/pkg/analyzer/service.go index 9f67234297..005cf44a5b 100644 --- a/pkg/analyzer/service.go +++ b/pkg/analyzer/service.go @@ -98,15 +98,17 @@ func (ServiceAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) { count++ pods = append(pods, addresses.TargetRef.Kind+"/"+addresses.TargetRef.Name) } + } + } - doc := apiDoc.GetApiDocV2("subsets.notReadyAddresses") + if count > 0 { + doc := apiDoc.GetApiDocV2("subsets.notReadyAddresses") - failures = append(failures, common.Failure{ - Text: fmt.Sprintf("Service has not ready endpoints, pods: %s, expected %d", pods, count), - KubernetesDoc: doc, - Sensitive: []common.Sensitive{}, - }) - } + failures = append(failures, common.Failure{ + Text: fmt.Sprintf("Service has not ready endpoints, pods: %s, expected %d", pods, count), + KubernetesDoc: doc, + Sensitive: []common.Sensitive{}, + }) } } diff --git a/pkg/analyzer/service_test.go b/pkg/analyzer/service_test.go index e237ab05c0..4f1c148ab2 100644 --- a/pkg/analyzer/service_test.go +++ b/pkg/analyzer/service_test.go @@ -15,108 +15,160 @@ package analyzer import ( "context" + "sort" "testing" "github.com/k8sgpt-ai/k8sgpt/pkg/common" "github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes" - "github.com/magiconair/properties/assert" + "github.com/stretchr/testify/require" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes/fake" + "k8s.io/client-go/tools/leaderelection/resourcelock" ) func TestServiceAnalyzer(t *testing.T) { - - clientset := fake.NewSimpleClientset(&v1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - Name: "example", - Namespace: "default", - Annotations: map[string]string{}, - }, - }, - &v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "example", - Namespace: "default", - Annotations: map[string]string{}, - }, - Spec: v1.ServiceSpec{ - Selector: map[string]string{ - "app": "example", - }, - }}) - config := common.Analyzer{ Client: &kubernetes.Client{ - Client: clientset, + Client: fake.NewSimpleClientset( + &v1.Endpoints{ + ObjectMeta: metav1.ObjectMeta{ + Name: "Endpoint1", + Namespace: "test", + }, + // Endpoint with non-zero subsets. + Subsets: []v1.EndpointSubset{ + { + // These not ready end points will contribute to failures. + NotReadyAddresses: []v1.EndpointAddress{ + { + TargetRef: &v1.ObjectReference{ + Kind: "test-reference", + Name: "reference1", + }, + }, + { + TargetRef: &v1.ObjectReference{ + Kind: "test-reference", + Name: "reference2", + }, + }, + }, + }, + { + // These not ready end points will contribute to failures. + NotReadyAddresses: []v1.EndpointAddress{ + { + TargetRef: &v1.ObjectReference{ + Kind: "test-reference", + Name: "reference3", + }, + }, + }, + }, + }, + }, + &v1.Endpoints{ + ObjectMeta: metav1.ObjectMeta{ + Name: "Endpoint2", + Namespace: "test", + Annotations: map[string]string{ + // Leader election record annotation key defined. + resourcelock.LeaderElectionRecordAnnotationKey: "this is okay", + }, + }, + // Endpoint with zero subsets. + }, + &v1.Endpoints{ + ObjectMeta: metav1.ObjectMeta{ + // This won't contribute to any failures. + Name: "non-existent-service", + Namespace: "test", + Annotations: map[string]string{}, + }, + // Endpoint with zero subsets. + }, + &v1.Endpoints{ + ObjectMeta: metav1.ObjectMeta{ + Name: "Service1", + Namespace: "test", + Annotations: map[string]string{}, + }, + // Endpoint with zero subsets. + }, + &v1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "Service1", + Namespace: "test", + }, + Spec: v1.ServiceSpec{ + Selector: map[string]string{ + "app1": "test-app1", + "app2": "test-app2", + }, + }, + }, + &v1.Service{ + ObjectMeta: metav1.ObjectMeta{ + // This service won't be discovered. + Name: "Service2", + Namespace: "default", + }, + Spec: v1.ServiceSpec{ + Selector: map[string]string{ + "app1": "test-app1", + "app2": "test-app2", + }, + }, + }, + &v1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "Service3", + Namespace: "test", + }, + Spec: v1.ServiceSpec{ + // No Spec Selector + }, + }, + ), }, Context: context.Background(), - Namespace: "default", + Namespace: "test", } - serviceAnalyzer := ServiceAnalyzer{} - analysisResults, err := serviceAnalyzer.Analyze(config) - if err != nil { - t.Error(err) - } - assert.Equal(t, len(analysisResults), 1) -} + sAnalyzer := ServiceAnalyzer{} + results, err := sAnalyzer.Analyze(config) + require.NoError(t, err) -func TestServiceAnalyzerNamespaceFiltering(t *testing.T) { + sort.Slice(results, func(i, j int) bool { + return results[i].Name < results[j].Name + }) - clientset := fake.NewSimpleClientset( - &v1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - Name: "example", - Namespace: "default", - Annotations: map[string]string{}, + expectations := []struct { + name string + failuresText []string + }{ + { + name: "test/Endpoint1", + failuresText: []string{ + "Service has not ready endpoints, pods", }, }, - &v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "example", - Namespace: "default", - Annotations: map[string]string{}, - }, - Spec: v1.ServiceSpec{ - Selector: map[string]string{ - "app": "example", - }, - }, - }, - &v1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - Name: "example", - Namespace: "other-namespace", - Annotations: map[string]string{}, - }, - }, - &v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "example", - Namespace: "other-namespace", - Annotations: map[string]string{}, - }, - Spec: v1.ServiceSpec{ - Selector: map[string]string{ - "app": "example", - }, + { + name: "test/Service1", + failuresText: []string{ + "Service has no endpoints, expected label", + "Service has no endpoints, expected label", }, }, - ) - - config := common.Analyzer{ - Client: &kubernetes.Client{ - Client: clientset, - }, - Context: context.Background(), - Namespace: "default", } - serviceAnalyzer := ServiceAnalyzer{} - analysisResults, err := serviceAnalyzer.Analyze(config) - if err != nil { - t.Error(err) + require.Equal(t, len(expectations), len(results)) + + for i, result := range results { + require.Equal(t, expectations[i].name, result.Name) + for j, failure := range result.Error { + require.Contains(t, failure.Text, expectations[i].failuresText[j]) + } } - assert.Equal(t, len(analysisResults), 1) }