add unit tests for ScaledObjectReconciler

Signed-off-by: Sanskar Jaiswal <sanskar.jaiswal@weave.works>
This commit is contained in:
Sanskar Jaiswal
2022-06-09 21:38:10 +05:30
parent b2dc762937
commit 6a1b765a77
5 changed files with 194 additions and 21 deletions
+1 -1
View File
@@ -395,7 +395,7 @@ func newDeploymentControllerTestCanary(cc canaryConfigs) *flaggerv1.Canary {
APIVersion: "apps/v1",
Kind: "Deployment",
},
AutoscalerRef: &flaggerv1.LocalObjectReference{
AutoscalerRef: &flaggerv1.AutoscalerRefernce{
Name: "podinfo",
APIVersion: "autoscaling/v2beta2",
Kind: "HorizontalPodAutoscaler",
+15 -14
View File
@@ -37,38 +37,39 @@ func (sor *ScaledObjectReconciler) ReconcilePrimaryScaler(cd *flaggerv1.Canary,
}
func (sor *ScaledObjectReconciler) reconcilePrimaryScaler(cd *flaggerv1.Canary, init bool) error {
primaryName := fmt.Sprintf("%s-primary", cd.Spec.TargetRef.Name)
primaryName := fmt.Sprintf("%s-primary", cd.Spec.AutoscalerRef.Name)
targetSo, err := sor.flaggerClient.KedaV1alpha1().ScaledObjects(cd.Namespace).Get(context.TODO(), cd.Spec.AutoscalerRef.Name, metav1.GetOptions{})
if err != nil {
return fmt.Errorf("Keda ScaledObject %s.%s get query error: %w",
cd.Spec.AutoscalerRef.Name, cd.Namespace, err)
}
targetSoClone := targetSo.DeepCopy()
for _, trigger := range targetSo.Spec.Triggers {
for _, trigger := range targetSoClone.Spec.Triggers {
setPrimaryScaledObjectQuery(cd, trigger.Metadata)
}
soSpec := keda.ScaledObjectSpec{
ScaleTargetRef: &keda.ScaleTarget{
Name: primaryName,
Kind: targetSo.Spec.ScaleTargetRef.Kind,
APIVersion: targetSo.Spec.ScaleTargetRef.APIVersion,
EnvSourceContainerName: targetSo.Spec.ScaleTargetRef.EnvSourceContainerName,
Kind: targetSoClone.Spec.ScaleTargetRef.Kind,
APIVersion: targetSoClone.Spec.ScaleTargetRef.APIVersion,
EnvSourceContainerName: targetSoClone.Spec.ScaleTargetRef.EnvSourceContainerName,
},
PollingInterval: targetSo.Spec.PollingInterval,
CooldownPeriod: targetSo.Spec.CooldownPeriod,
MinReplicaCount: targetSo.Spec.MinReplicaCount,
MaxReplicaCount: targetSo.Spec.MaxReplicaCount,
Advanced: targetSo.Spec.Advanced,
Triggers: targetSo.Spec.Triggers,
Fallback: targetSo.Spec.Fallback,
IdleReplicaCount: targetSo.Spec.IdleReplicaCount,
PollingInterval: targetSoClone.Spec.PollingInterval,
CooldownPeriod: targetSoClone.Spec.CooldownPeriod,
MinReplicaCount: targetSoClone.Spec.MinReplicaCount,
MaxReplicaCount: targetSoClone.Spec.MaxReplicaCount,
Advanced: targetSoClone.Spec.Advanced,
Triggers: targetSoClone.Spec.Triggers,
Fallback: targetSoClone.Spec.Fallback,
IdleReplicaCount: targetSoClone.Spec.IdleReplicaCount,
}
primarySoName := fmt.Sprintf("%s-primary", cd.Spec.AutoscalerRef.Name)
primarySo, err := sor.flaggerClient.KedaV1alpha1().ScaledObjects(cd.Namespace).Get(context.TODO(), primarySoName, metav1.GetOptions{})
if errors.IsNotFound(err) {
primarySo = &keda.ScaledObject{
ObjectMeta: makeObjectMeta(primarySoName, primarySo.Labels, cd),
ObjectMeta: makeObjectMeta(primarySoName, targetSoClone.Labels, cd),
Spec: soSpec,
}
_, err = sor.flaggerClient.KedaV1alpha1().ScaledObjects(cd.Namespace).Create(context.TODO(), primarySo, metav1.CreateOptions{})
+128
View File
@@ -0,0 +1,128 @@
package canary
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
flaggerv1 "github.com/fluxcd/flagger/pkg/apis/flagger/v1beta1"
keda "github.com/fluxcd/flagger/pkg/apis/keda/v1alpha1"
)
func Test_reconcilePrimaryScaledObject(t *testing.T) {
mocks := newScalerReconcilerFixture(scalerConfig{
targetName: "podinfo",
scaler: "ScaledObject",
})
soReconciler := mocks.scalerReconciler.(*ScaledObjectReconciler)
so, err := mocks.flaggerClient.KedaV1alpha1().ScaledObjects("default").Get(context.TODO(), "podinfo", metav1.GetOptions{})
require.NoError(t, err)
err = soReconciler.reconcilePrimaryScaler(mocks.canary, true)
require.NoError(t, err)
primarySO, err := mocks.flaggerClient.KedaV1alpha1().ScaledObjects("default").Get(context.TODO(), "podinfo-primary", metav1.GetOptions{})
require.NoError(t, err)
assert.Equal(t, int(*primarySO.Spec.PollingInterval), 10)
assert.Equal(t, int(*primarySO.Spec.MinReplicaCount), 1)
assert.Equal(t, primarySO.Spec.Triggers[0].Metadata["query"], `sum(rate(http_requests_total{deployment="podinfo-primary"}[2m]))`)
so.Spec.PollingInterval = int32p(20)
so.Spec.Triggers[0].Metadata["query"] = `sum(rate(http_requests_total{deployment="podinfo-canary"}[10m]))`
_, err = mocks.flaggerClient.KedaV1alpha1().ScaledObjects("default").Update(context.TODO(), so, metav1.UpdateOptions{})
require.NoError(t, err)
err = soReconciler.reconcilePrimaryScaler(mocks.canary, false)
require.NoError(t, err)
primarySO, err = mocks.flaggerClient.KedaV1alpha1().ScaledObjects("default").Get(context.TODO(), "podinfo-primary", metav1.GetOptions{})
require.NoError(t, err)
assert.Equal(t, int(*primarySO.Spec.PollingInterval), 20)
assert.Equal(t, primarySO.Spec.Triggers[0].Metadata["query"], `sum(rate(http_requests_total{deployment="podinfo-primary"}[10m]))`)
}
func Test_pauseScaledObject(t *testing.T) {
mocks := newScalerReconcilerFixture(scalerConfig{
targetName: "podinfo",
scaler: "ScaledObject",
})
soReconciler := mocks.scalerReconciler.(*ScaledObjectReconciler)
err := soReconciler.PauseTargetScaler(mocks.canary)
require.NoError(t, err)
so, err := mocks.flaggerClient.KedaV1alpha1().ScaledObjects("default").Get(context.TODO(), "podinfo", metav1.GetOptions{})
require.NoError(t, err)
assert.Equal(t, so.Annotations[keda.PausedReplicasAnnotation], "0")
}
func Test_resumeScaledObject(t *testing.T) {
mocks := newScalerReconcilerFixture(scalerConfig{
targetName: "podinfo",
scaler: "ScaledObject",
})
soReconciler := mocks.scalerReconciler.(*ScaledObjectReconciler)
err := soReconciler.ResumeTargetScaler(mocks.canary)
require.NoError(t, err)
so, err := mocks.flaggerClient.KedaV1alpha1().ScaledObjects("default").Get(context.TODO(), "podinfo", metav1.GetOptions{})
require.NoError(t, err)
_, exists := so.Annotations[keda.PausedReplicasAnnotation]
assert.False(t, exists)
}
func Test_setPrimaryScaledObjectQuery(t *testing.T) {
cd := &flaggerv1.Canary{
Spec: flaggerv1.CanarySpec{
TargetRef: flaggerv1.LocalObjectReference{
Name: "podinfo",
},
AutoscalerRef: &flaggerv1.AutoscalerRefernce{
Name: "podinfo",
},
},
}
tests := []struct {
name string
query string
wantQuery string
}{
{
name: "query only has 'podinfo'",
query: `sum(rate(http_requests_total{deployment="podinfo"}[2m]))`,
wantQuery: `sum(rate(http_requests_total{deployment="podinfo-primary"}[2m]))`,
},
{
name: "query only has 'podinfo-canary'",
query: `sum(rate(http_requests_total{deployment="podinfo-canary"}[2m]))`,
wantQuery: `sum(rate(http_requests_total{deployment="podinfo-primary"}[2m]))`,
},
{
name: "query has both 'podinfo-canary' and 'podinfo'",
query: `sum(rate(http_requests_total{deployment="podinfo-canary", svc="podinfo"}[2m]))`,
wantQuery: `sum(rate(http_requests_total{deployment="podinfo-primary", svc="podinfo-primary"}[2m]))`,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
metadata := make(map[string]string)
metadata["query"] = test.query
setPrimaryScaledObjectQuery(cd, metadata)
assert.Equal(t, metadata["query"], test.wantQuery)
})
}
primaryQuery := `sum(rate(envoy_cluster_upstream_rq{ envoy_cluster_name="test_podinfo-primary_80" }[30s]))`
cd.Spec.AutoscalerRef.PrimaryScalerQuery = primaryQuery
metadata := make(map[string]string)
metadata["query"] = ""
setPrimaryScaledObjectQuery(cd, metadata)
assert.Equal(t, primaryQuery, metadata["query"])
}
+48 -4
View File
@@ -9,6 +9,8 @@ import (
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/fake"
keda "github.com/fluxcd/flagger/pkg/apis/keda/v1alpha1"
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"
@@ -31,7 +33,10 @@ type scalerConfig struct {
func newScalerReconcilerFixture(cfg scalerConfig) scalerReconcilerFixture {
canary := newDeploymentControllerTestCanary(canaryConfigs{targetName: cfg.targetName})
flaggerClient := fakeFlagger.NewSimpleClientset(canary)
flaggerClient := fakeFlagger.NewSimpleClientset(
canary,
newScaledObject(),
)
kubeClient := fake.NewSimpleClientset(
newScalerReconcilerTestHPAV2(),
@@ -55,10 +60,18 @@ func newScalerReconcilerFixture(cfg scalerConfig) scalerReconcilerFixture {
}
logger, _ := logger.NewLogger("debug")
var hpaReconciler HPAReconciler
var scalerReconciler ScalerReconciler
if cfg.scaler == "HorizontalPodAutoscaler" {
hpaReconciler = HPAReconciler{
scalerReconciler = &HPAReconciler{
kubeClient: kubeClient,
flaggerClient: flaggerClient,
logger: logger,
includeLabelPrefix: []string{"app.kubernetes.io"},
}
}
if cfg.scaler == "ScaledObject" {
scalerReconciler = &ScaledObjectReconciler{
kubeClient: kubeClient,
flaggerClient: flaggerClient,
logger: logger,
@@ -70,7 +83,7 @@ func newScalerReconcilerFixture(cfg scalerConfig) scalerReconcilerFixture {
canary: canary,
kubeClient: kubeClient,
flaggerClient: flaggerClient,
scalerReconciler: &hpaReconciler,
scalerReconciler: scalerReconciler,
logger: logger,
}
}
@@ -134,3 +147,34 @@ func newScalerReconcilerTestHPAV2() *hpav2.HorizontalPodAutoscaler {
return h
}
func newScaledObject() *keda.ScaledObject {
so := &keda.ScaledObject{
TypeMeta: metav1.TypeMeta{APIVersion: keda.SchemeGroupVersion.String()},
ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
Name: "podinfo",
},
Spec: keda.ScaledObjectSpec{
ScaleTargetRef: &keda.ScaleTarget{
Name: "podinfo",
},
PollingInterval: int32p(10),
MinReplicaCount: int32p(1),
MaxReplicaCount: int32p(4),
Triggers: []keda.ScaleTriggers{
{
Type: "prometheus",
Metadata: map[string]string{
"serverAddress": "http://flagger-prometheus.projectcontour:9090",
"metricName": "http_requests_total",
"query": `sum(rate(http_requests_total{deployment="podinfo-canary"}[2m]))`,
"threshold": "100",
},
},
},
},
}
return so
}
@@ -294,7 +294,7 @@ func newDeploymentTestCanary() *flaggerv1.Canary {
APIVersion: "apps/v1",
Kind: "Deployment",
},
AutoscalerRef: &flaggerv1.LocalObjectReference{
AutoscalerRef: &flaggerv1.AutoscalerRefernce{
Name: "podinfo",
APIVersion: "autoscaling/v2beta2",
Kind: "HorizontalPodAutoscaler",
@@ -356,7 +356,7 @@ func newDeploymentTestCanaryAB() *flaggerv1.Canary {
APIVersion: "apps/v1",
Kind: "Deployment",
},
AutoscalerRef: &flaggerv1.LocalObjectReference{
AutoscalerRef: &flaggerv1.AutoscalerRefernce{
Name: "podinfo",
APIVersion: "autoscaling/v2beta2",
Kind: "HorizontalPodAutoscaler",