mirror of
https://github.com/fluxcd/flagger.git
synced 2026-04-15 06:57:34 +00:00
add unit tests for ScaledObjectReconciler
Signed-off-by: Sanskar Jaiswal <sanskar.jaiswal@weave.works>
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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{})
|
||||
|
||||
@@ -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"])
|
||||
}
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user