From 50a6065dd6c949471d7854b1c78ca055763404c6 Mon Sep 17 00:00:00 2001 From: Indresh2410 Date: Sun, 7 Jul 2024 11:23:45 +0000 Subject: [PATCH 1/8] Add support to fetch username from env for MYSQL Scaler Signed-off-by: Indresh2410 --- pkg/scalers/mysql_scaler.go | 2 +- pkg/scalers/mysql_scaler_test.go | 15 +++++++++++++++ pkg/scalers/scaler.go | 17 +++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/pkg/scalers/mysql_scaler.go b/pkg/scalers/mysql_scaler.go index 976295d0268..1ff17c34813 100644 --- a/pkg/scalers/mysql_scaler.go +++ b/pkg/scalers/mysql_scaler.go @@ -115,7 +115,7 @@ func parseMySQLMetadata(config *scalersconfig.ScalerConfig) (*mySQLMetadata, err } meta.port = port - username, err := GetFromAuthOrMeta(config, "username") + username, err := GetFromAuthOrMetaOrEnv(config, "username", "usernameFromEnv") if err != nil { return nil, err } diff --git a/pkg/scalers/mysql_scaler_test.go b/pkg/scalers/mysql_scaler_test.go index e2b514a4b2a..956e225b37b 100644 --- a/pkg/scalers/mysql_scaler_test.go +++ b/pkg/scalers/mysql_scaler_test.go @@ -7,6 +7,7 @@ import ( ) var testMySQLResolvedEnv = map[string]string{ + "MYSQL_USERNAME": "test_username", "MYSQL_PASSWORD": "pass", "MYSQL_CONN_STR": "user@tcp(http://my.mysql.dev:3306)/stats_db", } @@ -46,6 +47,13 @@ var testMySQLMetadata = []parseMySQLMetadataTestData{ resolvedEnv: testMySQLResolvedEnv, raisesError: false, }, + // Params instead of conn str with userFromEnv + { + metadata: map[string]string{"query": "query", "queryValue": "12", "host": "test_host", "port": "test_port", "usernameFromEnv": "MYSQL_USERNAME", "passwordFromEnv": "MYSQL_PASSWORD", "dbName": "test_dbname"}, + authParams: map[string]string{}, + resolvedEnv: testMySQLResolvedEnv, + raisesError: false, + }, // Params from trigger authentication { metadata: map[string]string{"query": "query", "queryValue": "12"}, @@ -60,6 +68,13 @@ var testMySQLMetadata = []parseMySQLMetadataTestData{ resolvedEnv: testMySQLResolvedEnv, raisesError: true, }, + // No username provided in authParams, metadata, resolvedEnv + { + metadata: map[string]string{"query": "query", "queryValue": "12", "activationQueryValue": "AA"}, + authParams: map[string]string{"host": "test_host", "port": "test_port", "password": "MYSQL_PASSWORD", "dbName": "test_dbname"}, + resolvedEnv: map[string]string{}, + raisesError: true, + }, } var mySQLMetricIdentifiers = []mySQLMetricIdentifier{ diff --git a/pkg/scalers/scaler.go b/pkg/scalers/scaler.go index df37eb210e0..5cbc3d9886e 100644 --- a/pkg/scalers/scaler.go +++ b/pkg/scalers/scaler.go @@ -86,6 +86,23 @@ func GetFromAuthOrMeta(config *scalersconfig.ScalerConfig, field string) (string return result, err } +// GetFromAuthOrMetaOrEnv helps to get a field from Auth or Meta or Env sections +func GetFromAuthOrMetaOrEnv(config *scalersconfig.ScalerConfig, field string, envField string) (string, error) { + var result string + var err error + if config.AuthParams[field] != "" { + result = config.AuthParams[field] + } else if config.TriggerMetadata[field] != "" { + result = config.TriggerMetadata[field] + } else if config.ResolvedEnv[config.TriggerMetadata[envField]] != "" { + result = config.TriggerMetadata[envField] + } + if result == "" { + err = fmt.Errorf("%w: no %s or %s given", ErrScalerConfigMissingField, field, envField) + } + return result, err +} + // GenerateMetricNameWithIndex helps to add the index prefix to the metric name func GenerateMetricNameWithIndex(triggerIndex int, metricName string) string { return fmt.Sprintf("s%d-%s", triggerIndex, metricName) From 1fa2c48f24e16017322e71e30eae49ee6f0361cd Mon Sep 17 00:00:00 2001 From: Indresh2410 Date: Sun, 7 Jul 2024 11:38:22 +0000 Subject: [PATCH 2/8] Add changelog Signed-off-by: Indresh2410 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a1992488e8..61c60aba798 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -77,6 +77,7 @@ Here is an overview of all new **experimental** features: - **GCP Scalers**: Added custom time horizon in GCP scalers ([#5778](https://github.com/kedacore/keda/issues/5778)) - **GitHub Scaler**: Fixed pagination, fetching repository list ([#5738](https://github.com/kedacore/keda/issues/5738)) - **Kafka**: Fix logic to scale to zero on invalid offset even with earliest offsetResetPolicy ([#5689](https://github.com/kedacore/keda/issues/5689)) +- **MYSQL Scaler**: Add support to fetch username from env ([#5883](https://github.com/kedacore/keda/issues/5883)) ### Fixes From 6a3fcc7a94677ec6fbbc5dec6f26237e08ee8072 Mon Sep 17 00:00:00 2001 From: Indresh2410 Date: Sun, 7 Jul 2024 11:58:05 +0000 Subject: [PATCH 3/8] Change if-else block to switch block Signed-off-by: Indresh2410 --- pkg/scalers/scaler.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/pkg/scalers/scaler.go b/pkg/scalers/scaler.go index 5cbc3d9886e..73c41a5b3c6 100644 --- a/pkg/scalers/scaler.go +++ b/pkg/scalers/scaler.go @@ -90,12 +90,13 @@ func GetFromAuthOrMeta(config *scalersconfig.ScalerConfig, field string) (string func GetFromAuthOrMetaOrEnv(config *scalersconfig.ScalerConfig, field string, envField string) (string, error) { var result string var err error - if config.AuthParams[field] != "" { - result = config.AuthParams[field] - } else if config.TriggerMetadata[field] != "" { - result = config.TriggerMetadata[field] - } else if config.ResolvedEnv[config.TriggerMetadata[envField]] != "" { - result = config.TriggerMetadata[envField] + switch { + case config.AuthParams[field] != "": + return config.AuthParams[field], err + case config.TriggerMetadata[field] != "": + return config.TriggerMetadata[field], err + case config.ResolvedEnv[config.TriggerMetadata[envField]] != "": + return config.TriggerMetadata[envField], err } if result == "" { err = fmt.Errorf("%w: no %s or %s given", ErrScalerConfigMissingField, field, envField) From d7efc691c5987a161802f40b4572e23f7fea7154 Mon Sep 17 00:00:00 2001 From: Indresh2410 Date: Sun, 7 Jul 2024 13:29:50 +0000 Subject: [PATCH 4/8] Refactor scaler switch block Signed-off-by: Indresh2410 --- pkg/scalers/scaler.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/scalers/scaler.go b/pkg/scalers/scaler.go index 73c41a5b3c6..211e7edfef3 100644 --- a/pkg/scalers/scaler.go +++ b/pkg/scalers/scaler.go @@ -92,11 +92,11 @@ func GetFromAuthOrMetaOrEnv(config *scalersconfig.ScalerConfig, field string, en var err error switch { case config.AuthParams[field] != "": - return config.AuthParams[field], err + result = config.AuthParams[field] case config.TriggerMetadata[field] != "": - return config.TriggerMetadata[field], err + result = config.TriggerMetadata[field] case config.ResolvedEnv[config.TriggerMetadata[envField]] != "": - return config.TriggerMetadata[envField], err + result = config.TriggerMetadata[envField] } if result == "" { err = fmt.Errorf("%w: no %s or %s given", ErrScalerConfigMissingField, field, envField) From 322bba03b1c2cfcb98047e89adb7f859cef1369d Mon Sep 17 00:00:00 2001 From: Indresh2410 Date: Sun, 7 Jul 2024 14:02:58 +0000 Subject: [PATCH 5/8] Refactor method Signed-off-by: Indresh2410 --- pkg/scalers/mysql_scaler.go | 2 +- pkg/scalers/scaler.go | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/pkg/scalers/mysql_scaler.go b/pkg/scalers/mysql_scaler.go index 1ff17c34813..52bb52785ed 100644 --- a/pkg/scalers/mysql_scaler.go +++ b/pkg/scalers/mysql_scaler.go @@ -115,7 +115,7 @@ func parseMySQLMetadata(config *scalersconfig.ScalerConfig) (*mySQLMetadata, err } meta.port = port - username, err := GetFromAuthOrMetaOrEnv(config, "username", "usernameFromEnv") + username, err := GetFromAuthOrMetaOrEnv(config, "username") if err != nil { return nil, err } diff --git a/pkg/scalers/scaler.go b/pkg/scalers/scaler.go index 211e7edfef3..8ebb034b25e 100644 --- a/pkg/scalers/scaler.go +++ b/pkg/scalers/scaler.go @@ -35,6 +35,10 @@ import ( "github.com/kedacore/keda/v2/pkg/scalers/scalersconfig" ) +const ( + FromEnv = "FromEnv" +) + func init() { // Disable metrics for kafka client (sarama) // https://github.com/IBM/sarama/issues/1321 @@ -87,7 +91,7 @@ func GetFromAuthOrMeta(config *scalersconfig.ScalerConfig, field string) (string } // GetFromAuthOrMetaOrEnv helps to get a field from Auth or Meta or Env sections -func GetFromAuthOrMetaOrEnv(config *scalersconfig.ScalerConfig, field string, envField string) (string, error) { +func GetFromAuthOrMetaOrEnv(config *scalersconfig.ScalerConfig, field string) (string, error) { var result string var err error switch { @@ -95,11 +99,11 @@ func GetFromAuthOrMetaOrEnv(config *scalersconfig.ScalerConfig, field string, en result = config.AuthParams[field] case config.TriggerMetadata[field] != "": result = config.TriggerMetadata[field] - case config.ResolvedEnv[config.TriggerMetadata[envField]] != "": - result = config.TriggerMetadata[envField] + case config.ResolvedEnv[config.TriggerMetadata[field+FromEnv]] != "": + result = config.TriggerMetadata[field+FromEnv] } if result == "" { - err = fmt.Errorf("%w: no %s or %s given", ErrScalerConfigMissingField, field, envField) + err = fmt.Errorf("%w: no %s or %s given", ErrScalerConfigMissingField, field, field+FromEnv) } return result, err } From 1cf2e01f8d6164273c8ad3167dbbb198db9903ea Mon Sep 17 00:00:00 2001 From: Indresh2410 Date: Sun, 14 Jul 2024 06:06:22 +0000 Subject: [PATCH 6/8] Refactor Mysql Scaler Signed-off-by: Indresh2410 --- pkg/scalers/mysql_scaler.go | 123 +++++++------------------------ pkg/scalers/mysql_scaler_test.go | 4 +- pkg/scalers/scaler.go | 18 ----- 3 files changed, 30 insertions(+), 115 deletions(-) diff --git a/pkg/scalers/mysql_scaler.go b/pkg/scalers/mysql_scaler.go index 52bb52785ed..6022c4bb67b 100644 --- a/pkg/scalers/mysql_scaler.go +++ b/pkg/scalers/mysql_scaler.go @@ -5,7 +5,6 @@ import ( "database/sql" "fmt" "net" - "strconv" "strings" "github.com/go-logr/logr" @@ -25,16 +24,16 @@ type mySQLScaler struct { } type mySQLMetadata struct { - connectionString string // Database connection string - username string - password string - host string - port string - dbName string - query string - queryValue float64 - activationQueryValue float64 - metricName string + ConnectionString string `keda:"name=connectionString, order=authParams;resolvedEnv, optional"` // Database connection string + Username string `keda:"name=username, order=triggerMetadata;authParams;resolvedEnv, optional"` + Password string `keda:"name=password, order=authParams;resolvedEnv, optional"` + Host string `keda:"name=host, order=triggerMetadata;authParams, optional"` + Port string `keda:"name=port, order=triggerMetadata;authParams, optional"` + DbName string `keda:"name=dbName, order=triggerMetadata;authParams, optional"` + Query string `keda:"name=query, order=triggerMetadata"` + QueryValue float64 `keda:"name=queryValue, order=triggerMetadata"` + ActivationQueryValue float64 `keda:"name=activationQueryValue, order=triggerMetadata, default=0"` + MetricName string `keda:"name=metricName, order=triggerMetadata, optional"` } // NewMySQLScaler creates a new MySQL scaler @@ -64,101 +63,35 @@ func NewMySQLScaler(config *scalersconfig.ScalerConfig) (Scaler, error) { } func parseMySQLMetadata(config *scalersconfig.ScalerConfig) (*mySQLMetadata, error) { - meta := mySQLMetadata{} + meta := &mySQLMetadata{} - if val, ok := config.TriggerMetadata["query"]; ok { - meta.query = val - } else { - return nil, fmt.Errorf("no query given") - } - - if val, ok := config.TriggerMetadata["queryValue"]; ok { - queryValue, err := strconv.ParseFloat(val, 64) - if err != nil { - return nil, fmt.Errorf("queryValue parsing error %w", err) - } - meta.queryValue = queryValue - } else { - if config.AsMetricSource { - meta.queryValue = 0 - } else { - return nil, fmt.Errorf("no queryValue given") - } + if err := config.TypedConfig(meta); err != nil { + return nil, fmt.Errorf("error parsing mysql metadata: %w", err) } - meta.activationQueryValue = 0 - if val, ok := config.TriggerMetadata["activationQueryValue"]; ok { - activationQueryValue, err := strconv.ParseFloat(val, 64) - if err != nil { - return nil, fmt.Errorf("activationQueryValue parsing error %w", err) - } - meta.activationQueryValue = activationQueryValue - } - - switch { - case config.AuthParams["connectionString"] != "": - meta.connectionString = config.AuthParams["connectionString"] - case config.TriggerMetadata["connectionStringFromEnv"] != "": - meta.connectionString = config.ResolvedEnv[config.TriggerMetadata["connectionStringFromEnv"]] - default: - meta.connectionString = "" - var err error - host, err := GetFromAuthOrMeta(config, "host") - if err != nil { - return nil, err - } - meta.host = host - - port, err := GetFromAuthOrMeta(config, "port") - if err != nil { - return nil, err - } - meta.port = port - - username, err := GetFromAuthOrMetaOrEnv(config, "username") - if err != nil { - return nil, err - } - meta.username = username - - dbName, err := GetFromAuthOrMeta(config, "dbName") - if err != nil { - return nil, err - } - meta.dbName = dbName - - if config.AuthParams["password"] != "" { - meta.password = config.AuthParams["password"] - } else if config.TriggerMetadata["passwordFromEnv"] != "" { - meta.password = config.ResolvedEnv[config.TriggerMetadata["passwordFromEnv"]] - } - - if len(meta.password) == 0 { - return nil, fmt.Errorf("no password given") - } - } + meta.ActivationQueryValue = 0 - if meta.connectionString != "" { - meta.dbName = parseMySQLDbNameFromConnectionStr(meta.connectionString) + if meta.ConnectionString != "" { + meta.DbName = parseMySQLDbNameFromConnectionStr(meta.ConnectionString) } - meta.metricName = GenerateMetricNameWithIndex(config.TriggerIndex, kedautil.NormalizeString(fmt.Sprintf("mysql-%s", meta.dbName))) + meta.MetricName = GenerateMetricNameWithIndex(config.TriggerIndex, kedautil.NormalizeString(fmt.Sprintf("mysql-%s", meta.DbName))) - return &meta, nil + return meta, nil } // metadataToConnectionStr builds new MySQL connection string func metadataToConnectionStr(meta *mySQLMetadata) string { var connStr string - if meta.connectionString != "" { - connStr = meta.connectionString + if meta.ConnectionString != "" { + connStr = meta.ConnectionString } else { // Build connection str config := mysql.NewConfig() - config.Addr = net.JoinHostPort(meta.host, meta.port) - config.DBName = meta.dbName - config.Passwd = meta.password - config.User = meta.username + config.Addr = net.JoinHostPort(meta.Host, meta.Port) + config.DBName = meta.DbName + config.Passwd = meta.Password + config.User = meta.Username config.Net = "tcp" connStr = config.FormatDSN() } @@ -205,7 +138,7 @@ func (s *mySQLScaler) Close(context.Context) error { // getQueryResult returns result of the scaler query func (s *mySQLScaler) getQueryResult(ctx context.Context) (float64, error) { var value float64 - err := s.connection.QueryRowContext(ctx, s.metadata.query).Scan(&value) + err := s.connection.QueryRowContext(ctx, s.metadata.Query).Scan(&value) if err != nil { s.logger.Error(err, fmt.Sprintf("Could not query MySQL database: %s", err)) return 0, err @@ -217,9 +150,9 @@ func (s *mySQLScaler) getQueryResult(ctx context.Context) (float64, error) { func (s *mySQLScaler) GetMetricSpecForScaling(context.Context) []v2.MetricSpec { externalMetric := &v2.ExternalMetricSource{ Metric: v2.MetricIdentifier{ - Name: s.metadata.metricName, + Name: s.metadata.MetricName, }, - Target: GetMetricTargetMili(s.metricType, s.metadata.queryValue), + Target: GetMetricTargetMili(s.metricType, s.metadata.QueryValue), } metricSpec := v2.MetricSpec{ External: externalMetric, Type: externalMetricType, @@ -236,5 +169,5 @@ func (s *mySQLScaler) GetMetricsAndActivity(ctx context.Context, metricName stri metric := GenerateMetricInMili(metricName, num) - return []external_metrics.ExternalMetricValue{metric}, num > s.metadata.activationQueryValue, nil + return []external_metrics.ExternalMetricValue{metric}, num > s.metadata.ActivationQueryValue, nil } diff --git a/pkg/scalers/mysql_scaler_test.go b/pkg/scalers/mysql_scaler_test.go index 956e225b37b..70ed8c71d15 100644 --- a/pkg/scalers/mysql_scaler_test.go +++ b/pkg/scalers/mysql_scaler_test.go @@ -121,8 +121,8 @@ func TestMySQLGetMetricSpecForScaling(t *testing.T) { if err != nil { t.Fatal("Could not parse metadata:", err) } - if meta.metricName != testData.metricName { - t.Error("Wrong External metric source name:", meta.metricName) + if meta.MetricName != testData.metricName { + t.Error("Wrong External metric source name:", meta.MetricName) } } } diff --git a/pkg/scalers/scaler.go b/pkg/scalers/scaler.go index 8ebb034b25e..9867c9eba7e 100644 --- a/pkg/scalers/scaler.go +++ b/pkg/scalers/scaler.go @@ -90,24 +90,6 @@ func GetFromAuthOrMeta(config *scalersconfig.ScalerConfig, field string) (string return result, err } -// GetFromAuthOrMetaOrEnv helps to get a field from Auth or Meta or Env sections -func GetFromAuthOrMetaOrEnv(config *scalersconfig.ScalerConfig, field string) (string, error) { - var result string - var err error - switch { - case config.AuthParams[field] != "": - result = config.AuthParams[field] - case config.TriggerMetadata[field] != "": - result = config.TriggerMetadata[field] - case config.ResolvedEnv[config.TriggerMetadata[field+FromEnv]] != "": - result = config.TriggerMetadata[field+FromEnv] - } - if result == "" { - err = fmt.Errorf("%w: no %s or %s given", ErrScalerConfigMissingField, field, field+FromEnv) - } - return result, err -} - // GenerateMetricNameWithIndex helps to add the index prefix to the metric name func GenerateMetricNameWithIndex(triggerIndex int, metricName string) string { return fmt.Sprintf("s%d-%s", triggerIndex, metricName) From b59cadf3ebbad51dd2a4849e534b7a1f8bb83776 Mon Sep 17 00:00:00 2001 From: Indresh2410 Date: Sun, 14 Jul 2024 06:17:22 +0000 Subject: [PATCH 7/8] Address failure in static checks Signed-off-by: Indresh2410 --- pkg/scalers/mysql_scaler.go | 8 ++++---- pkg/scalers/scaler.go | 4 ---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/pkg/scalers/mysql_scaler.go b/pkg/scalers/mysql_scaler.go index 6022c4bb67b..75f59f2fd18 100644 --- a/pkg/scalers/mysql_scaler.go +++ b/pkg/scalers/mysql_scaler.go @@ -29,7 +29,7 @@ type mySQLMetadata struct { Password string `keda:"name=password, order=authParams;resolvedEnv, optional"` Host string `keda:"name=host, order=triggerMetadata;authParams, optional"` Port string `keda:"name=port, order=triggerMetadata;authParams, optional"` - DbName string `keda:"name=dbName, order=triggerMetadata;authParams, optional"` + DBName string `keda:"name=dbName, order=triggerMetadata;authParams, optional"` Query string `keda:"name=query, order=triggerMetadata"` QueryValue float64 `keda:"name=queryValue, order=triggerMetadata"` ActivationQueryValue float64 `keda:"name=activationQueryValue, order=triggerMetadata, default=0"` @@ -72,9 +72,9 @@ func parseMySQLMetadata(config *scalersconfig.ScalerConfig) (*mySQLMetadata, err meta.ActivationQueryValue = 0 if meta.ConnectionString != "" { - meta.DbName = parseMySQLDbNameFromConnectionStr(meta.ConnectionString) + meta.DBName = parseMySQLDbNameFromConnectionStr(meta.ConnectionString) } - meta.MetricName = GenerateMetricNameWithIndex(config.TriggerIndex, kedautil.NormalizeString(fmt.Sprintf("mysql-%s", meta.DbName))) + meta.MetricName = GenerateMetricNameWithIndex(config.TriggerIndex, kedautil.NormalizeString(fmt.Sprintf("mysql-%s", meta.DBName))) return meta, nil } @@ -89,7 +89,7 @@ func metadataToConnectionStr(meta *mySQLMetadata) string { // Build connection str config := mysql.NewConfig() config.Addr = net.JoinHostPort(meta.Host, meta.Port) - config.DBName = meta.DbName + config.DBName = meta.DBName config.Passwd = meta.Password config.User = meta.Username config.Net = "tcp" diff --git a/pkg/scalers/scaler.go b/pkg/scalers/scaler.go index 9867c9eba7e..df37eb210e0 100644 --- a/pkg/scalers/scaler.go +++ b/pkg/scalers/scaler.go @@ -35,10 +35,6 @@ import ( "github.com/kedacore/keda/v2/pkg/scalers/scalersconfig" ) -const ( - FromEnv = "FromEnv" -) - func init() { // Disable metrics for kafka client (sarama) // https://github.com/IBM/sarama/issues/1321 From 62dc49cfeed53a438995ac481fc000aef425c367 Mon Sep 17 00:00:00 2001 From: Indresh2410 Date: Thu, 18 Jul 2024 12:43:33 +0000 Subject: [PATCH 8/8] Address Review Comments Signed-off-by: Indresh2410 --- pkg/scalers/mysql_scaler.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/pkg/scalers/mysql_scaler.go b/pkg/scalers/mysql_scaler.go index 75f59f2fd18..65de8cfb1ec 100644 --- a/pkg/scalers/mysql_scaler.go +++ b/pkg/scalers/mysql_scaler.go @@ -69,8 +69,6 @@ func parseMySQLMetadata(config *scalersconfig.ScalerConfig) (*mySQLMetadata, err return nil, fmt.Errorf("error parsing mysql metadata: %w", err) } - meta.ActivationQueryValue = 0 - if meta.ConnectionString != "" { meta.DBName = parseMySQLDbNameFromConnectionStr(meta.ConnectionString) }