From d1a8931d1aa122f4ab55ed4d4dd28a180b1f96aa Mon Sep 17 00:00:00 2001 From: Rakshit Gondwal Date: Sat, 15 Apr 2023 23:28:23 +0530 Subject: [PATCH 1/6] feat: improve HPA analyzer to check ScaleTargetRef resources Signed-off-by: Rakshit Gondwal --- pkg/analyzer/hpa.go | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/pkg/analyzer/hpa.go b/pkg/analyzer/hpa.go index 6c7585a68b..b1bb57a145 100644 --- a/pkg/analyzer/hpa.go +++ b/pkg/analyzer/hpa.go @@ -28,10 +28,23 @@ func (HpaAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) { switch scaleTargetRef.Kind { case "Deployment": - _, err := a.Client.GetClient().AppsV1().Deployments(hpa.Namespace).Get(a.Context, scaleTargetRef.Name, metav1.GetOptions{}) - if err != nil { - scaleTargetRefNotFound = true - } + deployment, err := a.Client.GetClient().AppsV1().Deployments(hpa.Namespace).Get(a.Context, scaleTargetRef.Name, metav1.GetOptions{}) + if err != nil { + scaleTargetRefNotFound = true + } else { + // check if the deployment has resource configured + if (deployment.Spec.Template.Spec.Containers[0].Resources.Requests == nil) || (deployment.Spec.Template.Spec.Containers[0].Resources.Limits == nil) { + failures = append(failures, common.Failure{ + Text: fmt.Sprintf("Deployment %s/%s does not have resource configured.", deployment.Namespace, deployment.Name), + Sensitive: []common.Sensitive{ + { + Unmasked: deployment.Name, + Masked: util.MaskString(deployment.Name), + }, + }, + }) + } + } case "ReplicationController": _, err := a.Client.GetClient().CoreV1().ReplicationControllers(hpa.Namespace).Get(a.Context, scaleTargetRef.Name, metav1.GetOptions{}) if err != nil { From 62e3fb0017e66c131e4238cc8b2f80a52e22949d Mon Sep 17 00:00:00 2001 From: Rakshit Gondwal Date: Sat, 15 Apr 2023 23:29:38 +0530 Subject: [PATCH 2/6] feat: modify tests Signed-off-by: Rakshit Gondwal --- pkg/analyzer/hpaAnalyzer_test.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/pkg/analyzer/hpaAnalyzer_test.go b/pkg/analyzer/hpaAnalyzer_test.go index dfe9f13f0c..aad5b47378 100644 --- a/pkg/analyzer/hpaAnalyzer_test.go +++ b/pkg/analyzer/hpaAnalyzer_test.go @@ -10,6 +10,8 @@ import ( "github.com/magiconair/properties/assert" appsv1 "k8s.io/api/apps/v1" autoscalingv1 "k8s.io/api/autoscaling/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes/fake" ) @@ -185,6 +187,28 @@ func TestHPAAnalyzerWithExistingScaleTargetRef(t *testing.T) { Namespace: "default", Annotations: map[string]string{}, }, + Spec: appsv1.DeploymentSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "example", + Image: "nginx", + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + "cpu": resource.MustParse("100m"), + "memory": resource.MustParse("128Mi"), + }, + Limits: corev1.ResourceList{ + "cpu": resource.MustParse("200m"), + "memory": resource.MustParse("256Mi"), + }, + }, + }, + }, + }, + }, + }, }, ) hpaAnalyzer := HpaAnalyzer{} From 3150b1bd3fd7a34eb0b2601728af95eafde503c3 Mon Sep 17 00:00:00 2001 From: Rakshit Gondwal Date: Mon, 17 Apr 2023 18:08:46 +0530 Subject: [PATCH 3/6] feat: improve all ScaleTargetRef to check for resources Signed-off-by: Rakshit Gondwal --- pkg/analyzer/hpa.go | 79 +++++++++++++++++++++++++++++++++------------ 1 file changed, 59 insertions(+), 20 deletions(-) diff --git a/pkg/analyzer/hpa.go b/pkg/analyzer/hpa.go index 9394ef980e..e53f605786 100644 --- a/pkg/analyzer/hpa.go +++ b/pkg/analyzer/hpa.go @@ -34,37 +34,76 @@ func (HpaAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) { switch scaleTargetRef.Kind { case "Deployment": - deployment, err := a.Client.GetClient().AppsV1().Deployments(hpa.Namespace).Get(a.Context, scaleTargetRef.Name, metav1.GetOptions{}) - if err != nil { - scaleTargetRefNotFound = true - } else { - // check if the deployment has resource configured - if (deployment.Spec.Template.Spec.Containers[0].Resources.Requests == nil) || (deployment.Spec.Template.Spec.Containers[0].Resources.Limits == nil) { - failures = append(failures, common.Failure{ - Text: fmt.Sprintf("Deployment %s/%s does not have resource configured.", deployment.Namespace, deployment.Name), - Sensitive: []common.Sensitive{ - { - Unmasked: deployment.Name, - Masked: util.MaskString(deployment.Name), - }, - }, - }) - } - } + deployment, err := a.Client.GetClient().AppsV1().Deployments(hpa.Namespace).Get(a.Context, scaleTargetRef.Name, metav1.GetOptions{}) + if err != nil { + scaleTargetRefNotFound = true + } else { + // check if the deployment has resource configured + if (deployment.Spec.Template.Spec.Containers[0].Resources.Requests == nil) || (deployment.Spec.Template.Spec.Containers[0].Resources.Limits == nil) { + failures = append(failures, common.Failure{ + Text: fmt.Sprintf("Deployment %s/%s does not have resource configured.", deployment.Namespace, deployment.Name), + Sensitive: []common.Sensitive{ + { + Unmasked: deployment.Name, + Masked: util.MaskString(deployment.Name), + }, + }, + }) + } + } case "ReplicationController": - _, err := a.Client.GetClient().CoreV1().ReplicationControllers(hpa.Namespace).Get(a.Context, scaleTargetRef.Name, metav1.GetOptions{}) + rc, err := a.Client.GetClient().CoreV1().ReplicationControllers(hpa.Namespace).Get(a.Context, scaleTargetRef.Name, metav1.GetOptions{}) if err != nil { scaleTargetRefNotFound = true + } else { + // check if the replication controller has resource configured + if (rc.Spec.Template.Spec.Containers[0].Resources.Requests == nil) || (rc.Spec.Template.Spec.Containers[0].Resources.Limits == nil) { + failures = append(failures, common.Failure{ + Text: fmt.Sprintf("ReplicationController %s/%s does not have resource configured.", rc.Namespace, rc.Name), + Sensitive: []common.Sensitive{ + { + Unmasked: rc.Name, + Masked: util.MaskString(rc.Name), + }, + }, + }) + } } case "ReplicaSet": - _, err := a.Client.GetClient().AppsV1().ReplicaSets(hpa.Namespace).Get(a.Context, scaleTargetRef.Name, metav1.GetOptions{}) + rs, err := a.Client.GetClient().AppsV1().ReplicaSets(hpa.Namespace).Get(a.Context, scaleTargetRef.Name, metav1.GetOptions{}) if err != nil { scaleTargetRefNotFound = true + } else { + // check if the replica set has resource configured + if (rs.Spec.Template.Spec.Containers[0].Resources.Requests == nil) || (rs.Spec.Template.Spec.Containers[0].Resources.Limits == nil) { + failures = append(failures, common.Failure{ + Text: fmt.Sprintf("ReplicaSet %s/%s does not have resource configured.", rs.Namespace, rs.Name), + Sensitive: []common.Sensitive{ + { + Unmasked: rs.Name, + Masked: util.MaskString(rs.Name), + }, + }, + }) + } } case "StatefulSet": - _, err := a.Client.GetClient().AppsV1().StatefulSets(hpa.Namespace).Get(a.Context, scaleTargetRef.Name, metav1.GetOptions{}) + ss, err := a.Client.GetClient().AppsV1().StatefulSets(hpa.Namespace).Get(a.Context, scaleTargetRef.Name, metav1.GetOptions{}) if err != nil { scaleTargetRefNotFound = true + } else { + // check if the stateful set has resource configured + if (ss.Spec.Template.Spec.Containers[0].Resources.Requests == nil) || (ss.Spec.Template.Spec.Containers[0].Resources.Limits == nil) { + failures = append(failures, common.Failure{ + Text: fmt.Sprintf("StatefulSet %s/%s does not have resource configured.", ss.Namespace, ss.Name), + Sensitive: []common.Sensitive{ + { + Unmasked: ss.Name, + Masked: util.MaskString(ss.Name), + }, + }, + }) + } } default: failures = append(failures, common.Failure{ From fc62ac694dfbedc94308d073eeeb45230f99e8b6 Mon Sep 17 00:00:00 2001 From: Rakshit Gondwal Date: Mon, 17 Apr 2023 18:10:20 +0530 Subject: [PATCH 4/6] test: add test cases for all ScaleTargetRef types Signed-off-by: Rakshit Gondwal --- pkg/analyzer/hpaAnalyzer_test.go | 194 ++++++++++++++++++++++++++++++- 1 file changed, 193 insertions(+), 1 deletion(-) diff --git a/pkg/analyzer/hpaAnalyzer_test.go b/pkg/analyzer/hpaAnalyzer_test.go index aad5b47378..5cec6b7b38 100644 --- a/pkg/analyzer/hpaAnalyzer_test.go +++ b/pkg/analyzer/hpaAnalyzer_test.go @@ -165,7 +165,7 @@ func TestHPAAnalyzerWithNonExistentScaleTargetRef(t *testing.T) { } } -func TestHPAAnalyzerWithExistingScaleTargetRef(t *testing.T) { +func TestHPAAnalyzerWithExistingScaleTargetRefAsDeployment(t *testing.T) { clientset := fake.NewSimpleClientset( &autoscalingv1.HorizontalPodAutoscaler{ @@ -228,3 +228,195 @@ func TestHPAAnalyzerWithExistingScaleTargetRef(t *testing.T) { assert.Equal(t, len(analysis.Error), 0) } } + +func TestHPAAnalyzerWithExistingScaleTargetRefAsReplicationController(t *testing.T) { + + clientset := fake.NewSimpleClientset( + &autoscalingv1.HorizontalPodAutoscaler{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example", + Namespace: "default", + Annotations: map[string]string{}, + }, + Spec: autoscalingv1.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: autoscalingv1.CrossVersionObjectReference{ + Kind: "ReplicationController", + Name: "example", + }, + }, + }, + &corev1.ReplicationController{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example", + Namespace: "default", + Annotations: map[string]string{}, + }, + Spec: corev1.ReplicationControllerSpec{ + Template: &corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "example", + Image: "nginx", + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + "cpu": resource.MustParse("100m"), + "memory": resource.MustParse("128Mi"), + }, + Limits: corev1.ResourceList{ + "cpu": resource.MustParse("200m"), + "memory": resource.MustParse("256Mi"), + }, + }, + }, + }, + }, + }, + }, + }, + ) + hpaAnalyzer := HpaAnalyzer{} + + config := common.Analyzer{ + Client: &kubernetes.Client{ + Client: clientset, + }, + Context: context.Background(), + Namespace: "default", + } + analysisResults, err := hpaAnalyzer.Analyze(config) + if err != nil { + t.Error(err) + } + for _, analysis := range analysisResults { + assert.Equal(t, len(analysis.Error), 0) + } +} + +func TestHPAAnalyzerWithExistingScaleTargetRefAsReplicaSet(t *testing.T) { + + clientset := fake.NewSimpleClientset( + &autoscalingv1.HorizontalPodAutoscaler{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example", + Namespace: "default", + Annotations: map[string]string{}, + }, + Spec: autoscalingv1.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: autoscalingv1.CrossVersionObjectReference{ + Kind: "ReplicaSet", + Name: "example", + }, + }, + }, + &appsv1.ReplicaSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example", + Namespace: "default", + Annotations: map[string]string{}, + }, + Spec: appsv1.ReplicaSetSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "example", + Image: "nginx", + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + "cpu": resource.MustParse("100m"), + "memory": resource.MustParse("128Mi"), + }, + Limits: corev1.ResourceList{ + "cpu": resource.MustParse("200m"), + "memory": resource.MustParse("256Mi"), + }, + }, + }, + }, + }, + }, + }, + }, + ) + hpaAnalyzer := HpaAnalyzer{} + + config := common.Analyzer{ + Client: &kubernetes.Client{ + Client: clientset, + }, + Context: context.Background(), + Namespace: "default", + } + analysisResults, err := hpaAnalyzer.Analyze(config) + if err != nil { + t.Error(err) + } + for _, analysis := range analysisResults { + assert.Equal(t, len(analysis.Error), 0) + } +} + +func TestHPAAnalyzerWithExistingScaleTargetRefAsStatefulSet(t *testing.T) { + + clientset := fake.NewSimpleClientset( + &autoscalingv1.HorizontalPodAutoscaler{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example", + Namespace: "default", + Annotations: map[string]string{}, + }, + Spec: autoscalingv1.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: autoscalingv1.CrossVersionObjectReference{ + Kind: "StatefulSet", + Name: "example", + }, + }, + }, + &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example", + Namespace: "default", + Annotations: map[string]string{}, + }, + Spec: appsv1.StatefulSetSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "example", + Image: "nginx", + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + "cpu": resource.MustParse("100m"), + "memory": resource.MustParse("128Mi"), + }, + Limits: corev1.ResourceList{ + "cpu": resource.MustParse("200m"), + "memory": resource.MustParse("256Mi"), + }, + }, + }, + }, + }, + }, + }, + }, + ) + hpaAnalyzer := HpaAnalyzer{} + + config := common.Analyzer{ + Client: &kubernetes.Client{ + Client: clientset, + }, + Context: context.Background(), + Namespace: "default", + } + analysisResults, err := hpaAnalyzer.Analyze(config) + if err != nil { + t.Error(err) + } + for _, analysis := range analysisResults { + assert.Equal(t, len(analysis.Error), 0) + } +} From af06ef19d60325a08b2f186b6a600a7c22f5126d Mon Sep 17 00:00:00 2001 From: Matthis <99146727+matthisholleville@users.noreply.github.com> Date: Mon, 17 Apr 2023 17:58:22 +0200 Subject: [PATCH 5/6] refactor: use interface to avoid dupplication Signed-off-by: Matthis Holleville Signed-off-by: Rakshit Gondwal --- pkg/analyzer/hpa.go | 134 ++++++++++++++++++++++++-------------------- 1 file changed, 72 insertions(+), 62 deletions(-) diff --git a/pkg/analyzer/hpa.go b/pkg/analyzer/hpa.go index e53f605786..70b3a5096b 100644 --- a/pkg/analyzer/hpa.go +++ b/pkg/analyzer/hpa.go @@ -5,6 +5,8 @@ import ( "github.com/k8sgpt-ai/k8sgpt/pkg/common" "github.com/k8sgpt-ai/k8sgpt/pkg/util" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -30,80 +32,28 @@ func (HpaAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) { // check ScaleTargetRef exist scaleTargetRef := hpa.Spec.ScaleTargetRef - scaleTargetRefNotFound := false + var podInfo PodInfo switch scaleTargetRef.Kind { case "Deployment": deployment, err := a.Client.GetClient().AppsV1().Deployments(hpa.Namespace).Get(a.Context, scaleTargetRef.Name, metav1.GetOptions{}) - if err != nil { - scaleTargetRefNotFound = true - } else { - // check if the deployment has resource configured - if (deployment.Spec.Template.Spec.Containers[0].Resources.Requests == nil) || (deployment.Spec.Template.Spec.Containers[0].Resources.Limits == nil) { - failures = append(failures, common.Failure{ - Text: fmt.Sprintf("Deployment %s/%s does not have resource configured.", deployment.Namespace, deployment.Name), - Sensitive: []common.Sensitive{ - { - Unmasked: deployment.Name, - Masked: util.MaskString(deployment.Name), - }, - }, - }) - } + if err == nil { + podInfo = DeploymentInfo{deployment} } case "ReplicationController": rc, err := a.Client.GetClient().CoreV1().ReplicationControllers(hpa.Namespace).Get(a.Context, scaleTargetRef.Name, metav1.GetOptions{}) - if err != nil { - scaleTargetRefNotFound = true - } else { - // check if the replication controller has resource configured - if (rc.Spec.Template.Spec.Containers[0].Resources.Requests == nil) || (rc.Spec.Template.Spec.Containers[0].Resources.Limits == nil) { - failures = append(failures, common.Failure{ - Text: fmt.Sprintf("ReplicationController %s/%s does not have resource configured.", rc.Namespace, rc.Name), - Sensitive: []common.Sensitive{ - { - Unmasked: rc.Name, - Masked: util.MaskString(rc.Name), - }, - }, - }) - } + if err == nil { + podInfo = ReplicationControllerInfo{rc} } case "ReplicaSet": rs, err := a.Client.GetClient().AppsV1().ReplicaSets(hpa.Namespace).Get(a.Context, scaleTargetRef.Name, metav1.GetOptions{}) - if err != nil { - scaleTargetRefNotFound = true - } else { - // check if the replica set has resource configured - if (rs.Spec.Template.Spec.Containers[0].Resources.Requests == nil) || (rs.Spec.Template.Spec.Containers[0].Resources.Limits == nil) { - failures = append(failures, common.Failure{ - Text: fmt.Sprintf("ReplicaSet %s/%s does not have resource configured.", rs.Namespace, rs.Name), - Sensitive: []common.Sensitive{ - { - Unmasked: rs.Name, - Masked: util.MaskString(rs.Name), - }, - }, - }) - } + if err == nil { + podInfo = ReplicaSetInfo{rs} } case "StatefulSet": ss, err := a.Client.GetClient().AppsV1().StatefulSets(hpa.Namespace).Get(a.Context, scaleTargetRef.Name, metav1.GetOptions{}) - if err != nil { - scaleTargetRefNotFound = true - } else { - // check if the stateful set has resource configured - if (ss.Spec.Template.Spec.Containers[0].Resources.Requests == nil) || (ss.Spec.Template.Spec.Containers[0].Resources.Limits == nil) { - failures = append(failures, common.Failure{ - Text: fmt.Sprintf("StatefulSet %s/%s does not have resource configured.", ss.Namespace, ss.Name), - Sensitive: []common.Sensitive{ - { - Unmasked: ss.Name, - Masked: util.MaskString(ss.Name), - }, - }, - }) - } + if err == nil { + podInfo = StatefulSetInfo{ss} } default: failures = append(failures, common.Failure{ @@ -112,7 +62,7 @@ func (HpaAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) { }) } - if scaleTargetRefNotFound { + if podInfo == nil { failures = append(failures, common.Failure{ Text: fmt.Sprintf("HorizontalPodAutoscaler uses %s/%s as ScaleTargetRef which does not exist.", scaleTargetRef.Kind, scaleTargetRef.Name), Sensitive: []common.Sensitive{ @@ -122,6 +72,26 @@ func (HpaAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) { }, }, }) + } else { + containers := len(podInfo.GetPodSpec().Containers) + for _, container := range podInfo.GetPodSpec().Containers { + if container.Resources.Requests == nil || container.Resources.Limits == nil { + containers-- + } + } + + if containers <= 0 { + failures = append(failures, common.Failure{ + Text: fmt.Sprintf("%s %s/%s does not have resource configured.", scaleTargetRef.Kind, a.Namespace, scaleTargetRef.Name), + Sensitive: []common.Sensitive{ + { + Unmasked: scaleTargetRef.Name, + Masked: util.MaskString(scaleTargetRef.Name), + }, + }, + }) + } + } if len(failures) > 0 { @@ -148,3 +118,43 @@ func (HpaAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) { return a.Results, nil } + +type PodInfo interface { + GetPodSpec() corev1.PodSpec +} + +type DeploymentInfo struct { + *appsv1.Deployment +} + +func (d DeploymentInfo) GetPodSpec() corev1.PodSpec { + return d.Spec.Template.Spec +} + +// define a structure for ReplicationController +type ReplicationControllerInfo struct { + *corev1.ReplicationController +} + +func (rc ReplicationControllerInfo) GetPodSpec() corev1.PodSpec { + return rc.Spec.Template.Spec +} + +// define a structure for ReplicaSet +type ReplicaSetInfo struct { + *appsv1.ReplicaSet +} + +func (rs ReplicaSetInfo) GetPodSpec() corev1.PodSpec { + return rs.Spec.Template.Spec +} + +// define a structure for StatefulSet +type StatefulSetInfo struct { + *appsv1.StatefulSet +} + +// implement PodInfo for StatefulSetInfo +func (ss StatefulSetInfo) GetPodSpec() corev1.PodSpec { + return ss.Spec.Template.Spec +} From 970b6f7dabf5420bb47dc8b22c17a77d35e8de08 Mon Sep 17 00:00:00 2001 From: Rakshit Gondwal Date: Tue, 18 Apr 2023 16:51:44 +0530 Subject: [PATCH 6/6] test: add test case for NoResourceConfiguredForScaleTargetRef Signed-off-by: Rakshit Gondwal --- pkg/analyzer/hpaAnalyzer_test.go | 67 ++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/pkg/analyzer/hpaAnalyzer_test.go b/pkg/analyzer/hpaAnalyzer_test.go index c854fb3c8b..23e7c2c5c1 100644 --- a/pkg/analyzer/hpaAnalyzer_test.go +++ b/pkg/analyzer/hpaAnalyzer_test.go @@ -421,6 +421,73 @@ func TestHPAAnalyzerWithExistingScaleTargetRefAsStatefulSet(t *testing.T) { } } +func TestHPAAnalyzerWithExistingScaleTargetRefWithoutSpecifyingResources(t *testing.T) { + + clientset := fake.NewSimpleClientset( + &autoscalingv1.HorizontalPodAutoscaler{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example", + Namespace: "default", + Annotations: map[string]string{}, + }, + Spec: autoscalingv1.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: autoscalingv1.CrossVersionObjectReference{ + Kind: "Deployment", + Name: "example", + }, + }, + }, + &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example", + Namespace: "default", + Annotations: map[string]string{}, + }, + Spec: appsv1.DeploymentSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "example", + Image: "nginx", + }, + }, + }, + }, + }, + }, + ) + hpaAnalyzer := HpaAnalyzer{} + + config := common.Analyzer{ + Client: &kubernetes.Client{ + Client: clientset, + }, + Context: context.Background(), + Namespace: "default", + } + analysisResults, err := hpaAnalyzer.Analyze(config) + if err != nil { + t.Error(err) + } + + var errorFound bool + for _, analysis := range analysisResults { + for _, err := range analysis.Error { + if strings.Contains(err.Text, "does not have resource configured."){ + errorFound = true + break + } + if errorFound { + break + } + } + if !errorFound { + t.Error("expected error 'does not have resource configured.' not found in analysis results") + } + } +} + func TestHPAAnalyzerNamespaceFiltering(t *testing.T) { clientset := fake.NewSimpleClientset( &autoscalingv1.HorizontalPodAutoscaler{