From 6d72e81d5dcbacc6c7136a854ee54bace9f6d05a Mon Sep 17 00:00:00 2001 From: ycabrer <43866176+ycabrer@users.noreply.github.com> Date: Tue, 1 Dec 2020 08:38:25 -0700 Subject: [PATCH 01/10] add metricName uniqueness check Signed-off-by: ycabrer <43866176+ycabrer@users.noreply.github.com> --- controllers/scaledobject_controller.go | 28 ++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/controllers/scaledobject_controller.go b/controllers/scaledobject_controller.go index 1d18c42911f..d4fcb4ae839 100644 --- a/controllers/scaledobject_controller.go +++ b/controllers/scaledobject_controller.go @@ -190,6 +190,11 @@ func (r *ScaledObjectReconciler) reconcileScaledObject(logger logr.Logger, scale return "ScaledObject doesn't have correct scaleTargetRef specification", err } + err = r.validateMetricNameUniqueness(logger, scaledObject) + if err != nil { + return "Error checking metric name uniquenesss", err + } + // Create a new HPA or update existing one according to ScaledObject newHPACreated, err := r.ensureHPAForScaledObjectExists(logger, scaledObject, &gvkr) if err != nil { @@ -235,6 +240,29 @@ func (r *ScaledObjectReconciler) ensureScaledObjectLabel(logger logr.Logger, sca return r.Client.Update(context.TODO(), scaledObject) } +func (r *ScaledObjectReconciler) validateMetricNameUniqueness(logger logr.Logger, scaledObject *kedav1alpha1.ScaledObject) error { + scalers, err := r.scaleHandler.GetScalers(scaledObject) + if err != nil { + return err + } + + var observedMetricNames map[string]struct{} + for _, scaler := range scalers { + for _, metric := range scaler.GetMetricSpecForScaling() { + metricName := metric.External.Metric.Name + + if _, ok := observedMetricNames[metricName]; ok { + return fmt.Errorf("metricName %s defined mutliple times in ScaledObject %s, please refer the documentation how to define metircName manually", metricName, scaledObject.Name) + } + + observedMetricNames[metricName] = struct{}{} + } + } + + logger.V(1).Info("All metric names are unique in ScaledObject", "value", scaledObject.Name) + return nil +} + // checkTargetResourceIsScalable checks if resource targeted for scaling exists and exposes /scale subresource func (r *ScaledObjectReconciler) checkTargetResourceIsScalable(logger logr.Logger, scaledObject *kedav1alpha1.ScaledObject) (kedav1alpha1.GroupVersionKindResource, error) { gvkr, err := kedautil.ParseGVKR(r.restMapper, scaledObject.Spec.ScaleTargetRef.APIVersion, scaledObject.Spec.ScaleTargetRef.Kind) From 11b78c5da97bc896701a8ddff433c79464679666 Mon Sep 17 00:00:00 2001 From: ycabrer <43866176+ycabrer@users.noreply.github.com> Date: Tue, 1 Dec 2020 20:53:14 -0700 Subject: [PATCH 02/10] fix metricName uniqueness check nil error Signed-off-by: ycabrer <43866176+ycabrer@users.noreply.github.com> --- controllers/scaledobject_controller.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controllers/scaledobject_controller.go b/controllers/scaledobject_controller.go index d4fcb4ae839..0e655be8774 100644 --- a/controllers/scaledobject_controller.go +++ b/controllers/scaledobject_controller.go @@ -246,7 +246,7 @@ func (r *ScaledObjectReconciler) validateMetricNameUniqueness(logger logr.Logger return err } - var observedMetricNames map[string]struct{} + observedMetricNames := make(map[string]struct{}, 0) for _, scaler := range scalers { for _, metric := range scaler.GetMetricSpecForScaling() { metricName := metric.External.Metric.Name From 11bad0554f623abecea25536d888279bf912e32f Mon Sep 17 00:00:00 2001 From: ycabrer <43866176+ycabrer@users.noreply.github.com> Date: Tue, 1 Dec 2020 20:53:53 -0700 Subject: [PATCH 03/10] add metricName uniqueness check tests Signed-off-by: ycabrer <43866176+ycabrer@users.noreply.github.com> --- controllers/scaledobject_controller_test.go | 124 ++++++++++++++++++++ pkg/mock/mock_scaling/mock_interface.go | 77 ++++++++++++ 2 files changed, 201 insertions(+) create mode 100644 controllers/scaledobject_controller_test.go create mode 100644 pkg/mock/mock_scaling/mock_interface.go diff --git a/controllers/scaledobject_controller_test.go b/controllers/scaledobject_controller_test.go new file mode 100644 index 00000000000..513c4c8c878 --- /dev/null +++ b/controllers/scaledobject_controller_test.go @@ -0,0 +1,124 @@ +package controllers + +import ( + "fmt" + + "github.com/golang/mock/gomock" + kedav1alpha1 "github.com/kedacore/keda/v2/api/v1alpha1" + "github.com/kedacore/keda/v2/pkg/mock/mock_scaling" + "github.com/kedacore/keda/v2/pkg/scalers" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "sigs.k8s.io/controller-runtime/pkg/log/zap" +) + +type GinkgoTestReporter struct{} + +func (g GinkgoTestReporter) Errorf(format string, args ...interface{}) { + Fail(fmt.Sprintf(format, args...)) +} + +func (g GinkgoTestReporter) Fatalf(format string, args ...interface{}) { + Fail(fmt.Sprintf(format, args...)) +} + +var _ = Describe("ScaledObjectController", func() { + var ( + testLogger = zap.LoggerTo(GinkgoWriter, true) + ) + + Describe("Metric Names", func() { + var ( + metricNameTestReconciler ScaledObjectReconciler + mockScaleHandler *mock_scaling.MockScaleHandler + ) + + var triggerMeta []map[string]string = []map[string]string{ + {"serverAddress": "http://localhost:9090", "metricName": "http_requests_total", "threshold": "100", "query": "up", "disableScaleToZero": "true"}, + {"serverAddress": "http://localhost:9090", "metricName": "http_requests_total2", "threshold": "100", "query": "up"}, + } + + BeforeEach(func() { + mockScaleHandler = mock_scaling.NewMockScaleHandler(gomock.NewController(GinkgoTestReporter{})) + + metricNameTestReconciler = ScaledObjectReconciler{ + scaleHandler: mockScaleHandler, + } + }) + + Context("With Unique Values", func() { + var uniqueNamedScaledObjectTrigger = &kedav1alpha1.ScaledObject{} + + It("should pass metric name validation", func() { + testScalers := make([]scalers.Scaler, 0) + for i, tm := range triggerMeta { + config := &scalers.ScalerConfig{ + Name: fmt.Sprintf("test.%d", i), + Namespace: "test", + TriggerMetadata: tm, + ResolvedEnv: nil, + AuthParams: nil, + } + + s, err := scalers.NewPrometheusScaler(config) + if err != nil { + Fail(err.Error()) + } + + testScalers = append(testScalers, s) + } + + mockScaleHandler.EXPECT().GetScalers(uniqueNamedScaledObjectTrigger).Return(testScalers, nil) + + Ω(metricNameTestReconciler.validateMetricNameUniqueness(testLogger, uniqueNamedScaledObjectTrigger)).Should(BeNil()) + }) + + It("should pass metric name validation with single value", func() { + config := &scalers.ScalerConfig{ + Name: "test", + Namespace: "test", + TriggerMetadata: triggerMeta[0], + ResolvedEnv: nil, + AuthParams: nil, + } + + s, err := scalers.NewPrometheusScaler(config) + if err != nil { + Fail(err.Error()) + } + + mockScaleHandler.EXPECT().GetScalers(uniqueNamedScaledObjectTrigger).Return([]scalers.Scaler{s}, nil) + + Ω(metricNameTestReconciler.validateMetricNameUniqueness(testLogger, uniqueNamedScaledObjectTrigger)).Should(BeNil()) + }) + }) + + Context("With Duplicate Values", func() { + var duplicateNamedScaledObjectTrigger = &kedav1alpha1.ScaledObject{} + + It("should pass metric name validation", func() { + testScalers := make([]scalers.Scaler, 0) + for i := 0; i < 4; i++ { + config := &scalers.ScalerConfig{ + Name: fmt.Sprintf("test.%d", i), + Namespace: "test", + TriggerMetadata: triggerMeta[0], + ResolvedEnv: nil, + AuthParams: nil, + } + + s, err := scalers.NewPrometheusScaler(config) + if err != nil { + Fail(err.Error()) + } + + testScalers = append(testScalers, s) + } + + mockScaleHandler.EXPECT().GetScalers(duplicateNamedScaledObjectTrigger).Return(testScalers, nil) + + Ω(metricNameTestReconciler.validateMetricNameUniqueness(testLogger, duplicateNamedScaledObjectTrigger)).ShouldNot(BeNil()) + }) + }) + }) +}) diff --git a/pkg/mock/mock_scaling/mock_interface.go b/pkg/mock/mock_scaling/mock_interface.go new file mode 100644 index 00000000000..1c3037e1b09 --- /dev/null +++ b/pkg/mock/mock_scaling/mock_interface.go @@ -0,0 +1,77 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: scale_handler.go + +// Package mock_scaling is a generated GoMock package. +package mock_scaling + +import ( + gomock "github.com/golang/mock/gomock" + scalers "github.com/kedacore/keda/v2/pkg/scalers" + reflect "reflect" +) + +// MockScaleHandler is a mock of ScaleHandler interface +type MockScaleHandler struct { + ctrl *gomock.Controller + recorder *MockScaleHandlerMockRecorder +} + +// MockScaleHandlerMockRecorder is the mock recorder for MockScaleHandler +type MockScaleHandlerMockRecorder struct { + mock *MockScaleHandler +} + +// NewMockScaleHandler creates a new mock instance +func NewMockScaleHandler(ctrl *gomock.Controller) *MockScaleHandler { + mock := &MockScaleHandler{ctrl: ctrl} + mock.recorder = &MockScaleHandlerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockScaleHandler) EXPECT() *MockScaleHandlerMockRecorder { + return m.recorder +} + +// HandleScalableObject mocks base method +func (m *MockScaleHandler) HandleScalableObject(scalableObject interface{}) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HandleScalableObject", scalableObject) + ret0, _ := ret[0].(error) + return ret0 +} + +// HandleScalableObject indicates an expected call of HandleScalableObject +func (mr *MockScaleHandlerMockRecorder) HandleScalableObject(scalableObject interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleScalableObject", reflect.TypeOf((*MockScaleHandler)(nil).HandleScalableObject), scalableObject) +} + +// DeleteScalableObject mocks base method +func (m *MockScaleHandler) DeleteScalableObject(scalableObject interface{}) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteScalableObject", scalableObject) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteScalableObject indicates an expected call of DeleteScalableObject +func (mr *MockScaleHandlerMockRecorder) DeleteScalableObject(scalableObject interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteScalableObject", reflect.TypeOf((*MockScaleHandler)(nil).DeleteScalableObject), scalableObject) +} + +// GetScalers mocks base method +func (m *MockScaleHandler) GetScalers(scalableObject interface{}) ([]scalers.Scaler, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetScalers", scalableObject) + ret0, _ := ret[0].([]scalers.Scaler) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetScalers indicates an expected call of GetScalers +func (mr *MockScaleHandlerMockRecorder) GetScalers(scalableObject interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetScalers", reflect.TypeOf((*MockScaleHandler)(nil).GetScalers), scalableObject) +} From 5819429677cd2cda9b8c3e0b655889599cdb794b Mon Sep 17 00:00:00 2001 From: ycabrer <43866176+ycabrer@users.noreply.github.com> Date: Tue, 1 Dec 2020 20:55:52 -0700 Subject: [PATCH 04/10] update changelog for metricName uniqueness Signed-off-by: ycabrer <43866176+ycabrer@users.noreply.github.com> --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7cdf500d1de..6e6a2d86f57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ - Improve error reporting in prometheus scaler ([PR #1497](https://github.com/kedacore/keda/pull/1497)) ### Breaking Changes +- Require metricNames be unique in scaled objects. ### Other - Bump go module version to v2 ([#1324](https://github.com/kedacore/keda/pull/1324)) From 7a374943383bd85491e02bbde054cb40a23b30bd Mon Sep 17 00:00:00 2001 From: ycabrer <43866176+ycabrer@users.noreply.github.com> Date: Tue, 1 Dec 2020 21:04:19 -0700 Subject: [PATCH 05/10] fix static checks Signed-off-by: ycabrer <43866176+ycabrer@users.noreply.github.com> --- controllers/scaledobject_controller.go | 4 +- pkg/mock/mock_scaling/mock_interface.go | 58 ++++++++++++------------- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/controllers/scaledobject_controller.go b/controllers/scaledobject_controller.go index 0e655be8774..5cf4788b22b 100644 --- a/controllers/scaledobject_controller.go +++ b/controllers/scaledobject_controller.go @@ -246,13 +246,13 @@ func (r *ScaledObjectReconciler) validateMetricNameUniqueness(logger logr.Logger return err } - observedMetricNames := make(map[string]struct{}, 0) + observedMetricNames := make(map[string]struct{}) for _, scaler := range scalers { for _, metric := range scaler.GetMetricSpecForScaling() { metricName := metric.External.Metric.Name if _, ok := observedMetricNames[metricName]; ok { - return fmt.Errorf("metricName %s defined mutliple times in ScaledObject %s, please refer the documentation how to define metircName manually", metricName, scaledObject.Name) + return fmt.Errorf("metricName %s defined multiple times in ScaledObject %s, please refer the documentation how to define metircName manually", metricName, scaledObject.Name) } observedMetricNames[metricName] = struct{}{} diff --git a/pkg/mock/mock_scaling/mock_interface.go b/pkg/mock/mock_scaling/mock_interface.go index 1c3037e1b09..45540b5307d 100644 --- a/pkg/mock/mock_scaling/mock_interface.go +++ b/pkg/mock/mock_scaling/mock_interface.go @@ -5,73 +5,73 @@ package mock_scaling import ( - gomock "github.com/golang/mock/gomock" - scalers "github.com/kedacore/keda/v2/pkg/scalers" - reflect "reflect" + gomock "github.com/golang/mock/gomock" + scalers "github.com/kedacore/keda/v2/pkg/scalers" + reflect "reflect" ) // MockScaleHandler is a mock of ScaleHandler interface type MockScaleHandler struct { - ctrl *gomock.Controller - recorder *MockScaleHandlerMockRecorder + ctrl *gomock.Controller + recorder *MockScaleHandlerMockRecorder } // MockScaleHandlerMockRecorder is the mock recorder for MockScaleHandler type MockScaleHandlerMockRecorder struct { - mock *MockScaleHandler + mock *MockScaleHandler } // NewMockScaleHandler creates a new mock instance func NewMockScaleHandler(ctrl *gomock.Controller) *MockScaleHandler { - mock := &MockScaleHandler{ctrl: ctrl} - mock.recorder = &MockScaleHandlerMockRecorder{mock} - return mock + mock := &MockScaleHandler{ctrl: ctrl} + mock.recorder = &MockScaleHandlerMockRecorder{mock} + return mock } // EXPECT returns an object that allows the caller to indicate expected use func (m *MockScaleHandler) EXPECT() *MockScaleHandlerMockRecorder { - return m.recorder + return m.recorder } // HandleScalableObject mocks base method func (m *MockScaleHandler) HandleScalableObject(scalableObject interface{}) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "HandleScalableObject", scalableObject) - ret0, _ := ret[0].(error) - return ret0 + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HandleScalableObject", scalableObject) + ret0, _ := ret[0].(error) + return ret0 } // HandleScalableObject indicates an expected call of HandleScalableObject func (mr *MockScaleHandlerMockRecorder) HandleScalableObject(scalableObject interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleScalableObject", reflect.TypeOf((*MockScaleHandler)(nil).HandleScalableObject), scalableObject) + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleScalableObject", reflect.TypeOf((*MockScaleHandler)(nil).HandleScalableObject), scalableObject) } // DeleteScalableObject mocks base method func (m *MockScaleHandler) DeleteScalableObject(scalableObject interface{}) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteScalableObject", scalableObject) - ret0, _ := ret[0].(error) - return ret0 + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteScalableObject", scalableObject) + ret0, _ := ret[0].(error) + return ret0 } // DeleteScalableObject indicates an expected call of DeleteScalableObject func (mr *MockScaleHandlerMockRecorder) DeleteScalableObject(scalableObject interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteScalableObject", reflect.TypeOf((*MockScaleHandler)(nil).DeleteScalableObject), scalableObject) + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteScalableObject", reflect.TypeOf((*MockScaleHandler)(nil).DeleteScalableObject), scalableObject) } // GetScalers mocks base method func (m *MockScaleHandler) GetScalers(scalableObject interface{}) ([]scalers.Scaler, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetScalers", scalableObject) - ret0, _ := ret[0].([]scalers.Scaler) - ret1, _ := ret[1].(error) - return ret0, ret1 + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetScalers", scalableObject) + ret0, _ := ret[0].([]scalers.Scaler) + ret1, _ := ret[1].(error) + return ret0, ret1 } // GetScalers indicates an expected call of GetScalers func (mr *MockScaleHandlerMockRecorder) GetScalers(scalableObject interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetScalers", reflect.TypeOf((*MockScaleHandler)(nil).GetScalers), scalableObject) + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetScalers", reflect.TypeOf((*MockScaleHandler)(nil).GetScalers), scalableObject) } From bdba4609686e3ab952ace6d97506a41eb904a5c7 Mon Sep 17 00:00:00 2001 From: ycabrer <43866176+ycabrer@users.noreply.github.com> Date: Tue, 1 Dec 2020 21:06:57 -0700 Subject: [PATCH 06/10] fix spelling Signed-off-by: ycabrer <43866176+ycabrer@users.noreply.github.com> --- controllers/scaledobject_controller.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controllers/scaledobject_controller.go b/controllers/scaledobject_controller.go index 5cf4788b22b..446095fe366 100644 --- a/controllers/scaledobject_controller.go +++ b/controllers/scaledobject_controller.go @@ -192,7 +192,7 @@ func (r *ScaledObjectReconciler) reconcileScaledObject(logger logr.Logger, scale err = r.validateMetricNameUniqueness(logger, scaledObject) if err != nil { - return "Error checking metric name uniquenesss", err + return "Error checking metric name uniqueness", err } // Create a new HPA or update existing one according to ScaledObject From 5e5ddbb436dc2801a65c023b9596a6e6e4130b34 Mon Sep 17 00:00:00 2001 From: ycabrer <43866176+ycabrer@users.noreply.github.com> Date: Mon, 7 Dec 2020 09:35:14 -0700 Subject: [PATCH 07/10] Only validate external metric names Signed-off-by: ycabrer <43866176+ycabrer@users.noreply.github.com> --- controllers/scaledobject_controller.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/controllers/scaledobject_controller.go b/controllers/scaledobject_controller.go index 446095fe366..0414fd9a3f5 100644 --- a/controllers/scaledobject_controller.go +++ b/controllers/scaledobject_controller.go @@ -243,14 +243,19 @@ func (r *ScaledObjectReconciler) ensureScaledObjectLabel(logger logr.Logger, sca func (r *ScaledObjectReconciler) validateMetricNameUniqueness(logger logr.Logger, scaledObject *kedav1alpha1.ScaledObject) error { scalers, err := r.scaleHandler.GetScalers(scaledObject) if err != nil { + logger.Error(err, "Unable to fetch scalers in metric name uniqueness check") return err } observedMetricNames := make(map[string]struct{}) for _, scaler := range scalers { for _, metric := range scaler.GetMetricSpecForScaling() { - metricName := metric.External.Metric.Name + // Only validate external metricNames + if metric.External == nil { + continue + } + metricName := metric.External.Metric.Name if _, ok := observedMetricNames[metricName]; ok { return fmt.Errorf("metricName %s defined multiple times in ScaledObject %s, please refer the documentation how to define metircName manually", metricName, scaledObject.Name) } From 3ef1aef30a93e0c8583523fad12360a9b01195e3 Mon Sep 17 00:00:00 2001 From: ycabrer <43866176+ycabrer@users.noreply.github.com> Date: Thu, 14 Jan 2021 13:20:29 -0700 Subject: [PATCH 08/10] Ignore interfacer linter in golang ci (_finalizer.go) Signed-off-by: ycabrer <43866176+ycabrer@users.noreply.github.com> --- .golangci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.golangci.yml b/.golangci.yml index d8be386aed3..b732f8fb6aa 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -56,6 +56,9 @@ issues: - path: scale_handler.go linters: - gocyclo + - path: _finalizer.go + linters: + - interfacer # https://github.com/go-critic/go-critic/issues/926 - linters: From fa955d1f13e4469f5094ec5f514e1484fbe1b5c5 Mon Sep 17 00:00:00 2001 From: ycabrer <43866176+ycabrer@users.noreply.github.com> Date: Tue, 19 Jan 2021 00:18:52 -0700 Subject: [PATCH 09/10] Ignore interfacer linter comments Signed-off-by: ycabrer <43866176+ycabrer@users.noreply.github.com> --- .golangci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.golangci.yml b/.golangci.yml index b732f8fb6aa..1f4e8a4228d 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -56,6 +56,7 @@ issues: - path: scale_handler.go linters: - gocyclo + # Excluding interfacer for finalizers, reason: https://github.com/kedacore/keda/pull/1390 - path: _finalizer.go linters: - interfacer From fa7aba76e21789f19d4881e9d0a677b4184dec1f Mon Sep 17 00:00:00 2001 From: ycabrer <43866176+ycabrer@users.noreply.github.com> Date: Tue, 19 Jan 2021 00:25:48 -0700 Subject: [PATCH 10/10] Update Changelog Signed-off-by: ycabrer <43866176+ycabrer@users.noreply.github.com> --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e6a2d86f57..759a5bfac09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,7 +40,7 @@ - Improve error reporting in prometheus scaler ([PR #1497](https://github.com/kedacore/keda/pull/1497)) ### Breaking Changes -- Require metricNames be unique in scaled objects. +- Require metricNames be unique in scaled objects ([#1390](https://github.com/kedacore/keda/pull/1390)) ### Other - Bump go module version to v2 ([#1324](https://github.com/kedacore/keda/pull/1324))