diff --git a/apis/keda/v1alpha1/withtriggers_types.go b/apis/keda/v1alpha1/withtriggers_types.go index 7dcc410ea90..00b9821cff7 100644 --- a/apis/keda/v1alpha1/withtriggers_types.go +++ b/apis/keda/v1alpha1/withtriggers_types.go @@ -18,6 +18,7 @@ package v1alpha1 import ( "fmt" + "strings" "time" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -90,5 +91,5 @@ func (t *WithTriggers) GetPollingInterval() time.Duration { // GenerateIdenitifier returns identifier for the object in for "kind.namespace.name" func (t *WithTriggers) GenerateIdenitifier() string { - return fmt.Sprintf("%s.%s.%s", t.Kind, t.Namespace, t.Name) + return strings.ToLower(fmt.Sprintf("%s.%s.%s", t.Kind, t.Namespace, t.Name)) } diff --git a/controllers/keda/scaledobject_controller_test.go b/controllers/keda/scaledobject_controller_test.go index a8052bcbe4d..bf40980a5d3 100644 --- a/controllers/keda/scaledobject_controller_test.go +++ b/controllers/keda/scaledobject_controller_test.go @@ -292,6 +292,81 @@ var _ = Describe("ScaledObjectController", func() { Expect(hpa.Spec.Metrics[0].External.Metric.Name).To(Equal("s0-cron-UTC-0xxxx-1xxxx")) }) + //https://github.com/kedacore/keda/issues/2407 + It("cache is correctly recreated if SO is deleted and created", func() { + // Create the scaling target. + err := k8sClient.Create(context.Background(), generateDeployment("cache-regenerate")) + Expect(err).ToNot(HaveOccurred()) + + // Create the ScaledObject with one trigger. + so := &kedav1alpha1.ScaledObject{ + ObjectMeta: metav1.ObjectMeta{Name: "cache-regenerate", Namespace: "default"}, + Spec: kedav1alpha1.ScaledObjectSpec{ + ScaleTargetRef: &kedav1alpha1.ScaleTarget{ + Name: "cache-regenerate", + }, + Triggers: []kedav1alpha1.ScaleTriggers{ + { + Type: "cron", + Metadata: map[string]string{ + "timezone": "UTC", + "start": "0 * * * *", + "end": "1 * * * *", + "desiredReplicas": "1", + }, + }, + }, + }, + } + err = k8sClient.Create(context.Background(), so) + Expect(err).ToNot(HaveOccurred()) + + // Get and confirm the HPA. + hpa := &autoscalingv2beta2.HorizontalPodAutoscaler{} + Eventually(func() error { + return k8sClient.Get(context.Background(), types.NamespacedName{Name: "keda-hpa-cache-regenerate", Namespace: "default"}, hpa) + }).ShouldNot(HaveOccurred()) + Expect(hpa.Spec.Metrics).To(HaveLen(1)) + Expect(hpa.Spec.Metrics[0].External.Metric.Name).To(Equal("s0-cron-UTC-0xxxx-1xxxx")) + + // Delete the ScaledObject + err = k8sClient.Delete(context.Background(), so) + Expect(err).ToNot(HaveOccurred()) + time.Sleep(30 * time.Second) + + // Create the same ScaledObject with a change in the trigger. + so = &kedav1alpha1.ScaledObject{ + ObjectMeta: metav1.ObjectMeta{Name: "cache-regenerate", Namespace: "default"}, + Spec: kedav1alpha1.ScaledObjectSpec{ + ScaleTargetRef: &kedav1alpha1.ScaleTarget{ + Name: "cache-regenerate", + }, + Triggers: []kedav1alpha1.ScaleTriggers{ + { + Type: "cron", + Metadata: map[string]string{ + "timezone": "CET", + "start": "0 * * * *", + "end": "1 * * * *", + "desiredReplicas": "1", + }, + }, + }, + }, + } + err = k8sClient.Create(context.Background(), so) + Expect(err).ToNot(HaveOccurred()) + time.Sleep(30 * time.Second) + + // Get and confirm the HPA. + hpa2 := &autoscalingv2beta2.HorizontalPodAutoscaler{} + Eventually(func() error { + return k8sClient.Get(context.Background(), types.NamespacedName{Name: "keda-hpa-cache-regenerate", Namespace: "default"}, hpa2) + }).ShouldNot(HaveOccurred()) + Expect(hpa2.Spec.Metrics).To(HaveLen(1)) + Expect(hpa2.Spec.Metrics[0].External.Metric.Name).To(Equal("s0-cron-CET-0xxxx-1xxxx")) + }) + It("deploys ScaledObject and creates HPA, when IdleReplicaCount, MinReplicaCount and MaxReplicaCount is defined", func() { deploymentName := "idleminmax" diff --git a/go.mod b/go.mod index 55db1130c72..6848d676b42 100644 --- a/go.mod +++ b/go.mod @@ -120,6 +120,7 @@ require ( github.com/go-playground/locales v0.14.0 // indirect github.com/go-playground/universal-translator v0.18.0 // indirect github.com/go-stack/stack v1.8.0 // indirect + github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect diff --git a/go.sum b/go.sum index 8f4308fcbf6..9a3e3d53dd5 100644 --- a/go.sum +++ b/go.sum @@ -374,6 +374,7 @@ github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfC github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw= github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= diff --git a/pkg/mock/mock_client/mock_interfaces.go b/pkg/mock/mock_client/mock_interfaces.go index b33495dcdf2..0dae7ff258a 100644 --- a/pkg/mock/mock_client/mock_interfaces.go +++ b/pkg/mock/mock_client/mock_interfaces.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: /go/pkg/mod/sigs.k8s.io/controller-runtime@v0.10.2/pkg/client/interfaces.go +// Source: /go/pkg/mod/sigs.k8s.io/controller-runtime@v0.10.3/pkg/client/interfaces.go // Package mock_client is a generated GoMock package. package mock_client diff --git a/pkg/mock/mock_scale/mock_interfaces.go b/pkg/mock/mock_scale/mock_interfaces.go index 48c6afb71bd..f889afc77b6 100644 --- a/pkg/mock/mock_scale/mock_interfaces.go +++ b/pkg/mock/mock_scale/mock_interfaces.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: /home/ahmed/go/pkg/mod/k8s.io/client-go@v0.22.2/scale/interfaces.go +// Source: /go/pkg/mod/k8s.io/client-go@v0.22.4/scale/interfaces.go // Package mock_scale is a generated GoMock package. package mock_scale diff --git a/pkg/scaling/scale_handler.go b/pkg/scaling/scale_handler.go index 07e1a4ba6de..ad20c01aa35 100644 --- a/pkg/scaling/scale_handler.go +++ b/pkg/scaling/scale_handler.go @@ -126,6 +126,7 @@ func (h *scaleHandler) DeleteScalableObject(ctx context.Context, scalableObject cancel() } h.scaleLoopContexts.Delete(key) + delete(h.scalerCaches, key) h.recorder.Event(withTriggers, corev1.EventTypeNormal, eventreason.KEDAScalersStopped, "Stopped scalers watch") } else { h.logger.V(1).Info("ScaleObject was not found in controller cache", "key", key) @@ -163,7 +164,7 @@ func (h *scaleHandler) GetScalersCache(ctx context.Context, scalableObject inter return nil, err } - key := strings.ToLower(fmt.Sprintf("%s.%s.%s", withTriggers.Kind, withTriggers.Name, withTriggers.Namespace)) + key := withTriggers.GenerateIdenitifier() h.lock.RLock() if cache, ok := h.scalerCaches[key]; ok && cache.Generation == withTriggers.Generation {