From d751d114e2f8defbab35e4f0dbed9ac54333f47d Mon Sep 17 00:00:00 2001 From: rodolfodc Date: Sun, 14 Mar 2021 18:29:02 -0300 Subject: [PATCH 01/12] create openstack aodh metrics scaler Signed-off-by: rodolfodc --- pkg/scalers/openstack_aodh_scaler.go | 362 +++++++++++++++++++++++++++ 1 file changed, 362 insertions(+) create mode 100644 pkg/scalers/openstack_aodh_scaler.go diff --git a/pkg/scalers/openstack_aodh_scaler.go b/pkg/scalers/openstack_aodh_scaler.go new file mode 100644 index 00000000000..f628dd9e3dc --- /dev/null +++ b/pkg/scalers/openstack_aodh_scaler.go @@ -0,0 +1,362 @@ +package scalers + +import ( + "context" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "path" + "strconv" + "time" + + "github.com/kedacore/keda/v2/pkg/scalers/openstack" + kedautil "github.com/kedacore/keda/v2/pkg/util" + v2beta2 "k8s.io/api/autoscaling/v2beta2" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/metrics/pkg/apis/external_metrics" + logf "sigs.k8s.io/controller-runtime/pkg/log" +) + +const ( + defaultValueWhenError = 0 + aodhDefaultHTTPClientTimeout = 30 +) + +/* expected structure declarations */ + +type aodhMetadata struct { + metricsURL string + metricID string + aggregationMethod string + granularity int + threshold float64 + timeout int +} + +type aodhAuthenticationMetadata struct { + userID string + password string + authURL string + appCredentialSecret string + appCredentialSecretID string +} + +type aodhScaler struct { + metadata *aodhMetadata + authMetadata *openstack.Client +} + +type measureResult struct { + measures [][]interface{} +} + +/* end of declarations */ + +var aodhLog = logf.Log.WithName("aodh_scaler") + +// NewOpenstackAodhScaler creates new AODH openstack scaler instance +func NewOpenstackAodhScaler(config *ScalerConfig) (Scaler, error) { + var keystoneAuth *openstack.Client + + aodhMetadata, err := parseAodhMetadata(config) + + if err != nil { + return nil, fmt.Errorf("error parsing AODH metadata: %s", err) + } + + authMetadata, err := parseAodhAuthenticationMetadata(config) + + if err != nil { + return nil, fmt.Errorf("error parsing AODH authentication metadata: %s", err) + } + + // User choose the "application_credentials" authentication method + if authMetadata.appCredentialSecretID != "" { + keystoneAuth, err = openstack.NewAppCredentialsAuth(authMetadata.authURL, authMetadata.appCredentialSecretID, authMetadata.appCredentialSecret, aodhMetadata.timeout) + + if err != nil { + return nil, fmt.Errorf("error getting openstack credentials for application credentials method: %s", err) + } + + } else { + // User choose the "password" authentication method + if authMetadata.userID != "" { + keystoneAuth, err = openstack.NewPasswordAuth(authMetadata.authURL, authMetadata.userID, authMetadata.password, "", aodhMetadata.timeout) + + if err != nil { + return nil, fmt.Errorf("error getting openstack credentials for password method: %s", err) + } + } else { + return nil, fmt.Errorf("no authentication method was provided for OpenStack") + } + } + + return &aodhScaler{ + metadata: aodhMetadata, + authMetadata: keystoneAuth, + }, nil +} + +func parseAodhMetadata(config *ScalerConfig) (*aodhMetadata, error) { + meta := aodhMetadata{} + triggerMetadata := config.TriggerMetadata + + if val, ok := triggerMetadata["metricsURL"]; ok && val != "" { + meta.metricsURL = val + } else { + aodhLog.Error(fmt.Errorf("No metricsURL could be read"), "Error readig metricsURL") + return nil, fmt.Errorf("No metricsURL was declared") + } + + if val, ok := triggerMetadata["metricID"]; ok && val != "" { + meta.metricID = val + } else { + aodhLog.Error(fmt.Errorf("No metricID could be read"), "Error reading metricID") + return nil, fmt.Errorf("No metricID was declared") + } + + if val, ok := triggerMetadata["aggregationMethod"]; ok && val != "" { + meta.aggregationMethod = val + } else { + aodhLog.Error(fmt.Errorf("No aggregationMethod could be read"), "Error reading aggregation method") + return nil, fmt.Errorf("No aggregationMethod could be read") + } + + if val, ok := triggerMetadata["granularity"]; ok && val != "" { + if granularity, err := strconv.Atoi(val); err != nil { + if err != nil { + aodhLog.Error(err, "Error converting granulality information %s", err.Error) + return nil, err + } + + meta.granularity = granularity + + } + + } else { + return nil, fmt.Errorf("No granularity found") + } + + if val, ok := triggerMetadata["threshold"]; ok && val != "" { + // converts the string to float64 but its value is convertible to float32 without changing + _threshold, err := strconv.ParseFloat(val, 32) + if err != nil { + aodhLog.Error(err, "Error parsing AODH metadata", "threshold", "threshold") + return nil, fmt.Errorf("Error parsing AODH metadata : %s", err.Error()) + } + + meta.threshold = _threshold + } + + return &meta, nil +} + +func parseAodhAuthenticationMetadata(config *ScalerConfig) (aodhAuthenticationMetadata, error) { + authMeta := aodhAuthenticationMetadata{} + authParams := config.AuthParams + + if val, ok := authParams["authURL"]; ok && val != "" { + authMeta.authURL = authParams["authURL"] + } else { + return authMeta, fmt.Errorf("authURL doesn't exist in the authParams") + } + + if val, ok := authParams["userID"]; ok && val != "" { + authMeta.userID = val + + if val, ok := authParams["password"]; ok && val != "" { + authMeta.password = val + } else { + return authMeta, fmt.Errorf("password doesn't exist in the authParams") + } + + } else if val, ok := authParams["appCredentialSecretId"]; ok && val != "" { + authMeta.appCredentialSecretID = val + + if val, ok := authParams["appCredentialSecret"]; ok && val != "" { + authMeta.appCredentialSecret = val + } + + } else { + return authMeta, fmt.Errorf("neither userID or appCredentialSecretID exist in the authParams") + } + + return authMeta, nil +} + +func (a *aodhScaler) GetMetricSpecForScaling() []v2beta2.MetricSpec { + targetMetricVal := resource.NewQuantity(int64(a.metadata.threshold), resource.DecimalSI) + externalMetric := &v2beta2.ExternalMetricSource{ + Metric: v2beta2.MetricIdentifier{ + //aux1, err := fmt.Sprintf("%s-%s", "openstack-AODH", a.authMetadata.AuthURL) + Name: kedautil.NormalizeString(fmt.Sprintf("openstack-aodh-%s", a.metadata.aggregationMethod)), + }, + Target: v2beta2.MetricTarget{ + Type: v2beta2.AverageValueMetricType, + AverageValue: targetMetricVal, + }, + } + + metricSpec := v2beta2.MetricSpec{ + External: externalMetric, + Type: externalMetricType, + } + + return []v2beta2.MetricSpec{metricSpec} +} + +func (a *aodhScaler) GetMetrics(ctx context.Context, metricName string, metricSelector labels.Selector) ([]external_metrics.ExternalMetricValue, error) { + val, err := a.readOpenstackMetrics() + + if err != nil { + aodhLog.Error(err, "Error collecting metric value") + return []external_metrics.ExternalMetricValue{}, err + } + + metric := external_metrics.ExternalMetricValue{ + MetricName: metricName, + Value: *resource.NewQuantity(int64(val), resource.DecimalSI), + Timestamp: metav1.Now(), + } + + return append([]external_metrics.ExternalMetricValue{}, metric), nil +} + +func (a *aodhScaler) IsActive(ctx context.Context) (bool, error) { + val, err := a.readOpenstackMetrics() + + if err != nil { + return false, err + } + + return val > 0, nil +} + +func (a *aodhScaler) Close() error { + return nil +} + +// Gets measureament from API as float64, converts it to int and return the value. +func (a *aodhScaler) readOpenstackMetrics() (float64, error) { + + var token string = "" + var metricURL string = a.metadata.metricsURL + + isValid, validationError := openstack.IsTokenValid(*a.authMetadata) + + if validationError != nil { + aodhLog.Error(validationError, "Unable to check token validity.") + return 0, validationError + } + + if !isValid { + var tokenRequestError error + token, tokenRequestError = a.authMetadata.GetToken() + a.authMetadata.AuthToken = token + if tokenRequestError != nil { + aodhLog.Error(tokenRequestError, "The token being used is invalid") + return defaultValueWhenError, tokenRequestError + } + } + + token = a.authMetadata.AuthToken + + aodhMetricsURL, err := url.Parse(metricURL) + + if err != nil { + aodhLog.Error(err, "The metrics URL provided is invalid") + return defaultValueWhenError, fmt.Errorf("The metrics URL is invalid: %s", err.Error()) + } + + aodhMetricsURL.Path = path.Join(aodhMetricsURL.Path, a.metadata.metricID+"/measures") + queryParameter := aodhMetricsURL.Query() + granularity := 2 // We start with granularity with value 2 cause gnocchi APIm which is used by openstack, consider a time window, and we want to get the last value + + if a.metadata.granularity <= 0 { + aodhLog.Error(fmt.Errorf("Granularity Value is less than 1"), "Minimum accepatble value expected for ganularity is 1.") + return defaultValueWhenError, fmt.Errorf("Granularity Value is less than 1") + } + + if (a.metadata.granularity / 60) > 1 { + granularity = (a.metadata.granularity / 60) + } + + granularity-- + + queryParameter.Set("granularity", strconv.Itoa(a.metadata.granularity)) + queryParameter.Set("aggregation", a.metadata.aggregationMethod) + + currTimeWithWindow := time.Now().Add(time.Minute + time.Duration(granularity)).Format(time.RFC3339) + queryParameter.Set("start", string(currTimeWithWindow)[:17]+"00") + + aodhMetricsURL.RawQuery = queryParameter.Encode() + + aodhRequest, newReqErr := http.NewRequest("GET", aodhMetricsURL.String(), nil) + if newReqErr != nil { + aodhLog.Error(newReqErr, "Could not build metrics request", nil) + } + aodhRequest.Header.Set("X-Auth-Token", token) + + resp, requestError := a.authMetadata.HTTPClient.Do(aodhRequest) + + if requestError != nil { + aodhLog.Error(requestError, "Unable to request Metrics from URL: %s.", a.metadata.metricsURL) + return defaultValueWhenError, requestError + } + + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + + bodyError, readError := ioutil.ReadAll(resp.Body) + + if readError != nil { + aodhLog.Error(readError, "Request failed with code: %s for URL: %s", resp.StatusCode, a.metadata.metricsURL) + return defaultValueWhenError, readError + } + + return defaultValueWhenError, fmt.Errorf(string(bodyError)) + } + + m := measureResult{} + body, errConvertJSON := ioutil.ReadAll(resp.Body) + + if errConvertJSON != nil { + aodhLog.Error(errConvertJSON, "Failed to convert Body format response to json") + return defaultValueWhenError, err + } + + if body == nil { + return defaultValueWhenError, nil + } + + errUnMarshall := json.Unmarshal([]byte(body), &m.measures) + + if errUnMarshall != nil { + aodhLog.Error(errUnMarshall, "Failed converting json format Body structure.") + return defaultValueWhenError, errUnMarshall + } + + var targetMeasure []interface{} = nil + + if len(m.measures) > 1 { + targetMeasure = m.measures[len(m.measures)-1] + } + + if len(targetMeasure) != 3 { + aodhLog.Error(fmt.Errorf("Unexpected json response"), "Unexpected json tuple, expected structure is [string, float, float].") + return defaultValueWhenError, fmt.Errorf("Unexpected json response") + } + + if val, ok := targetMeasure[2].(float64); ok { + return val, nil + } + + aodhLog.Error(fmt.Errorf("Failed to convert interface type to flaot64"), "Unable to convert targetMeasure to expected format float64") + return defaultValueWhenError, fmt.Errorf("Failed to convert interface type to flaot64") + +} From 8ec13a0a9d796074d135b1b5cea4b74533774efd Mon Sep 17 00:00:00 2001 From: rodolfodc Date: Wed, 17 Mar 2021 11:19:45 -0300 Subject: [PATCH 02/12] Modify scaler to fit openstack keystone client api Signed-off-by: rodolfodc --- pkg/scalers/openstack_aodh_scaler.go | 362 --------------------------- 1 file changed, 362 deletions(-) delete mode 100644 pkg/scalers/openstack_aodh_scaler.go diff --git a/pkg/scalers/openstack_aodh_scaler.go b/pkg/scalers/openstack_aodh_scaler.go deleted file mode 100644 index f628dd9e3dc..00000000000 --- a/pkg/scalers/openstack_aodh_scaler.go +++ /dev/null @@ -1,362 +0,0 @@ -package scalers - -import ( - "context" - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "net/url" - "path" - "strconv" - "time" - - "github.com/kedacore/keda/v2/pkg/scalers/openstack" - kedautil "github.com/kedacore/keda/v2/pkg/util" - v2beta2 "k8s.io/api/autoscaling/v2beta2" - "k8s.io/apimachinery/pkg/api/resource" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/metrics/pkg/apis/external_metrics" - logf "sigs.k8s.io/controller-runtime/pkg/log" -) - -const ( - defaultValueWhenError = 0 - aodhDefaultHTTPClientTimeout = 30 -) - -/* expected structure declarations */ - -type aodhMetadata struct { - metricsURL string - metricID string - aggregationMethod string - granularity int - threshold float64 - timeout int -} - -type aodhAuthenticationMetadata struct { - userID string - password string - authURL string - appCredentialSecret string - appCredentialSecretID string -} - -type aodhScaler struct { - metadata *aodhMetadata - authMetadata *openstack.Client -} - -type measureResult struct { - measures [][]interface{} -} - -/* end of declarations */ - -var aodhLog = logf.Log.WithName("aodh_scaler") - -// NewOpenstackAodhScaler creates new AODH openstack scaler instance -func NewOpenstackAodhScaler(config *ScalerConfig) (Scaler, error) { - var keystoneAuth *openstack.Client - - aodhMetadata, err := parseAodhMetadata(config) - - if err != nil { - return nil, fmt.Errorf("error parsing AODH metadata: %s", err) - } - - authMetadata, err := parseAodhAuthenticationMetadata(config) - - if err != nil { - return nil, fmt.Errorf("error parsing AODH authentication metadata: %s", err) - } - - // User choose the "application_credentials" authentication method - if authMetadata.appCredentialSecretID != "" { - keystoneAuth, err = openstack.NewAppCredentialsAuth(authMetadata.authURL, authMetadata.appCredentialSecretID, authMetadata.appCredentialSecret, aodhMetadata.timeout) - - if err != nil { - return nil, fmt.Errorf("error getting openstack credentials for application credentials method: %s", err) - } - - } else { - // User choose the "password" authentication method - if authMetadata.userID != "" { - keystoneAuth, err = openstack.NewPasswordAuth(authMetadata.authURL, authMetadata.userID, authMetadata.password, "", aodhMetadata.timeout) - - if err != nil { - return nil, fmt.Errorf("error getting openstack credentials for password method: %s", err) - } - } else { - return nil, fmt.Errorf("no authentication method was provided for OpenStack") - } - } - - return &aodhScaler{ - metadata: aodhMetadata, - authMetadata: keystoneAuth, - }, nil -} - -func parseAodhMetadata(config *ScalerConfig) (*aodhMetadata, error) { - meta := aodhMetadata{} - triggerMetadata := config.TriggerMetadata - - if val, ok := triggerMetadata["metricsURL"]; ok && val != "" { - meta.metricsURL = val - } else { - aodhLog.Error(fmt.Errorf("No metricsURL could be read"), "Error readig metricsURL") - return nil, fmt.Errorf("No metricsURL was declared") - } - - if val, ok := triggerMetadata["metricID"]; ok && val != "" { - meta.metricID = val - } else { - aodhLog.Error(fmt.Errorf("No metricID could be read"), "Error reading metricID") - return nil, fmt.Errorf("No metricID was declared") - } - - if val, ok := triggerMetadata["aggregationMethod"]; ok && val != "" { - meta.aggregationMethod = val - } else { - aodhLog.Error(fmt.Errorf("No aggregationMethod could be read"), "Error reading aggregation method") - return nil, fmt.Errorf("No aggregationMethod could be read") - } - - if val, ok := triggerMetadata["granularity"]; ok && val != "" { - if granularity, err := strconv.Atoi(val); err != nil { - if err != nil { - aodhLog.Error(err, "Error converting granulality information %s", err.Error) - return nil, err - } - - meta.granularity = granularity - - } - - } else { - return nil, fmt.Errorf("No granularity found") - } - - if val, ok := triggerMetadata["threshold"]; ok && val != "" { - // converts the string to float64 but its value is convertible to float32 without changing - _threshold, err := strconv.ParseFloat(val, 32) - if err != nil { - aodhLog.Error(err, "Error parsing AODH metadata", "threshold", "threshold") - return nil, fmt.Errorf("Error parsing AODH metadata : %s", err.Error()) - } - - meta.threshold = _threshold - } - - return &meta, nil -} - -func parseAodhAuthenticationMetadata(config *ScalerConfig) (aodhAuthenticationMetadata, error) { - authMeta := aodhAuthenticationMetadata{} - authParams := config.AuthParams - - if val, ok := authParams["authURL"]; ok && val != "" { - authMeta.authURL = authParams["authURL"] - } else { - return authMeta, fmt.Errorf("authURL doesn't exist in the authParams") - } - - if val, ok := authParams["userID"]; ok && val != "" { - authMeta.userID = val - - if val, ok := authParams["password"]; ok && val != "" { - authMeta.password = val - } else { - return authMeta, fmt.Errorf("password doesn't exist in the authParams") - } - - } else if val, ok := authParams["appCredentialSecretId"]; ok && val != "" { - authMeta.appCredentialSecretID = val - - if val, ok := authParams["appCredentialSecret"]; ok && val != "" { - authMeta.appCredentialSecret = val - } - - } else { - return authMeta, fmt.Errorf("neither userID or appCredentialSecretID exist in the authParams") - } - - return authMeta, nil -} - -func (a *aodhScaler) GetMetricSpecForScaling() []v2beta2.MetricSpec { - targetMetricVal := resource.NewQuantity(int64(a.metadata.threshold), resource.DecimalSI) - externalMetric := &v2beta2.ExternalMetricSource{ - Metric: v2beta2.MetricIdentifier{ - //aux1, err := fmt.Sprintf("%s-%s", "openstack-AODH", a.authMetadata.AuthURL) - Name: kedautil.NormalizeString(fmt.Sprintf("openstack-aodh-%s", a.metadata.aggregationMethod)), - }, - Target: v2beta2.MetricTarget{ - Type: v2beta2.AverageValueMetricType, - AverageValue: targetMetricVal, - }, - } - - metricSpec := v2beta2.MetricSpec{ - External: externalMetric, - Type: externalMetricType, - } - - return []v2beta2.MetricSpec{metricSpec} -} - -func (a *aodhScaler) GetMetrics(ctx context.Context, metricName string, metricSelector labels.Selector) ([]external_metrics.ExternalMetricValue, error) { - val, err := a.readOpenstackMetrics() - - if err != nil { - aodhLog.Error(err, "Error collecting metric value") - return []external_metrics.ExternalMetricValue{}, err - } - - metric := external_metrics.ExternalMetricValue{ - MetricName: metricName, - Value: *resource.NewQuantity(int64(val), resource.DecimalSI), - Timestamp: metav1.Now(), - } - - return append([]external_metrics.ExternalMetricValue{}, metric), nil -} - -func (a *aodhScaler) IsActive(ctx context.Context) (bool, error) { - val, err := a.readOpenstackMetrics() - - if err != nil { - return false, err - } - - return val > 0, nil -} - -func (a *aodhScaler) Close() error { - return nil -} - -// Gets measureament from API as float64, converts it to int and return the value. -func (a *aodhScaler) readOpenstackMetrics() (float64, error) { - - var token string = "" - var metricURL string = a.metadata.metricsURL - - isValid, validationError := openstack.IsTokenValid(*a.authMetadata) - - if validationError != nil { - aodhLog.Error(validationError, "Unable to check token validity.") - return 0, validationError - } - - if !isValid { - var tokenRequestError error - token, tokenRequestError = a.authMetadata.GetToken() - a.authMetadata.AuthToken = token - if tokenRequestError != nil { - aodhLog.Error(tokenRequestError, "The token being used is invalid") - return defaultValueWhenError, tokenRequestError - } - } - - token = a.authMetadata.AuthToken - - aodhMetricsURL, err := url.Parse(metricURL) - - if err != nil { - aodhLog.Error(err, "The metrics URL provided is invalid") - return defaultValueWhenError, fmt.Errorf("The metrics URL is invalid: %s", err.Error()) - } - - aodhMetricsURL.Path = path.Join(aodhMetricsURL.Path, a.metadata.metricID+"/measures") - queryParameter := aodhMetricsURL.Query() - granularity := 2 // We start with granularity with value 2 cause gnocchi APIm which is used by openstack, consider a time window, and we want to get the last value - - if a.metadata.granularity <= 0 { - aodhLog.Error(fmt.Errorf("Granularity Value is less than 1"), "Minimum accepatble value expected for ganularity is 1.") - return defaultValueWhenError, fmt.Errorf("Granularity Value is less than 1") - } - - if (a.metadata.granularity / 60) > 1 { - granularity = (a.metadata.granularity / 60) - } - - granularity-- - - queryParameter.Set("granularity", strconv.Itoa(a.metadata.granularity)) - queryParameter.Set("aggregation", a.metadata.aggregationMethod) - - currTimeWithWindow := time.Now().Add(time.Minute + time.Duration(granularity)).Format(time.RFC3339) - queryParameter.Set("start", string(currTimeWithWindow)[:17]+"00") - - aodhMetricsURL.RawQuery = queryParameter.Encode() - - aodhRequest, newReqErr := http.NewRequest("GET", aodhMetricsURL.String(), nil) - if newReqErr != nil { - aodhLog.Error(newReqErr, "Could not build metrics request", nil) - } - aodhRequest.Header.Set("X-Auth-Token", token) - - resp, requestError := a.authMetadata.HTTPClient.Do(aodhRequest) - - if requestError != nil { - aodhLog.Error(requestError, "Unable to request Metrics from URL: %s.", a.metadata.metricsURL) - return defaultValueWhenError, requestError - } - - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - - bodyError, readError := ioutil.ReadAll(resp.Body) - - if readError != nil { - aodhLog.Error(readError, "Request failed with code: %s for URL: %s", resp.StatusCode, a.metadata.metricsURL) - return defaultValueWhenError, readError - } - - return defaultValueWhenError, fmt.Errorf(string(bodyError)) - } - - m := measureResult{} - body, errConvertJSON := ioutil.ReadAll(resp.Body) - - if errConvertJSON != nil { - aodhLog.Error(errConvertJSON, "Failed to convert Body format response to json") - return defaultValueWhenError, err - } - - if body == nil { - return defaultValueWhenError, nil - } - - errUnMarshall := json.Unmarshal([]byte(body), &m.measures) - - if errUnMarshall != nil { - aodhLog.Error(errUnMarshall, "Failed converting json format Body structure.") - return defaultValueWhenError, errUnMarshall - } - - var targetMeasure []interface{} = nil - - if len(m.measures) > 1 { - targetMeasure = m.measures[len(m.measures)-1] - } - - if len(targetMeasure) != 3 { - aodhLog.Error(fmt.Errorf("Unexpected json response"), "Unexpected json tuple, expected structure is [string, float, float].") - return defaultValueWhenError, fmt.Errorf("Unexpected json response") - } - - if val, ok := targetMeasure[2].(float64); ok { - return val, nil - } - - aodhLog.Error(fmt.Errorf("Failed to convert interface type to flaot64"), "Unable to convert targetMeasure to expected format float64") - return defaultValueWhenError, fmt.Errorf("Failed to convert interface type to flaot64") - -} From 8be74d79bd7e1d66ad1e52589df97c8d8af2ff79 Mon Sep 17 00:00:00 2001 From: rodolfodc Date: Wed, 17 Mar 2021 11:47:05 -0300 Subject: [PATCH 03/12] Add tests and rename aodh scaler to openstack metric scaler Signed-off-by: rodolfodc --- pkg/scalers/openstack_metrics_scaler.go | 365 +++++++++++++++++++ pkg/scalers/openstack_metrics_scaler_test.go | 161 ++++++++ 2 files changed, 526 insertions(+) create mode 100644 pkg/scalers/openstack_metrics_scaler.go create mode 100644 pkg/scalers/openstack_metrics_scaler_test.go diff --git a/pkg/scalers/openstack_metrics_scaler.go b/pkg/scalers/openstack_metrics_scaler.go new file mode 100644 index 00000000000..c27e12ae876 --- /dev/null +++ b/pkg/scalers/openstack_metrics_scaler.go @@ -0,0 +1,365 @@ +package scalers + +import ( + "context" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "path" + "strconv" + "time" + + "github.com/kedacore/keda/v2/pkg/scalers/openstack" + kedautil "github.com/kedacore/keda/v2/pkg/util" + v2beta2 "k8s.io/api/autoscaling/v2beta2" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/metrics/pkg/apis/external_metrics" + logf "sigs.k8s.io/controller-runtime/pkg/log" +) + +const ( + defaultValueWhenError = 0 + metricDefaultHTTPClientTimeout = 30 +) + +/* expected structure declarations */ + +type openstackMetricMetadata struct { + metricsURL string + metricID string + aggregationMethod string + granularity int + threshold float64 + timeout int +} + +type openstackMetricAuthenticationMetadata struct { + userID string + password string + authURL string + appCredentialSecret string + appCredentialSecretID string +} + +type openstackMetricScaler struct { + metadata *openstackMetricMetadata + metricClient openstack.Client +} + +type measureResult struct { + measures [][]interface{} +} + +/* end of declarations */ + +var openstackMetricLog = logf.Log.WithName("openstack_metric_scaler") + +// NewOpenstackMetricScaler creates new openstack metrics scaler instance +func NewOpenstackMetricScaler(config *ScalerConfig) (Scaler, error) { + var keystoneAuth *openstack.KeystoneAuthRequest + var metricsClient openstack.Client + + openstackMetricMetadata, err := parseOpenstackMetricMetadata(config) + + if err != nil { + return nil, fmt.Errorf("error parsing openstack Metric metadata: %s", err) + } + + authMetadata, err := parseOpenstackMetricAuthenticationMetadata(config) + + if err != nil { + return nil, fmt.Errorf("error parsing openstack metric authentication metadata: %s", err) + } + + // User choose the "application_credentials" authentication method + if authMetadata.appCredentialSecretID != "" { + keystoneAuth, err = openstack.NewAppCredentialsAuth(authMetadata.authURL, authMetadata.appCredentialSecretID, authMetadata.appCredentialSecret, openstackMetricMetadata.timeout) + + if err != nil { + return nil, fmt.Errorf("error getting openstack credentials for application credentials method: %s", err) + } + + } else { + // User choose the "password" authentication method + if authMetadata.userID != "" { + keystoneAuth, err = openstack.NewPasswordAuth(authMetadata.authURL, authMetadata.userID, authMetadata.password, "", openstackMetricMetadata.timeout) + + if err != nil { + return nil, fmt.Errorf("error getting openstack credentials for password method: %s", err) + } + } else { + return nil, fmt.Errorf("no authentication method was provided for OpenStack") + } + } + + if openstackMetricMetadata.metricsURL == "" { + metricsClient, err = keystoneAuth.RequestClient() + } + + return &openstackMetricScaler{ + metadata: openstackMetricMetadata, + metricClient: metricsClient, + }, nil +} + +func parseOpenstackMetricMetadata(config *ScalerConfig) (*openstackMetricMetadata, error) { + meta := openstackMetricMetadata{} + triggerMetadata := config.TriggerMetadata + + if val, ok := triggerMetadata["metricsURL"]; ok && val != "" { + meta.metricsURL = val + } else { + openstackMetricLog.Error(fmt.Errorf("No metricsURL could be read"), "Error readig metricsURL") + return nil, fmt.Errorf("No metricsURL was declared") + } + + if val, ok := triggerMetadata["metricID"]; ok && val != "" { + meta.metricID = val + } else { + openstackMetricLog.Error(fmt.Errorf("No metricID could be read"), "Error reading metricID") + return nil, fmt.Errorf("No metricID was declared") + } + + if val, ok := triggerMetadata["aggregationMethod"]; ok && val != "" { + meta.aggregationMethod = val + } else { + openstackMetricLog.Error(fmt.Errorf("No aggregationMethod could be read"), "Error reading aggregation method") + return nil, fmt.Errorf("No aggregationMethod could be read") + } + + if val, ok := triggerMetadata["granularity"]; ok && val != "" { + if granularity, err := strconv.Atoi(val); err != nil { + if err != nil { + openstackMetricLog.Error(err, "Error converting granulality information %s", err.Error) + return nil, err + } + + meta.granularity = granularity + + } + + } else { + return nil, fmt.Errorf("No granularity found") + } + + if val, ok := triggerMetadata["threshold"]; ok && val != "" { + // converts the string to float64 but its value is convertible to float32 without changing + _threshold, err := strconv.ParseFloat(val, 32) + if err != nil { + openstackMetricLog.Error(err, "Error parsing openstack metric metadata", "threshold", "threshold") + return nil, fmt.Errorf("Error parsing openstack metric metadata : %s", err.Error()) + } + + meta.threshold = _threshold + } + + return &meta, nil +} + +func parseOpenstackMetricAuthenticationMetadata(config *ScalerConfig) (openstackMetricAuthenticationMetadata, error) { + authMeta := openstackMetricAuthenticationMetadata{} + authParams := config.AuthParams + + if val, ok := authParams["authURL"]; ok && val != "" { + authMeta.authURL = authParams["authURL"] + } else { + return authMeta, fmt.Errorf("authURL doesn't exist in the authParams") + } + + if val, ok := authParams["userID"]; ok && val != "" { + authMeta.userID = val + + if val, ok := authParams["password"]; ok && val != "" { + authMeta.password = val + } else { + return authMeta, fmt.Errorf("password doesn't exist in the authParams") + } + + } else if val, ok := authParams["appCredentialSecretId"]; ok && val != "" { + authMeta.appCredentialSecretID = val + + if val, ok := authParams["appCredentialSecret"]; ok && val != "" { + authMeta.appCredentialSecret = val + } + + } else { + return authMeta, fmt.Errorf("neither userID or appCredentialSecretID exist in the authParams") + } + + return authMeta, nil +} + +func (a *openstackMetricScaler) GetMetricSpecForScaling() []v2beta2.MetricSpec { + targetMetricVal := resource.NewQuantity(int64(a.metadata.threshold), resource.DecimalSI) + externalMetric := &v2beta2.ExternalMetricSource{ + Metric: v2beta2.MetricIdentifier{ + //aux1, err := fmt.Sprintf("%s-%s", "openstack-metric", a.authMetadata.AuthURL) + Name: kedautil.NormalizeString(fmt.Sprintf("openstack-metric-%s", a.metadata.aggregationMethod)), + }, + Target: v2beta2.MetricTarget{ + Type: v2beta2.AverageValueMetricType, + AverageValue: targetMetricVal, + }, + } + + metricSpec := v2beta2.MetricSpec{ + External: externalMetric, + Type: externalMetricType, + } + + return []v2beta2.MetricSpec{metricSpec} +} + +func (a *openstackMetricScaler) GetMetrics(ctx context.Context, metricName string, metricSelector labels.Selector) ([]external_metrics.ExternalMetricValue, error) { + val, err := a.readOpenstackMetrics() + + if err != nil { + openstackMetricLog.Error(err, "Error collecting metric value") + return []external_metrics.ExternalMetricValue{}, err + } + + metric := external_metrics.ExternalMetricValue{ + MetricName: metricName, + Value: *resource.NewQuantity(int64(val), resource.DecimalSI), + Timestamp: metav1.Now(), + } + + return append([]external_metrics.ExternalMetricValue{}, metric), nil +} + +func (a *openstackMetricScaler) IsActive(ctx context.Context) (bool, error) { + val, err := a.readOpenstackMetrics() + + if err != nil { + return false, err + } + + return val > 0, nil +} + +func (a *openstackMetricScaler) Close() error { + return nil +} + +// Gets measureament from API as float64, converts it to int and return the value. +func (a *openstackMetricScaler) readOpenstackMetrics() (float64, error) { + + var metricURL string = a.metadata.metricsURL + + isValid, validationError := a.metricClient.IsTokenValid() + + if validationError != nil { + openstackMetricLog.Error(validationError, "Unable to check token validity.") + return 0, validationError + } + + if !isValid { + var tokenRequestError error + tokenRequestError = a.metricClient.RenewToken() + if tokenRequestError != nil { + openstackMetricLog.Error(tokenRequestError, "The token being used is invalid") + return defaultValueWhenError, tokenRequestError + } + } + + token := a.metricClient.Token + + openstackMetricsURL, err := url.Parse(metricURL) + + if err != nil { + openstackMetricLog.Error(err, "The metrics URL provided is invalid") + return defaultValueWhenError, fmt.Errorf("The metrics URL is invalid: %s", err.Error()) + } + + openstackMetricsURL.Path = path.Join(openstackMetricsURL.Path, a.metadata.metricID+"/measures") + queryParameter := openstackMetricsURL.Query() + granularity := 2 // We start with granularity with value 2 cause gnocchi APIm which is used by openstack, consider a time window, and we want to get the last value + + if a.metadata.granularity <= 0 { + openstackMetricLog.Error(fmt.Errorf("Granularity Value is less than 1"), "Minimum accepatble value expected for ganularity is 1.") + return defaultValueWhenError, fmt.Errorf("Granularity Value is less than 1") + } + + if (a.metadata.granularity / 60) > 1 { + granularity = (a.metadata.granularity / 60) + } + + granularity-- + + queryParameter.Set("granularity", strconv.Itoa(a.metadata.granularity)) + queryParameter.Set("aggregation", a.metadata.aggregationMethod) + + currTimeWithWindow := time.Now().Add(time.Minute + time.Duration(granularity)).Format(time.RFC3339) + queryParameter.Set("start", string(currTimeWithWindow)[:17]+"00") + + openstackMetricsURL.RawQuery = queryParameter.Encode() + + openstackMetricRequest, newReqErr := http.NewRequest("GET", openstackMetricsURL.String(), nil) + if newReqErr != nil { + openstackMetricLog.Error(newReqErr, "Could not build metrics request", nil) + } + openstackMetricRequest.Header.Set("X-Auth-Token", token) + + resp, requestError := a.metricClient.HTTPClient.Do(openstackMetricRequest) + + if requestError != nil { + openstackMetricLog.Error(requestError, "Unable to request Metrics from URL: %s.", a.metadata.metricsURL) + return defaultValueWhenError, requestError + } + + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + + bodyError, readError := ioutil.ReadAll(resp.Body) + + if readError != nil { + openstackMetricLog.Error(readError, "Request failed with code: %s for URL: %s", resp.StatusCode, a.metadata.metricsURL) + return defaultValueWhenError, readError + } + + return defaultValueWhenError, fmt.Errorf(string(bodyError)) + } + + m := measureResult{} + body, errConvertJSON := ioutil.ReadAll(resp.Body) + + if errConvertJSON != nil { + openstackMetricLog.Error(errConvertJSON, "Failed to convert Body format response to json") + return defaultValueWhenError, err + } + + if body == nil { + return defaultValueWhenError, nil + } + + errUnMarshall := json.Unmarshal([]byte(body), &m.measures) + + if errUnMarshall != nil { + openstackMetricLog.Error(errUnMarshall, "Failed converting json format Body structure.") + return defaultValueWhenError, errUnMarshall + } + + var targetMeasure []interface{} = nil + + if len(m.measures) > 1 { + targetMeasure = m.measures[len(m.measures)-1] + } + + if len(targetMeasure) != 3 { + openstackMetricLog.Error(fmt.Errorf("Unexpected json response"), "Unexpected json tuple, expected structure is [string, float, float].") + return defaultValueWhenError, fmt.Errorf("Unexpected json response") + } + + if val, ok := targetMeasure[2].(float64); ok { + return val, nil + } + + openstackMetricLog.Error(fmt.Errorf("Failed to convert interface type to flaot64"), "Unable to convert targetMeasure to expected format float64") + return defaultValueWhenError, fmt.Errorf("Failed to convert interface type to flaot64") + +} diff --git a/pkg/scalers/openstack_metrics_scaler_test.go b/pkg/scalers/openstack_metrics_scaler_test.go new file mode 100644 index 00000000000..437f26dde6a --- /dev/null +++ b/pkg/scalers/openstack_metrics_scaler_test.go @@ -0,0 +1,161 @@ +package scalers + +import ( + "testing" + + "github.com/kedacore/keda/v2/pkg/scalers/openstack" + "github.com/stretchr/testify/assert" +) + +type parseOpenstackMetricMetadataTestData struct { + metadata map[string]string +} + +type parseOpenstackMetricAuthMetadataTestData struct { + authMetadata map[string]string +} + +type openstackMetricScalerMetricIdentifier struct { + resolvedEnv map[string]string + metadataTestData *parseOpenstackMetricMetadataTestData + authMetadataTestData *parseOpenstackMetricAuthMetadataTestData + name string +} + +var opentsackMetricMetadataTestData = []parseOpenstackMetricMetadataTestData{ + {metadata: map[string]string{"metricsURL": "http://localhost:8041/v1/metric", "metricID": "003bb589-166d-439d-8c31-cbf098d863de", "aggregationMethod": "mean", "granularity": "300", "threshold": "1250"}}, + {metadata: map[string]string{"metricsURL": "http://localhost:8041/v1/metric", "metricID": "003bb589-166d-439d-8c31-cbf098d863de", "aggregationMethod": "sum", "granularity": "300", "threshold": "1250"}}, + {metadata: map[string]string{"metricsURL": "http://localhost:8041/v1/metric", "metricID": "003bb589-166d-439d-8c31-cbf098d863de", "aggregationMethod": "max", "granularity": "300", "threshold": "1250"}}, + {metadata: map[string]string{"metricsURL": "http://localhost:8041/v1/metric", "metricID": "003bb589-166d-439d-8c31-cbf098d863de", "aggregationMethod": "mean", "granularity": "300", "threshold": "1250", "timeout": "30"}}, +} + +var openstackMetricAuthMetadataTestData = []parseOpenstackMetricAuthMetadataTestData{ + {authMetadata: map[string]string{"userID": "my-id", "password": "my-password", "authURL": "http://localhost:5000/v3/"}}, + {authMetadata: map[string]string{"appCredentialID": "my-app-credential-id", "appCredentialSecret": "my-app-credential-secret", "authURL": "http://localhost:5000/v3/"}}, +} + +var invalidOpenstackMetricMetadaTestData = []parseOpenstackMetricMetadataTestData{ + + // Missing metrics url + {metadata: map[string]string{"metricID": "003bb589-166d-439d-8c31-cbf098d863de", "aggregationMethod": "mean", "granularity": "300", "threshold": "1250"}}, + + // Empty metrics url + {metadata: map[string]string{"metricsUrl": "", "metricID": "003bb589-166d-439d-8c31-cbf098d863de", "aggregationMethod": "mean", "granularity": "300", "threshold": "1250"}}, + + // Missing metricID + {metadata: map[string]string{"metricsUrl": "http://localhost:8041/v1/metric", "aggregationMethod": "mean", "granularity": "300", "threshold": "1250", "timeout": "30"}}, + + //Empty metricID + {metadata: map[string]string{"metricsUrl": "http://localhost:8041/v1/metric", "metricID": "", "aggregationMethod": "mean", "granularity": "300", "threshold": "1250"}}, + + //Missing aggregation method + {metadata: map[string]string{"metricsUrl": "http://localhost:8041/v1/metric", "metricID": "003bb589-166d-439d-8c31-cbf098d863de", "granularity": "300", "threshold": "1250", "timeout": "30"}}, + + //Missing granularity + {metadata: map[string]string{"metricsUrl": "http://localhost:8041/v1/metric", "metricID": "003bb589-166d-439d-8c31-cbf098d863de", "aggregationMethod": "mean", "threshold": "1250", "timeout": "30"}}, + + //Missing threshold + {metadata: map[string]string{"metricsUrl": "http://localhost:8041/v1/metric", "metricID": "003bb589-166d-439d-8c31-cbf098d863de", "aggregationMethod": "mean", "granularity": "300", "timeout": "30"}}, + + //granularity 0 + {metadata: map[string]string{"metricsURL": "http://localhost:8041/v1/metric", "metricID": "003bb589-166d-439d-8c31-cbf098d863de", "aggregationMethod": "mean", "granularity": "avc", "threshold": "1250"}}, + + //threshold 0 + {metadata: map[string]string{"metricsURL": "http://localhost:8041/v1/metric", "metricID": "003bb589-166d-439d-8c31-cbf098d863de", "aggregationMethod": "mean", "granularity": "300", "threshold": "0z"}}, +} + +var invalidOpenstackMetricAuthMetadataTestData = []parseOpenstackMetricAuthMetadataTestData{ + // Using Password method: + + // Missing userID + {authMetadata: map[string]string{"password": "my-password", "authURL": "http://localhost:5000/v3/"}}, + // Missing password + {authMetadata: map[string]string{"userID": "my-id", "authURL": "http://localhost:5000/v3/"}}, + + // Missing authURL + {authMetadata: map[string]string{"userID": "my-id", "password": "my-password"}}, + + // Using Application Credentials method: + + // Missing appCredentialID + {authMetadata: map[string]string{"appCredentialSecret": "my-app-credential-secret", "authURL": "http://localhost:5000/v3/"}}, + // Missing appCredentialSecret + {authMetadata: map[string]string{"appCredentialID": "my-app-credential-id", "authURL": "http://localhost:5000/v3/"}}, + // Missing authURL + {authMetadata: map[string]string{"appCredentialID": "my-app-credential-id", "appCredentialSecret": "my-app-credential-secret"}}, +} + +func TestOpenstackMetricsGetMetricsForSpecScaling(t *testing.T) { + // first, test cases with authentication based on password + testCases := []openstackMetricScalerMetricIdentifier{ + {nil, &opentsackMetricMetadataTestData[0], &openstackMetricAuthMetadataTestData[0], "openstack-metric-mean"}, + {nil, &opentsackMetricMetadataTestData[1], &openstackMetricAuthMetadataTestData[0], "openstack-metric-sum"}, + {nil, &opentsackMetricMetadataTestData[2], &openstackMetricAuthMetadataTestData[0], "openstack-metric-max"}, + {nil, &opentsackMetricMetadataTestData[3], &openstackMetricAuthMetadataTestData[0], "openstack-metric-mean"}, + + {nil, &opentsackMetricMetadataTestData[0], &openstackMetricAuthMetadataTestData[1], "openstack-metric-mean"}, + {nil, &opentsackMetricMetadataTestData[1], &openstackMetricAuthMetadataTestData[1], "openstack-metric-sum"}, + {nil, &opentsackMetricMetadataTestData[2], &openstackMetricAuthMetadataTestData[1], "openstack-metric-max"}, + {nil, &opentsackMetricMetadataTestData[3], &openstackMetricAuthMetadataTestData[1], "openstack-metric-mean"}, + } + + for _, testData := range testCases { + testData := testData + meta, err := parseOpenstackMetricMetadata(&ScalerConfig{ResolvedEnv: testData.resolvedEnv, TriggerMetadata: testData.metadataTestData.metadata, AuthParams: testData.authMetadataTestData.authMetadata}) + + if err != nil { + t.Fatal("Could not parse metadata from openstack metrics scaler") + } + + _, err = parseOpenstackMetricAuthenticationMetadata(&ScalerConfig{ResolvedEnv: testData.resolvedEnv, TriggerMetadata: testData.metadataTestData.metadata, AuthParams: testData.authMetadataTestData.authMetadata}) + + mockMetricsScaler := openstackMetricScaler{meta, openstack.Client{}} + metricsSpec := mockMetricsScaler.GetMetricSpecForScaling() + metricName := metricsSpec[0].External.Metric.Name + + if metricName != testData.name { + t.Error("Wrong External metric source name:", metricName) + } + } +} + +func TestOpenstackMetricsGetMetricsForSpecScalingInvalidMetaData(t *testing.T) { + testCases := []openstackMetricScalerMetricIdentifier{ + {nil, &invalidOpenstackMetricMetadaTestData[0], &openstackMetricAuthMetadataTestData[0], "Missing metrics url"}, + {nil, &invalidOpenstackMetricMetadaTestData[1], &openstackMetricAuthMetadataTestData[0], "Empty metrics url"}, + {nil, &invalidOpenstackMetricMetadaTestData[2], &openstackMetricAuthMetadataTestData[0], "Missing metricID"}, + {nil, &invalidOpenstackMetricMetadaTestData[3], &openstackMetricAuthMetadataTestData[0], "Empty metricID"}, + {nil, &invalidOpenstackMetricMetadaTestData[4], &openstackMetricAuthMetadataTestData[0], "Missing aggregation method"}, + {nil, &invalidOpenstackMetricMetadaTestData[5], &openstackMetricAuthMetadataTestData[0], "Missing granularity"}, + {nil, &invalidOpenstackMetricMetadaTestData[6], &openstackMetricAuthMetadataTestData[0], "Missing threshold"}, + {nil, &invalidOpenstackMetricMetadaTestData[7], &openstackMetricAuthMetadataTestData[0], "Missing threshold"}, + {nil, &invalidOpenstackMetricMetadaTestData[8], &openstackMetricAuthMetadataTestData[0], "Missing threshold"}, + } + + for _, testData := range testCases { + testData := testData + t.Run(testData.name, func(pt *testing.T) { + _, err := parseOpenstackMetricMetadata(&ScalerConfig{ResolvedEnv: testData.resolvedEnv, TriggerMetadata: testData.metadataTestData.metadata, AuthParams: testData.authMetadataTestData.authMetadata}) + assert.NotNil(t, err) + }) + } +} + +func TestOpenstackMetricAuthenticationInvalidAuthMetadata(t *testing.T) { + testCases := []openstackMetricScalerMetricIdentifier{ + {nil, &opentsackMetricMetadataTestData[0], &invalidOpenstackMetricAuthMetadataTestData[0], "Missing userID"}, + {nil, &opentsackMetricMetadataTestData[0], &invalidOpenstackMetricAuthMetadataTestData[1], "Missing password"}, + {nil, &opentsackMetricMetadataTestData[0], &invalidOpenstackMetricAuthMetadataTestData[2], "Missing authURL"}, + {nil, &opentsackMetricMetadataTestData[0], &invalidOpenstackMetricAuthMetadataTestData[3], "Missing appCredentialID"}, + {nil, &opentsackMetricMetadataTestData[0], &invalidOpenstackMetricAuthMetadataTestData[4], "Missing appCredentialSecret"}, + {nil, &opentsackMetricMetadataTestData[0], &invalidOpenstackMetricAuthMetadataTestData[5], "Missing authURL - application credential"}, + } + + for _, testData := range testCases { + testData := testData + t.Run(testData.name, func(ptr *testing.T) { + _, err := parseOpenstackMetricAuthenticationMetadata(&ScalerConfig{ResolvedEnv: testData.resolvedEnv, TriggerMetadata: testData.metadataTestData.metadata, AuthParams: testData.authMetadataTestData.authMetadata}) + assert.NotNil(t, err) + }) + } +} From 9b9f2a5857c4959eb6bce2dafb2930d870135274 Mon Sep 17 00:00:00 2001 From: rodolfodc Date: Wed, 17 Mar 2021 11:53:21 -0300 Subject: [PATCH 04/12] add metric scaler to sclae_handler Signed-off-by: rodolfodc --- pkg/scaling/scale_handler.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/scaling/scale_handler.go b/pkg/scaling/scale_handler.go index f038f9f577c..9d11761f2b0 100644 --- a/pkg/scaling/scale_handler.go +++ b/pkg/scaling/scale_handler.go @@ -495,6 +495,8 @@ func buildScaler(triggerType string, config *scalers.ScalerConfig) (scalers.Scal return scalers.NewMSSQLScaler(config) case "mysql": return scalers.NewMySQLScaler(config) + case "openstack-metric": + return scalers.NewOpenstackMetricScaler(config) case "openstack-swift": return scalers.NewOpenstackSwiftScaler(config) case "postgresql": From 06c33086788510533bed55db8e7d47f29a7152e6 Mon Sep 17 00:00:00 2001 From: rodolfodc Date: Fri, 19 Mar 2021 12:33:03 -0300 Subject: [PATCH 05/12] Apply golangci-lint and fix its apointments Signed-off-by: rodolfodc --- pkg/scalers/openstack_metrics_scaler.go | 66 ++++++++++---------- pkg/scalers/openstack_metrics_scaler_test.go | 16 +++-- 2 files changed, 44 insertions(+), 38 deletions(-) diff --git a/pkg/scalers/openstack_metrics_scaler.go b/pkg/scalers/openstack_metrics_scaler.go index c27e12ae876..0286a960717 100644 --- a/pkg/scalers/openstack_metrics_scaler.go +++ b/pkg/scalers/openstack_metrics_scaler.go @@ -82,7 +82,6 @@ func NewOpenstackMetricScaler(config *ScalerConfig) (Scaler, error) { if err != nil { return nil, fmt.Errorf("error getting openstack credentials for application credentials method: %s", err) } - } else { // User choose the "password" authentication method if authMetadata.userID != "" { @@ -98,6 +97,10 @@ func NewOpenstackMetricScaler(config *ScalerConfig) (Scaler, error) { if openstackMetricMetadata.metricsURL == "" { metricsClient, err = keystoneAuth.RequestClient() + if err != nil { + openstackMetricLog.Error(err, "Fail to retrieve new keystone clinet for openstack metrics scaler") + return nil, err + } } return &openstackMetricScaler{ @@ -113,22 +116,22 @@ func parseOpenstackMetricMetadata(config *ScalerConfig) (*openstackMetricMetadat if val, ok := triggerMetadata["metricsURL"]; ok && val != "" { meta.metricsURL = val } else { - openstackMetricLog.Error(fmt.Errorf("No metricsURL could be read"), "Error readig metricsURL") - return nil, fmt.Errorf("No metricsURL was declared") + openstackMetricLog.Error(fmt.Errorf("no metrics url could be read"), "Error readig metricsURL") + return nil, fmt.Errorf("no metrics url was declared") } if val, ok := triggerMetadata["metricID"]; ok && val != "" { meta.metricID = val } else { - openstackMetricLog.Error(fmt.Errorf("No metricID could be read"), "Error reading metricID") - return nil, fmt.Errorf("No metricID was declared") + openstackMetricLog.Error(fmt.Errorf("no metric id could be read"), "Error reading metricID") + return nil, fmt.Errorf("no metric id was declared") } if val, ok := triggerMetadata["aggregationMethod"]; ok && val != "" { meta.aggregationMethod = val } else { - openstackMetricLog.Error(fmt.Errorf("No aggregationMethod could be read"), "Error reading aggregation method") - return nil, fmt.Errorf("No aggregationMethod could be read") + openstackMetricLog.Error(fmt.Errorf("no aggregation method could be read"), "Error reading aggregation method") + return nil, fmt.Errorf("no aggregation method could be read") } if val, ok := triggerMetadata["granularity"]; ok && val != "" { @@ -137,26 +140,33 @@ func parseOpenstackMetricMetadata(config *ScalerConfig) (*openstackMetricMetadat openstackMetricLog.Error(err, "Error converting granulality information %s", err.Error) return nil, err } - meta.granularity = granularity - } - } else { - return nil, fmt.Errorf("No granularity found") + return nil, fmt.Errorf("no granularity found") } if val, ok := triggerMetadata["threshold"]; ok && val != "" { // converts the string to float64 but its value is convertible to float32 without changing _threshold, err := strconv.ParseFloat(val, 32) if err != nil { - openstackMetricLog.Error(err, "Error parsing openstack metric metadata", "threshold", "threshold") - return nil, fmt.Errorf("Error parsing openstack metric metadata : %s", err.Error()) + openstackMetricLog.Error(err, "error parsing openstack metric metadata", "threshold", "threshold") + return nil, fmt.Errorf("error parsing openstack metric metadata : %s", err.Error()) } meta.threshold = _threshold } + if val, ok := triggerMetadata["timeout"]; ok && val != "" { + httpClientTimeout, err := strconv.Atoi(val) + if err != nil { + return nil, fmt.Errorf("httpClientTimeout parsing error: %s", err.Error()) + } + meta.timeout = httpClientTimeout + } else { + meta.timeout = metricDefaultHTTPClientTimeout + } + return &meta, nil } @@ -178,14 +188,12 @@ func parseOpenstackMetricAuthenticationMetadata(config *ScalerConfig) (openstack } else { return authMeta, fmt.Errorf("password doesn't exist in the authParams") } - } else if val, ok := authParams["appCredentialSecretId"]; ok && val != "" { authMeta.appCredentialSecretID = val if val, ok := authParams["appCredentialSecret"]; ok && val != "" { authMeta.appCredentialSecret = val } - } else { return authMeta, fmt.Errorf("neither userID or appCredentialSecretID exist in the authParams") } @@ -197,7 +205,6 @@ func (a *openstackMetricScaler) GetMetricSpecForScaling() []v2beta2.MetricSpec { targetMetricVal := resource.NewQuantity(int64(a.metadata.threshold), resource.DecimalSI) externalMetric := &v2beta2.ExternalMetricSource{ Metric: v2beta2.MetricIdentifier{ - //aux1, err := fmt.Sprintf("%s-%s", "openstack-metric", a.authMetadata.AuthURL) Name: kedautil.NormalizeString(fmt.Sprintf("openstack-metric-%s", a.metadata.aggregationMethod)), }, Target: v2beta2.MetricTarget{ @@ -247,7 +254,6 @@ func (a *openstackMetricScaler) Close() error { // Gets measureament from API as float64, converts it to int and return the value. func (a *openstackMetricScaler) readOpenstackMetrics() (float64, error) { - var metricURL string = a.metadata.metricsURL isValid, validationError := a.metricClient.IsTokenValid() @@ -258,8 +264,7 @@ func (a *openstackMetricScaler) readOpenstackMetrics() (float64, error) { } if !isValid { - var tokenRequestError error - tokenRequestError = a.metricClient.RenewToken() + tokenRequestError := a.metricClient.RenewToken() if tokenRequestError != nil { openstackMetricLog.Error(tokenRequestError, "The token being used is invalid") return defaultValueWhenError, tokenRequestError @@ -271,8 +276,8 @@ func (a *openstackMetricScaler) readOpenstackMetrics() (float64, error) { openstackMetricsURL, err := url.Parse(metricURL) if err != nil { - openstackMetricLog.Error(err, "The metrics URL provided is invalid") - return defaultValueWhenError, fmt.Errorf("The metrics URL is invalid: %s", err.Error()) + openstackMetricLog.Error(err, "metric url provided is invalid") + return defaultValueWhenError, fmt.Errorf("metric url is invalid: %s", err.Error()) } openstackMetricsURL.Path = path.Join(openstackMetricsURL.Path, a.metadata.metricID+"/measures") @@ -280,8 +285,8 @@ func (a *openstackMetricScaler) readOpenstackMetrics() (float64, error) { granularity := 2 // We start with granularity with value 2 cause gnocchi APIm which is used by openstack, consider a time window, and we want to get the last value if a.metadata.granularity <= 0 { - openstackMetricLog.Error(fmt.Errorf("Granularity Value is less than 1"), "Minimum accepatble value expected for ganularity is 1.") - return defaultValueWhenError, fmt.Errorf("Granularity Value is less than 1") + openstackMetricLog.Error(fmt.Errorf("granularity value is less than 1"), "Minimum accepatble value expected for ganularity is 1.") + return defaultValueWhenError, fmt.Errorf("granularity value is less than 1") } if (a.metadata.granularity / 60) > 1 { @@ -294,7 +299,7 @@ func (a *openstackMetricScaler) readOpenstackMetrics() (float64, error) { queryParameter.Set("aggregation", a.metadata.aggregationMethod) currTimeWithWindow := time.Now().Add(time.Minute + time.Duration(granularity)).Format(time.RFC3339) - queryParameter.Set("start", string(currTimeWithWindow)[:17]+"00") + queryParameter.Set("start", currTimeWithWindow[:17]+"00") openstackMetricsURL.RawQuery = queryParameter.Encode() @@ -312,9 +317,7 @@ func (a *openstackMetricScaler) readOpenstackMetrics() (float64, error) { } defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - bodyError, readError := ioutil.ReadAll(resp.Body) if readError != nil { @@ -337,7 +340,7 @@ func (a *openstackMetricScaler) readOpenstackMetrics() (float64, error) { return defaultValueWhenError, nil } - errUnMarshall := json.Unmarshal([]byte(body), &m.measures) + errUnMarshall := json.Unmarshal(body, &m.measures) if errUnMarshall != nil { openstackMetricLog.Error(errUnMarshall, "Failed converting json format Body structure.") @@ -351,15 +354,14 @@ func (a *openstackMetricScaler) readOpenstackMetrics() (float64, error) { } if len(targetMeasure) != 3 { - openstackMetricLog.Error(fmt.Errorf("Unexpected json response"), "Unexpected json tuple, expected structure is [string, float, float].") - return defaultValueWhenError, fmt.Errorf("Unexpected json response") + openstackMetricLog.Error(fmt.Errorf("unexpected json response"), "unexpected json tuple, expected structure is [string, float, float].") + return defaultValueWhenError, fmt.Errorf("unexpected json response") } if val, ok := targetMeasure[2].(float64); ok { return val, nil } - openstackMetricLog.Error(fmt.Errorf("Failed to convert interface type to flaot64"), "Unable to convert targetMeasure to expected format float64") - return defaultValueWhenError, fmt.Errorf("Failed to convert interface type to flaot64") - + openstackMetricLog.Error(fmt.Errorf("failed to convert interface type to float64"), "unable to convert target measure to expected format float64") + return defaultValueWhenError, fmt.Errorf("failed to convert interface type to float64") } diff --git a/pkg/scalers/openstack_metrics_scaler_test.go b/pkg/scalers/openstack_metrics_scaler_test.go index 437f26dde6a..558e88cf640 100644 --- a/pkg/scalers/openstack_metrics_scaler_test.go +++ b/pkg/scalers/openstack_metrics_scaler_test.go @@ -45,22 +45,22 @@ var invalidOpenstackMetricMetadaTestData = []parseOpenstackMetricMetadataTestDat // Missing metricID {metadata: map[string]string{"metricsUrl": "http://localhost:8041/v1/metric", "aggregationMethod": "mean", "granularity": "300", "threshold": "1250", "timeout": "30"}}, - //Empty metricID + // Empty metricID {metadata: map[string]string{"metricsUrl": "http://localhost:8041/v1/metric", "metricID": "", "aggregationMethod": "mean", "granularity": "300", "threshold": "1250"}}, - //Missing aggregation method + // Missing aggregation method {metadata: map[string]string{"metricsUrl": "http://localhost:8041/v1/metric", "metricID": "003bb589-166d-439d-8c31-cbf098d863de", "granularity": "300", "threshold": "1250", "timeout": "30"}}, - //Missing granularity + // Missing granularity {metadata: map[string]string{"metricsUrl": "http://localhost:8041/v1/metric", "metricID": "003bb589-166d-439d-8c31-cbf098d863de", "aggregationMethod": "mean", "threshold": "1250", "timeout": "30"}}, - //Missing threshold + // Missing threshold {metadata: map[string]string{"metricsUrl": "http://localhost:8041/v1/metric", "metricID": "003bb589-166d-439d-8c31-cbf098d863de", "aggregationMethod": "mean", "granularity": "300", "timeout": "30"}}, - //granularity 0 + // granularity 0 {metadata: map[string]string{"metricsURL": "http://localhost:8041/v1/metric", "metricID": "003bb589-166d-439d-8c31-cbf098d863de", "aggregationMethod": "mean", "granularity": "avc", "threshold": "1250"}}, - //threshold 0 + // threshold 0 {metadata: map[string]string{"metricsURL": "http://localhost:8041/v1/metric", "metricID": "003bb589-166d-439d-8c31-cbf098d863de", "aggregationMethod": "mean", "granularity": "300", "threshold": "0z"}}, } @@ -109,6 +109,10 @@ func TestOpenstackMetricsGetMetricsForSpecScaling(t *testing.T) { _, err = parseOpenstackMetricAuthenticationMetadata(&ScalerConfig{ResolvedEnv: testData.resolvedEnv, TriggerMetadata: testData.metadataTestData.metadata, AuthParams: testData.authMetadataTestData.authMetadata}) + if err != nil { + t.Error(err, "parseOpenstackMetricAuthenticationMetadata method failed") + } + mockMetricsScaler := openstackMetricScaler{meta, openstack.Client{}} metricsSpec := mockMetricsScaler.GetMetricSpecForScaling() metricName := metricsSpec[0].External.Metric.Name From b829bdff6ad17db77800c7c516354f47aa654eab Mon Sep 17 00:00:00 2001 From: rodolfodc Date: Fri, 19 Mar 2021 13:43:42 -0300 Subject: [PATCH 06/12] Update tests to new openstack keystone api Signed-off-by: rodolfodc --- pkg/scalers/openstack_metrics_scaler.go | 6 +----- pkg/scalers/openstack_metrics_scaler_test.go | 8 ++++---- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/pkg/scalers/openstack_metrics_scaler.go b/pkg/scalers/openstack_metrics_scaler.go index 0286a960717..7becadb10e2 100644 --- a/pkg/scalers/openstack_metrics_scaler.go +++ b/pkg/scalers/openstack_metrics_scaler.go @@ -188,12 +188,8 @@ func parseOpenstackMetricAuthenticationMetadata(config *ScalerConfig) (openstack } else { return authMeta, fmt.Errorf("password doesn't exist in the authParams") } - } else if val, ok := authParams["appCredentialSecretId"]; ok && val != "" { + } else if val, ok := authParams["appCredentialSecret"]; ok && val != "" { authMeta.appCredentialSecretID = val - - if val, ok := authParams["appCredentialSecret"]; ok && val != "" { - authMeta.appCredentialSecret = val - } } else { return authMeta, fmt.Errorf("neither userID or appCredentialSecretID exist in the authParams") } diff --git a/pkg/scalers/openstack_metrics_scaler_test.go b/pkg/scalers/openstack_metrics_scaler_test.go index 558e88cf640..b886a09f72e 100644 --- a/pkg/scalers/openstack_metrics_scaler_test.go +++ b/pkg/scalers/openstack_metrics_scaler_test.go @@ -77,8 +77,8 @@ var invalidOpenstackMetricAuthMetadataTestData = []parseOpenstackMetricAuthMetad // Using Application Credentials method: - // Missing appCredentialID - {authMetadata: map[string]string{"appCredentialSecret": "my-app-credential-secret", "authURL": "http://localhost:5000/v3/"}}, + // Missing appCredentialID and appCredentialSecret + {authMetadata: map[string]string{"authURL": "http://localhost:5000/v3/"}}, // Missing appCredentialSecret {authMetadata: map[string]string{"appCredentialID": "my-app-credential-id", "authURL": "http://localhost:5000/v3/"}}, // Missing authURL @@ -110,7 +110,7 @@ func TestOpenstackMetricsGetMetricsForSpecScaling(t *testing.T) { _, err = parseOpenstackMetricAuthenticationMetadata(&ScalerConfig{ResolvedEnv: testData.resolvedEnv, TriggerMetadata: testData.metadataTestData.metadata, AuthParams: testData.authMetadataTestData.authMetadata}) if err != nil { - t.Error(err, "parseOpenstackMetricAuthenticationMetadata method failed") + t.Fatal("could not parse openstack metric authentication metadata") } mockMetricsScaler := openstackMetricScaler{meta, openstack.Client{}} @@ -150,7 +150,7 @@ func TestOpenstackMetricAuthenticationInvalidAuthMetadata(t *testing.T) { {nil, &opentsackMetricMetadataTestData[0], &invalidOpenstackMetricAuthMetadataTestData[0], "Missing userID"}, {nil, &opentsackMetricMetadataTestData[0], &invalidOpenstackMetricAuthMetadataTestData[1], "Missing password"}, {nil, &opentsackMetricMetadataTestData[0], &invalidOpenstackMetricAuthMetadataTestData[2], "Missing authURL"}, - {nil, &opentsackMetricMetadataTestData[0], &invalidOpenstackMetricAuthMetadataTestData[3], "Missing appCredentialID"}, + {nil, &opentsackMetricMetadataTestData[0], &invalidOpenstackMetricAuthMetadataTestData[3], "Missing appCredentialID and appCredentialSecret"}, {nil, &opentsackMetricMetadataTestData[0], &invalidOpenstackMetricAuthMetadataTestData[4], "Missing appCredentialSecret"}, {nil, &opentsackMetricMetadataTestData[0], &invalidOpenstackMetricAuthMetadataTestData[5], "Missing authURL - application credential"}, } From 6a04cd9ca4142faffa9256a96a88f5fa95a0ca28 Mon Sep 17 00:00:00 2001 From: rodolfodc Date: Mon, 22 Mar 2021 10:46:55 -0300 Subject: [PATCH 07/12] fix time window calculation and improve request time window query Signed-off-by: rodolfodc --- pkg/scalers/openstack_metrics_scaler.go | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/pkg/scalers/openstack_metrics_scaler.go b/pkg/scalers/openstack_metrics_scaler.go index 7becadb10e2..b7b2cb929e4 100644 --- a/pkg/scalers/openstack_metrics_scaler.go +++ b/pkg/scalers/openstack_metrics_scaler.go @@ -278,23 +278,28 @@ func (a *openstackMetricScaler) readOpenstackMetrics() (float64, error) { openstackMetricsURL.Path = path.Join(openstackMetricsURL.Path, a.metadata.metricID+"/measures") queryParameter := openstackMetricsURL.Query() - granularity := 2 // We start with granularity with value 2 cause gnocchi APIm which is used by openstack, consider a time window, and we want to get the last value + granularity := 0 // We start with granularity with value 2 cause gnocchi APIm which is used by openstack, consider a time window, and we want to get the last value if a.metadata.granularity <= 0 { openstackMetricLog.Error(fmt.Errorf("granularity value is less than 1"), "Minimum accepatble value expected for ganularity is 1.") return defaultValueWhenError, fmt.Errorf("granularity value is less than 1") } - if (a.metadata.granularity / 60) > 1 { - granularity = (a.metadata.granularity / 60) + if (a.metadata.granularity / 60) > 0 { + granularity = (a.metadata.granularity / 60) - 1 } - granularity-- - queryParameter.Set("granularity", strconv.Itoa(a.metadata.granularity)) queryParameter.Set("aggregation", a.metadata.aggregationMethod) - currTimeWithWindow := time.Now().Add(time.Minute + time.Duration(granularity)).Format(time.RFC3339) + var currTimeWithWindow string + + if granularity > 0 { + currTimeWithWindow = time.Now().Add(time.Minute * time.Duration(granularity)).Format(time.RFC3339) + } else { + currTimeWithWindow = time.Now().Format(time.RFC3339) + } + queryParameter.Set("start", currTimeWithWindow[:17]+"00") openstackMetricsURL.RawQuery = queryParameter.Encode() From cd88a04193722755ebe492054f93e138956e54f3 Mon Sep 17 00:00:00 2001 From: rodolfodc Date: Mon, 22 Mar 2021 17:35:18 -0300 Subject: [PATCH 08/12] Fix bugs during e2e test Signed-off-by: rodolfodc --- pkg/scalers/openstack_metrics_scaler.go | 30 ++++++++++++------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/pkg/scalers/openstack_metrics_scaler.go b/pkg/scalers/openstack_metrics_scaler.go index b7b2cb929e4..f708447eeaf 100644 --- a/pkg/scalers/openstack_metrics_scaler.go +++ b/pkg/scalers/openstack_metrics_scaler.go @@ -95,12 +95,10 @@ func NewOpenstackMetricScaler(config *ScalerConfig) (Scaler, error) { } } - if openstackMetricMetadata.metricsURL == "" { - metricsClient, err = keystoneAuth.RequestClient() - if err != nil { - openstackMetricLog.Error(err, "Fail to retrieve new keystone clinet for openstack metrics scaler") - return nil, err - } + metricsClient, err = keystoneAuth.RequestClient() + if err != nil { + openstackMetricLog.Error(err, "Fail to retrieve new keystone clinet for openstack metrics scaler") + return nil, err } return &openstackMetricScaler{ @@ -135,13 +133,12 @@ func parseOpenstackMetricMetadata(config *ScalerConfig) (*openstackMetricMetadat } if val, ok := triggerMetadata["granularity"]; ok && val != "" { - if granularity, err := strconv.Atoi(val); err != nil { - if err != nil { - openstackMetricLog.Error(err, "Error converting granulality information %s", err.Error) - return nil, err - } - meta.granularity = granularity + granularity, err := strconv.Atoi(val) + if err != nil { + openstackMetricLog.Error(err, "Error converting granulality information %s", err.Error) + return nil, err } + meta.granularity = granularity } else { return nil, fmt.Errorf("no granularity found") } @@ -348,14 +345,17 @@ func (a *openstackMetricScaler) readOpenstackMetrics() (float64, error) { return defaultValueWhenError, errUnMarshall } - var targetMeasure []interface{} = nil + var targetMeasure []interface{} - if len(m.measures) > 1 { + if len(m.measures) > 0 { targetMeasure = m.measures[len(m.measures)-1] + } else { + openstackMetricLog.Info("No measure was returned from openstack") + return defaultValueWhenError, nil } if len(targetMeasure) != 3 { - openstackMetricLog.Error(fmt.Errorf("unexpected json response"), "unexpected json tuple, expected structure is [string, float, float].") + openstackMetricLog.Error(fmt.Errorf("unexpected json response"), "unexpected json tuple, expected structure is [string, float, float]") return defaultValueWhenError, fmt.Errorf("unexpected json response") } From 97d58af112e14984b290149bdb946f146aa1225f Mon Sep 17 00:00:00 2001 From: rodolfodc Date: Wed, 24 Mar 2021 09:46:50 -0300 Subject: [PATCH 09/12] update changelog.md Signed-off-by: rodolfodc --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ca9127cd5e..f735176a760 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,8 +41,12 @@ - Emit Kubernetes Events on KEDA events ([#1523](https://github.com/kedacore/keda/pull/1523) | [#1647](https://github.com/kedacore/keda/pull/1647)) - Support Quantities in Metrics API scaler ([#1667](https://github.com/kedacore/keda/issues/1667)) - Add Microsoft SQL Server (MSSQL) scaler ([#674](https://github.com/kedacore/keda/issues/674) | [docs](https://keda.sh/docs/2.2/scalers/mssql/)) +<<<<<<< HEAD - Add `publishRate` trigger to RabbitMQ scaler ([#1653](https://github.com/kedacore/keda/pull/1653)) - ScaledJob: support metadata labels in Job template ([#1686](https://github.com/kedacore/keda/pull/1686)) +======= +- Add Openstack Metrics Scaler ([#1372](https://github.com/kedacore/keda/issues/1382)) +>>>>>>> update changelog.md ### Improvements From 4cb75403aaac6f50179366cb8b480dd13849de93 Mon Sep 17 00:00:00 2001 From: rodolfodc Date: Wed, 24 Mar 2021 18:18:43 -0300 Subject: [PATCH 10/12] Update CHANLOG.md file after rebasement to fix conflicts Signed-off-by: rodolfodc --- CHANGELOG.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f735176a760..ba3a25f870c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,12 +41,9 @@ - Emit Kubernetes Events on KEDA events ([#1523](https://github.com/kedacore/keda/pull/1523) | [#1647](https://github.com/kedacore/keda/pull/1647)) - Support Quantities in Metrics API scaler ([#1667](https://github.com/kedacore/keda/issues/1667)) - Add Microsoft SQL Server (MSSQL) scaler ([#674](https://github.com/kedacore/keda/issues/674) | [docs](https://keda.sh/docs/2.2/scalers/mssql/)) -<<<<<<< HEAD - Add `publishRate` trigger to RabbitMQ scaler ([#1653](https://github.com/kedacore/keda/pull/1653)) - ScaledJob: support metadata labels in Job template ([#1686](https://github.com/kedacore/keda/pull/1686)) -======= - Add Openstack Metrics Scaler ([#1372](https://github.com/kedacore/keda/issues/1382)) ->>>>>>> update changelog.md ### Improvements From 5121aa152504e137e800ae1a3812d5518a282cf3 Mon Sep 17 00:00:00 2001 From: rodolfodc Date: Fri, 26 Mar 2021 14:35:23 -0300 Subject: [PATCH 11/12] improve metric nomination on keda and update changelog.md Signed-off-by: rodolfodc --- CHANGELOG.md | 3 ++- pkg/scalers/openstack_metrics_scaler.go | 2 +- pkg/scalers/openstack_metrics_scaler_test.go | 18 +++++++++--------- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba3a25f870c..5192a741520 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ ## Unreleased +- Add Openstack Metrics Scaler ([#1382](https://github.com/kedacore/keda/issues/1382)) + ### New - Added basic, tls and bearer authentication support to the Prometheus scaler [#1559](https://github.com/kedacore/keda/issues/1559) @@ -43,7 +45,6 @@ - Add Microsoft SQL Server (MSSQL) scaler ([#674](https://github.com/kedacore/keda/issues/674) | [docs](https://keda.sh/docs/2.2/scalers/mssql/)) - Add `publishRate` trigger to RabbitMQ scaler ([#1653](https://github.com/kedacore/keda/pull/1653)) - ScaledJob: support metadata labels in Job template ([#1686](https://github.com/kedacore/keda/pull/1686)) -- Add Openstack Metrics Scaler ([#1372](https://github.com/kedacore/keda/issues/1382)) ### Improvements diff --git a/pkg/scalers/openstack_metrics_scaler.go b/pkg/scalers/openstack_metrics_scaler.go index f708447eeaf..4595452511d 100644 --- a/pkg/scalers/openstack_metrics_scaler.go +++ b/pkg/scalers/openstack_metrics_scaler.go @@ -198,7 +198,7 @@ func (a *openstackMetricScaler) GetMetricSpecForScaling() []v2beta2.MetricSpec { targetMetricVal := resource.NewQuantity(int64(a.metadata.threshold), resource.DecimalSI) externalMetric := &v2beta2.ExternalMetricSource{ Metric: v2beta2.MetricIdentifier{ - Name: kedautil.NormalizeString(fmt.Sprintf("openstack-metric-%s", a.metadata.aggregationMethod)), + Name: kedautil.NormalizeString(fmt.Sprintf("openstack-metric-%s-%s-%s", a.metadata.metricID, strconv.FormatFloat(a.metadata.threshold, 'f', 0, 32), a.metadata.aggregationMethod)), }, Target: v2beta2.MetricTarget{ Type: v2beta2.AverageValueMetricType, diff --git a/pkg/scalers/openstack_metrics_scaler_test.go b/pkg/scalers/openstack_metrics_scaler_test.go index b886a09f72e..36eefba98f1 100644 --- a/pkg/scalers/openstack_metrics_scaler_test.go +++ b/pkg/scalers/openstack_metrics_scaler_test.go @@ -88,15 +88,15 @@ var invalidOpenstackMetricAuthMetadataTestData = []parseOpenstackMetricAuthMetad func TestOpenstackMetricsGetMetricsForSpecScaling(t *testing.T) { // first, test cases with authentication based on password testCases := []openstackMetricScalerMetricIdentifier{ - {nil, &opentsackMetricMetadataTestData[0], &openstackMetricAuthMetadataTestData[0], "openstack-metric-mean"}, - {nil, &opentsackMetricMetadataTestData[1], &openstackMetricAuthMetadataTestData[0], "openstack-metric-sum"}, - {nil, &opentsackMetricMetadataTestData[2], &openstackMetricAuthMetadataTestData[0], "openstack-metric-max"}, - {nil, &opentsackMetricMetadataTestData[3], &openstackMetricAuthMetadataTestData[0], "openstack-metric-mean"}, - - {nil, &opentsackMetricMetadataTestData[0], &openstackMetricAuthMetadataTestData[1], "openstack-metric-mean"}, - {nil, &opentsackMetricMetadataTestData[1], &openstackMetricAuthMetadataTestData[1], "openstack-metric-sum"}, - {nil, &opentsackMetricMetadataTestData[2], &openstackMetricAuthMetadataTestData[1], "openstack-metric-max"}, - {nil, &opentsackMetricMetadataTestData[3], &openstackMetricAuthMetadataTestData[1], "openstack-metric-mean"}, + {nil, &opentsackMetricMetadataTestData[0], &openstackMetricAuthMetadataTestData[0], "openstack-metric-003bb589-166d-439d-8c31-cbf098d863de-1250-mean"}, + {nil, &opentsackMetricMetadataTestData[1], &openstackMetricAuthMetadataTestData[0], "openstack-metric-003bb589-166d-439d-8c31-cbf098d863de-1250-sum"}, + {nil, &opentsackMetricMetadataTestData[2], &openstackMetricAuthMetadataTestData[0], "openstack-metric-003bb589-166d-439d-8c31-cbf098d863de-1250-max"}, + {nil, &opentsackMetricMetadataTestData[3], &openstackMetricAuthMetadataTestData[0], "openstack-metric-003bb589-166d-439d-8c31-cbf098d863de-1250-mean"}, + + {nil, &opentsackMetricMetadataTestData[0], &openstackMetricAuthMetadataTestData[1], "openstack-metric-003bb589-166d-439d-8c31-cbf098d863de-1250-mean"}, + {nil, &opentsackMetricMetadataTestData[1], &openstackMetricAuthMetadataTestData[1], "openstack-metric-003bb589-166d-439d-8c31-cbf098d863de-1250-sum"}, + {nil, &opentsackMetricMetadataTestData[2], &openstackMetricAuthMetadataTestData[1], "openstack-metric-003bb589-166d-439d-8c31-cbf098d863de-1250-max"}, + {nil, &opentsackMetricMetadataTestData[3], &openstackMetricAuthMetadataTestData[1], "openstack-metric-003bb589-166d-439d-8c31-cbf098d863de-1250-mean"}, } for _, testData := range testCases { From f9cf2da0b9fa79efcb8cb540cf9a558e264589a4 Mon Sep 17 00:00:00 2001 From: rodolfodc Date: Fri, 26 Mar 2021 14:42:37 -0300 Subject: [PATCH 12/12] update OpenStack naming on changelog Signed-off-by: rodolfodc --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5192a741520..a0d26e135fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,7 @@ ## Unreleased -- Add Openstack Metrics Scaler ([#1382](https://github.com/kedacore/keda/issues/1382)) +- Add OpenStack Metrics Scaler ([#1382](https://github.com/kedacore/keda/issues/1382)) ### New