From ee110288355536117792cc910d6691e3db157b0c Mon Sep 17 00:00:00 2001 From: rickbrouwer Date: Mon, 11 Nov 2024 16:38:13 +0100 Subject: [PATCH] Add KEDAScalersInfo to display important information Signed-off-by: rickbrouwer --- CHANGELOG.md | 1 + pkg/eventreason/eventreason.go | 3 ++ pkg/scalers/cpu_memory_scaler.go | 21 +++++++-- pkg/scalers/cpu_memory_scaler_test.go | 3 +- pkg/scalers/ibmmq_scaler.go | 17 +++++-- pkg/scalers/scaler.go | 5 ++ pkg/scalers/scaler_test.go | 68 +++++++++++++++++++++++++++ pkg/scaling/scalers_builder.go | 7 +++ 8 files changed, 115 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de0b5ebec20..46f0955b485 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -89,6 +89,7 @@ New deprecation(s): ### Other - **General**: Bump newrelic-client-go deps to 2.51.2 (latest) ([#6325](https://github.com/kedacore/keda/pull/6325)) +- **General**: New eventreason KEDAScalersInfo to display important information ([#6328](https://github.com/kedacore/keda/pull/6328)) ## v2.16.0 diff --git a/pkg/eventreason/eventreason.go b/pkg/eventreason/eventreason.go index dd41cb6639a..45b162fb667 100644 --- a/pkg/eventreason/eventreason.go +++ b/pkg/eventreason/eventreason.go @@ -41,6 +41,9 @@ const ( // ScaledJobDeleted is for event when ScaledJob is deleted ScaledJobDeleted = "ScaledJobDeleted" + // KEDAScalersInfo is for event when Scaler has additional info + KEDAScalersInfo = "KEDAScalerInfo" + // KEDAScalersStarted is for event when scalers watch started for ScaledObject or ScaledJob KEDAScalersStarted = "KEDAScalersStarted" diff --git a/pkg/scalers/cpu_memory_scaler.go b/pkg/scalers/cpu_memory_scaler.go index 8da440ab77e..233c7dcf563 100644 --- a/pkg/scalers/cpu_memory_scaler.go +++ b/pkg/scalers/cpu_memory_scaler.go @@ -18,6 +18,7 @@ type cpuMemoryScaler struct { metadata cpuMemoryMetadata resourceName v1.ResourceName logger logr.Logger + info string } type cpuMemoryMetadata struct { @@ -33,19 +34,26 @@ type cpuMemoryMetadata struct { func NewCPUMemoryScaler(resourceName v1.ResourceName, config *scalersconfig.ScalerConfig) (Scaler, error) { logger := InitializeLogger(config, "cpu_memory_scaler") - meta, err := parseResourceMetadata(config, logger) + meta, err := parseResourceMetadata(config) if err != nil { return nil, fmt.Errorf("error parsing %s metadata: %w", resourceName, err) } - return &cpuMemoryScaler{ + scaler := &cpuMemoryScaler{ metadata: meta, resourceName: resourceName, logger: logger, - }, nil + } + + // This is deprecated and can be removed later + if meta.Type != "" { + scaler.info = "The 'type' setting is DEPRECATED and will be removed in v2.18 - Use 'metricType' instead." + } + + return scaler, nil } -func parseResourceMetadata(config *scalersconfig.ScalerConfig, logger logr.Logger) (cpuMemoryMetadata, error) { +func parseResourceMetadata(config *scalersconfig.ScalerConfig) (cpuMemoryMetadata, error) { meta := cpuMemoryMetadata{} err := config.TypedConfig(&meta) if err != nil { @@ -58,7 +66,6 @@ func parseResourceMetadata(config *scalersconfig.ScalerConfig, logger logr.Logge // This is deprecated and can be removed later if meta.Type != "" { - logger.Info("The 'type' setting is DEPRECATED and will be removed in v2.18 - Use 'metricType' instead.") switch meta.Type { case "AverageValue": meta.MetricType = v2.AverageValueMetricType @@ -100,6 +107,10 @@ func (s *cpuMemoryScaler) Close(context.Context) error { return nil } +func (s *cpuMemoryScaler) GetScalerInfo() string { + return s.info +} + // GetMetricSpecForScaling returns the metric spec for the HPA func (s *cpuMemoryScaler) GetMetricSpecForScaling(context.Context) []v2.MetricSpec { metricType := s.metadata.MetricType diff --git a/pkg/scalers/cpu_memory_scaler_test.go b/pkg/scalers/cpu_memory_scaler_test.go index 78f662de247..e9b896bee1a 100644 --- a/pkg/scalers/cpu_memory_scaler_test.go +++ b/pkg/scalers/cpu_memory_scaler_test.go @@ -43,13 +43,12 @@ var testCPUMemoryMetadata = []parseCPUMemoryMetadataTestData{ } func TestCPUMemoryParseMetadata(t *testing.T) { - logger := logr.Discard() for i, testData := range testCPUMemoryMetadata { config := &scalersconfig.ScalerConfig{ TriggerMetadata: testData.metadata, MetricType: testData.metricType, } - _, err := parseResourceMetadata(config, logger) + _, err := parseResourceMetadata(config) if err != nil && !testData.isError { t.Errorf("Test case %d: Expected success but got error: %v", i, err) } diff --git a/pkg/scalers/ibmmq_scaler.go b/pkg/scalers/ibmmq_scaler.go index f2d95099241..d1aba8564c9 100644 --- a/pkg/scalers/ibmmq_scaler.go +++ b/pkg/scalers/ibmmq_scaler.go @@ -23,6 +23,7 @@ type ibmmqScaler struct { metadata ibmmqMetadata httpClient *http.Client logger logr.Logger + info string } type ibmmqMetadata struct { @@ -92,7 +93,6 @@ func NewIBMMQScaler(config *scalersconfig.ScalerConfig) (Scaler, error) { // TODO: DEPRECATED to be removed in v2.18 if meta.TLS { - logger.Info("The 'tls' setting is DEPRECATED and will be removed in v2.18 - Use 'unsafeSsl' instead") meta.UnsafeSsl = meta.TLS } @@ -106,12 +106,19 @@ func NewIBMMQScaler(config *scalersconfig.ScalerConfig) (Scaler, error) { httpClient.Transport = kedautil.CreateHTTPTransportWithTLSConfig(tlsConfig) } - return &ibmmqScaler{ + scaler := &ibmmqScaler{ metricType: metricType, metadata: meta, httpClient: httpClient, logger: logger, - }, nil + } + + // TODO: DEPRECATED to be removed in v2.18 + if meta.TLS { + scaler.info = "The 'tls' setting is DEPRECATED and will be removed in v2.18 - Use 'unsafeSsl' instead" + } + + return scaler, nil } func (s *ibmmqScaler) Close(context.Context) error { @@ -121,6 +128,10 @@ func (s *ibmmqScaler) Close(context.Context) error { return nil } +func (s *ibmmqScaler) GetScalerInfo() string { + return s.info +} + func parseIBMMQMetadata(config *scalersconfig.ScalerConfig) (ibmmqMetadata, error) { meta := ibmmqMetadata{triggerIndex: config.TriggerIndex} if err := config.TypedConfig(&meta); err != nil { diff --git a/pkg/scalers/scaler.go b/pkg/scalers/scaler.go index df37eb210e0..f590e38aad1 100644 --- a/pkg/scalers/scaler.go +++ b/pkg/scalers/scaler.go @@ -54,6 +54,11 @@ type Scaler interface { Close(ctx context.Context) error } +// InfoProvider is an optional interface that scalers can implement to provide additional information +type InfoProvider interface { + GetScalerInfo() string +} + // PushScaler interface type PushScaler interface { Scaler diff --git a/pkg/scalers/scaler_test.go b/pkg/scalers/scaler_test.go index 59508b9ba58..3b4cc5631dd 100644 --- a/pkg/scalers/scaler_test.go +++ b/pkg/scalers/scaler_test.go @@ -1,16 +1,46 @@ package scalers import ( + "context" "reflect" "testing" "github.com/stretchr/testify/assert" v2 "k8s.io/api/autoscaling/v2" "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/metrics/pkg/apis/external_metrics" scalersconfig "github.com/kedacore/keda/v2/pkg/scalers/scalersconfig" ) +type mockScaler struct { + info string +} + +func (s *mockScaler) GetMetricSpecForScaling(context.Context) []v2.MetricSpec { + return nil +} + +func (s *mockScaler) GetMetrics(context.Context, string) ([]external_metrics.ExternalMetricValue, error) { + return nil, nil +} + +func (s *mockScaler) IsActive(context.Context) (bool, error) { + return false, nil +} + +func (s *mockScaler) Close(context.Context) error { + return nil +} + +func (s *mockScaler) GetMetricsAndActivity(context.Context, string) ([]external_metrics.ExternalMetricValue, bool, error) { + return nil, false, nil +} + +func (s *mockScaler) GetScalerInfo() string { + return s.info +} + func TestGetMetricTargetType(t *testing.T) { cases := []struct { name string @@ -473,3 +503,41 @@ func TestConvertStringToType(t *testing.T) { } } } + +func TestScalerInfo(t *testing.T) { + cases := []struct { + name string + scaler Scaler + expectedInfo string + expectingInfo bool + }{ + { + name: "scaler with info", + scaler: &mockScaler{info: "test info message"}, + expectedInfo: "test info message", + expectingInfo: true, + }, + { + name: "scaler with empty info", + scaler: &mockScaler{info: ""}, + expectedInfo: "", + expectingInfo: false, + }, + } + + for _, testCase := range cases { + tc := testCase + t.Run(tc.name, func(t *testing.T) { + if infoProvider, ok := tc.scaler.(InfoProvider); ok { + info := infoProvider.GetScalerInfo() + if tc.expectingInfo { + assert.Equal(t, tc.expectedInfo, info) + } else { + assert.Empty(t, info) + } + } else { + t.Error("Scaler does not implement InfoProvider interface") + } + }) + } +} diff --git a/pkg/scaling/scalers_builder.go b/pkg/scaling/scalers_builder.go index 80bfb40658f..bfd6236e6f8 100644 --- a/pkg/scaling/scalers_builder.go +++ b/pkg/scaling/scalers_builder.go @@ -102,6 +102,13 @@ func (h *scaleHandler) buildScalers(ctx context.Context, withTriggers *kedav1alp msg := fmt.Sprintf(message.ScalerIsBuiltMsg, trigger.Type) h.recorder.Event(withTriggers, corev1.EventTypeNormal, eventreason.KEDAScalersStarted, msg) + if infoProvider, ok := scaler.(scalers.InfoProvider); ok { + if info := infoProvider.GetScalerInfo(); info != "" { + infoMsg := fmt.Sprintf("Scaler %s additional info: %s", trigger.Type, info) + h.recorder.Event(withTriggers, corev1.EventTypeNormal, eventreason.KEDAScalersInfo, infoMsg) + } + } + result = append(result, cache.ScalerBuilder{ Scaler: scaler, ScalerConfig: *config,