add unit tests for hpa reconciler

Signed-off-by: Sanskar Jaiswal <sanskar.jaiswal@weave.works>
This commit is contained in:
Sanskar Jaiswal
2022-06-07 13:43:11 +05:30
parent fa595e160c
commit f23be1d0ec
4 changed files with 242 additions and 61 deletions
-29
View File
@@ -50,10 +50,6 @@ func TestDeploymentController_Sync_ConsistentNaming(t *testing.T) {
annotation := depPrimary.Annotations["kustomize.toolkit.fluxcd.io/checksum"]
assert.Equal(t, "", annotation)
hpaPrimary, err := mocks.kubeClient.AutoscalingV2beta2().HorizontalPodAutoscalers("default").Get(context.TODO(), "podinfo-primary", metav1.GetOptions{})
require.NoError(t, err)
assert.Equal(t, depPrimary.Name, hpaPrimary.Spec.ScaleTargetRef.Name)
}
func TestDeploymentController_Sync_InconsistentNaming(t *testing.T) {
@@ -71,10 +67,6 @@ func TestDeploymentController_Sync_InconsistentNaming(t *testing.T) {
primarySelectorValue := depPrimary.Spec.Selector.MatchLabels[dc.label]
assert.Equal(t, primarySelectorValue, fmt.Sprintf("%s-primary", dc.labelValue))
hpaPrimary, err := mocks.kubeClient.AutoscalingV2beta2().HorizontalPodAutoscalers("default").Get(context.TODO(), "podinfo-primary", metav1.GetOptions{})
require.NoError(t, err)
assert.Equal(t, depPrimary.Name, hpaPrimary.Spec.ScaleTargetRef.Name)
}
func TestDeploymentController_Promote(t *testing.T) {
@@ -90,15 +82,6 @@ func TestDeploymentController_Promote(t *testing.T) {
_, err = mocks.kubeClient.CoreV1().ConfigMaps("default").Update(context.TODO(), config2, metav1.UpdateOptions{})
require.NoError(t, err)
hpa, err := mocks.kubeClient.AutoscalingV2beta2().HorizontalPodAutoscalers("default").Get(context.TODO(), "podinfo", metav1.GetOptions{})
require.NoError(t, err)
hpaClone := hpa.DeepCopy()
hpaClone.Spec.MaxReplicas = 2
_, err = mocks.kubeClient.AutoscalingV2beta2().HorizontalPodAutoscalers("default").Update(context.TODO(), hpaClone, metav1.UpdateOptions{})
require.NoError(t, err)
err = mocks.controller.Promote(mocks.canary)
require.NoError(t, err)
@@ -121,18 +104,6 @@ func TestDeploymentController_Promote(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, config2.Data["color"], configPrimary.Data["color"])
hpaPrimary, err := mocks.kubeClient.AutoscalingV2beta2().HorizontalPodAutoscalers("default").Get(context.TODO(), "podinfo-primary", metav1.GetOptions{})
require.NoError(t, err)
assert.Equal(t, int32(2), hpaPrimary.Spec.MaxReplicas)
hpaPrimaryLabels := hpaPrimary.ObjectMeta.Labels
hpaSourceLabels := hpa.ObjectMeta.Labels
assert.Equal(t, hpaSourceLabels["app.kubernetes.io/test-label-1"], hpaPrimaryLabels["app.kubernetes.io/test-label-1"])
hpaPrimaryAnnotations := hpaPrimary.ObjectMeta.Annotations
hpaSourceAnnotations := hpa.ObjectMeta.Annotations
assert.Equal(t, hpaSourceAnnotations["app.kubernetes.io/test-annotation-1"], hpaPrimaryAnnotations["app.kubernetes.io/test-annotation-1"])
value := depPrimary.Spec.Template.Spec.Affinity.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution[0].PodAffinityTerm.LabelSelector.MatchExpressions[0].Values[0]
assert.Equal(t, "podinfo-primary", value)
-32
View File
@@ -24,7 +24,6 @@ import (
"github.com/stretchr/testify/require"
"go.uber.org/zap"
appsv1 "k8s.io/api/apps/v1"
hpav2 "k8s.io/api/autoscaling/v2beta2"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -91,7 +90,6 @@ func newCustomizableFixture(dc deploymentConfigs) (deploymentControllerFixture,
// init kube clientset and register mock objects
kubeClient := fake.NewSimpleClientset(
newDeploymentControllerTest(dc),
newDeploymentControllerTestHPA(),
newDeploymentControllerTestConfigMap(),
newDeploymentControllerTestConfigMapEnv(),
newDeploymentControllerTestConfigMapVol(),
@@ -1000,33 +998,3 @@ func newDeploymentControllerTestV2() *appsv1.Deployment {
return d
}
func newDeploymentControllerTestHPA() *hpav2.HorizontalPodAutoscaler {
h := &hpav2.HorizontalPodAutoscaler{
TypeMeta: metav1.TypeMeta{APIVersion: hpav2.SchemeGroupVersion.String()},
ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
Name: "podinfo",
},
Spec: hpav2.HorizontalPodAutoscalerSpec{
ScaleTargetRef: hpav2.CrossVersionObjectReference{
Name: "podinfo",
APIVersion: "apps/v1",
Kind: "Deployment",
},
Metrics: []hpav2.MetricSpec{
{
Type: "Resource",
Resource: &hpav2.ResourceMetricSource{
Name: "cpu",
Target: hpav2.MetricTarget{
AverageUtilization: int32p(99),
},
},
},
},
},
}
return h
}
+106
View File
@@ -0,0 +1,106 @@
package canary
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
hpav2 "k8s.io/api/autoscaling/v2"
hpav2beta2 "k8s.io/api/autoscaling/v2beta2"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func Test_reconcilePrimaryHpa(t *testing.T) {
mocks := newScalerReconcilerFixture(scalerConfig{
targetName: "podinfo",
scaler: "HorizontalPodAutoscaler",
// avoid creating a v2 HPA.
excludeObjs: []string{"HPAV2"},
})
hpaReconciler := mocks.scalerReconciler.(*HPAReconciler)
err := hpaReconciler.reconcilePrimaryHpa(mocks.canary, true)
require.NoError(t, err)
// assert that we fallback to v2beta2, when HPAv2 fails.
_, err = mocks.kubeClient.AutoscalingV2().HorizontalPodAutoscalers("default").Get(context.TODO(), "podinfo-primary", metav1.GetOptions{})
assert.True(t, errors.IsNotFound(err))
hpa, err := mocks.kubeClient.AutoscalingV2beta2().HorizontalPodAutoscalers("default").Get(context.TODO(), "podinfo-primary", metav1.GetOptions{})
require.NoError(t, err)
require.NotNil(t, hpa)
mocks = newScalerReconcilerFixture(scalerConfig{
targetName: "podinfo",
scaler: "HorizontalPodAutoscaler",
// avoid creating _any_ HPAs.
excludeObjs: []string{"HPAV2", "HPAV2Beta2"},
})
hpaReconciler = mocks.scalerReconciler.(*HPAReconciler)
// assert that we return an error if no HPAs are found.
err = hpaReconciler.reconcilePrimaryHpa(mocks.canary, true)
require.Error(t, err)
}
func Test_reconcilePrimaryHpaV2(t *testing.T) {
mocks := newScalerReconcilerFixture(scalerConfig{
targetName: "podinfo",
scaler: "HorizontalPodAutoscaler",
})
hpaReconciler := mocks.scalerReconciler.(*HPAReconciler)
hpa, err := mocks.kubeClient.AutoscalingV2().HorizontalPodAutoscalers("default").Get(context.TODO(), "podinfo", metav1.GetOptions{})
require.NoError(t, err)
err = hpaReconciler.reconcilePrimaryHpaV2(mocks.canary, hpa, true)
require.NoError(t, err)
primaryHPA, err := mocks.kubeClient.AutoscalingV2().HorizontalPodAutoscalers("default").Get(context.TODO(), "podinfo-primary", metav1.GetOptions{})
require.NoError(t, err)
assert.Equal(t, primaryHPA.Spec.ScaleTargetRef.Name, "podinfo-primary")
assert.Equal(t, int(*primaryHPA.Spec.Metrics[0].Resource.Target.AverageUtilization), 99)
hpa.Spec.Metrics[0].Resource.Target = hpav2.MetricTarget{AverageUtilization: int32p(50)}
_, err = mocks.kubeClient.AutoscalingV2().HorizontalPodAutoscalers("default").Update(context.TODO(), hpa, metav1.UpdateOptions{})
require.NoError(t, err)
err = hpaReconciler.reconcilePrimaryHpaV2(mocks.canary, hpa, false)
require.NoError(t, err)
primaryHPA, err = mocks.kubeClient.AutoscalingV2().HorizontalPodAutoscalers("default").Get(context.TODO(), "podinfo-primary", metav1.GetOptions{})
require.NoError(t, err)
assert.Equal(t, int(*primaryHPA.Spec.Metrics[0].Resource.Target.AverageUtilization), 50)
}
func Test_reconcilePrimaryHpaV2Beta2(t *testing.T) {
mocks := newScalerReconcilerFixture(scalerConfig{
targetName: "podinfo",
scaler: "HorizontalPodAutoscaler",
})
hpaReconciler := mocks.scalerReconciler.(*HPAReconciler)
hpa, err := mocks.kubeClient.AutoscalingV2beta2().HorizontalPodAutoscalers("default").Get(context.TODO(), "podinfo", metav1.GetOptions{})
require.NoError(t, err)
err = hpaReconciler.reconcilePrimaryHpaV2Beta2(mocks.canary, hpa, true)
require.NoError(t, err)
primaryHPA, err := mocks.kubeClient.AutoscalingV2beta2().HorizontalPodAutoscalers("default").Get(context.TODO(), "podinfo-primary", metav1.GetOptions{})
require.NoError(t, err)
assert.Equal(t, primaryHPA.Spec.ScaleTargetRef.Name, "podinfo-primary")
assert.Equal(t, int(*primaryHPA.Spec.Metrics[0].Resource.Target.AverageUtilization), 99)
hpa.Spec.Metrics[0].Resource.Target = hpav2beta2.MetricTarget{AverageUtilization: int32p(50)}
_, err = mocks.kubeClient.AutoscalingV2beta2().HorizontalPodAutoscalers("default").Update(context.TODO(), hpa, metav1.UpdateOptions{})
require.NoError(t, err)
err = hpaReconciler.reconcilePrimaryHpaV2Beta2(mocks.canary, hpa, false)
require.NoError(t, err)
primaryHPA, err = mocks.kubeClient.AutoscalingV2beta2().HorizontalPodAutoscalers("default").Get(context.TODO(), "podinfo-primary", metav1.GetOptions{})
require.NoError(t, err)
assert.Equal(t, int(*primaryHPA.Spec.Metrics[0].Resource.Target.AverageUtilization), 50)
}
@@ -0,0 +1,136 @@
package canary
import (
"go.uber.org/zap"
hpav2 "k8s.io/api/autoscaling/v2"
hpav2beta2 "k8s.io/api/autoscaling/v2beta2"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/fake"
flaggerv1 "github.com/fluxcd/flagger/pkg/apis/flagger/v1beta1"
clientset "github.com/fluxcd/flagger/pkg/client/clientset/versioned"
fakeFlagger "github.com/fluxcd/flagger/pkg/client/clientset/versioned/fake"
"github.com/fluxcd/flagger/pkg/logger"
)
type scalerReconcilerFixture struct {
canary *flaggerv1.Canary
kubeClient kubernetes.Interface
flaggerClient clientset.Interface
scalerReconciler ScalerReconciler
logger *zap.SugaredLogger
}
type scalerConfig struct {
targetName string
excludeObjs []string
scaler string
}
func newScalerReconcilerFixture(cfg scalerConfig) scalerReconcilerFixture {
canary := newDeploymentControllerTestCanary(canaryConfigs{targetName: cfg.targetName})
flaggerClient := fakeFlagger.NewSimpleClientset(canary)
kubeClient := fake.NewSimpleClientset(
newScalerReconcilerTestHPAV2(),
newScalerReconcilerTestHPAV2Beta2(),
)
for _, obj := range cfg.excludeObjs {
if obj == "HPAV2" {
kubeClient.Tracker().Delete(schema.GroupVersionResource{
Group: "autoscaling",
Version: "v2",
Resource: "horizontalpodautoscalers",
}, "default", "podinfo")
}
if obj == "HPAV2Beta2" {
kubeClient.Tracker().Delete(schema.GroupVersionResource{
Group: "autoscaling",
Version: "v2beta2",
Resource: "horizontalpodautoscalers",
}, "default", "podinfo")
}
}
logger, _ := logger.NewLogger("debug")
var hpaReconciler HPAReconciler
if cfg.scaler == "HorizontalPodAutoscaler" {
hpaReconciler = HPAReconciler{
kubeClient: kubeClient,
flaggerClient: flaggerClient,
logger: logger,
includeLabelPrefix: []string{"app.kubernetes.io"},
}
}
return scalerReconcilerFixture{
canary: canary,
kubeClient: kubeClient,
flaggerClient: flaggerClient,
scalerReconciler: &hpaReconciler,
logger: logger,
}
}
func newScalerReconcilerTestHPAV2Beta2() *hpav2beta2.HorizontalPodAutoscaler {
h := &hpav2beta2.HorizontalPodAutoscaler{
TypeMeta: metav1.TypeMeta{APIVersion: hpav2beta2.SchemeGroupVersion.String()},
ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
Name: "podinfo",
},
Spec: hpav2beta2.HorizontalPodAutoscalerSpec{
ScaleTargetRef: hpav2beta2.CrossVersionObjectReference{
Name: "podinfo",
APIVersion: "apps/v1",
Kind: "Deployment",
},
Metrics: []hpav2beta2.MetricSpec{
{
Type: "Resource",
Resource: &hpav2beta2.ResourceMetricSource{
Name: "cpu",
Target: hpav2beta2.MetricTarget{
AverageUtilization: int32p(99),
},
},
},
},
},
}
return h
}
func newScalerReconcilerTestHPAV2() *hpav2.HorizontalPodAutoscaler {
h := &hpav2.HorizontalPodAutoscaler{
TypeMeta: metav1.TypeMeta{APIVersion: hpav2.SchemeGroupVersion.String()},
ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
Name: "podinfo",
},
Spec: hpav2.HorizontalPodAutoscalerSpec{
ScaleTargetRef: hpav2.CrossVersionObjectReference{
Name: "podinfo",
APIVersion: "apps/v1",
Kind: "Deployment",
},
Metrics: []hpav2.MetricSpec{
{
Type: "Resource",
Resource: &hpav2.ResourceMetricSource{
Name: "cpu",
Target: hpav2.MetricTarget{
AverageUtilization: int32p(99),
},
},
},
},
},
}
return h
}