diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ff86045d42..685f30c56a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ ### New - TODO ([#XXX](https://github.com/kedacore/keda/issues/XXX)) +- Support Quantities in metrics API scaler ([#1667](https://github.com/kedacore/keda/issues/1667)) - Emit Kubernetes Events on KEDA events ([#1523](https://github.com/kedacore/keda/pull/1523)) - Add Microsoft SQL Server (MSSQL) scaler ([#674](https://github.com/kedacore/keda/issues/674) | [docs](https://keda.sh/docs/2.2/scalers/mssql/)) diff --git a/pkg/scalers/metrics_api_scaler.go b/pkg/scalers/metrics_api_scaler.go index 6a035ef67c4..47e66e6d7ad 100644 --- a/pkg/scalers/metrics_api_scaler.go +++ b/pkg/scalers/metrics_api_scaler.go @@ -172,39 +172,46 @@ func parseMetricsAPIMetadata(config *ScalerConfig) (*metricsAPIScalerMetadata, e } // GetValueFromResponse uses provided valueLocation to access the numeric value in provided body -func GetValueFromResponse(body []byte, valueLocation string) (int64, error) { +func GetValueFromResponse(body []byte, valueLocation string) (*resource.Quantity, error) { r := gjson.GetBytes(body, valueLocation) + errorMsg := "valueLocation must point to value of type number or a string representing a Quanitity got: '%s'" + if r.Type == gjson.String { + q, err := resource.ParseQuantity(r.String()) + if err != nil { + return nil, fmt.Errorf(errorMsg, r.String()) + } + return &q, nil + } if r.Type != gjson.Number { - msg := fmt.Sprintf("valueLocation must point to value of type number got: %s", r.Type.String()) - return 0, errors.New(msg) + return nil, fmt.Errorf(errorMsg, r.Type.String()) } - return int64(r.Num), nil + return resource.NewQuantity(int64(r.Num), resource.DecimalSI), nil } -func (s *metricsAPIScaler) getMetricValue() (int64, error) { +func (s *metricsAPIScaler) getMetricValue() (*resource.Quantity, error) { request, err := getMetricAPIServerRequest(s.metadata) if err != nil { - return 0, err + return nil, err } r, err := s.client.Do(request) if err != nil { - return 0, err + return nil, err } if r.StatusCode != http.StatusOK { msg := fmt.Sprintf("api returned %d", r.StatusCode) - return 0, errors.New(msg) + return nil, errors.New(msg) } defer r.Body.Close() b, err := ioutil.ReadAll(r.Body) if err != nil { - return 0, err + return nil, err } v, err := GetValueFromResponse(b, s.metadata.valueLocation) if err != nil { - return 0, err + return nil, err } return v, nil } @@ -222,7 +229,7 @@ func (s *metricsAPIScaler) IsActive(ctx context.Context) (bool, error) { return false, err } - return v > 0.0, nil + return v.AsApproximateFloat64() > 0.0, nil } // GetMetricSpecForScaling returns the MetricSpec for the Horizontal Pod Autoscaler @@ -253,7 +260,7 @@ func (s *metricsAPIScaler) GetMetrics(ctx context.Context, metricName string, me metric := external_metrics.ExternalMetricValue{ MetricName: metricName, - Value: *resource.NewQuantity(v, resource.DecimalSI), + Value: *v, Timestamp: metav1.Now(), } diff --git a/pkg/scalers/metrics_api_scaler_test.go b/pkg/scalers/metrics_api_scaler_test.go index a92277b3486..98906fed3ac 100644 --- a/pkg/scalers/metrics_api_scaler_test.go +++ b/pkg/scalers/metrics_api_scaler_test.go @@ -68,21 +68,42 @@ func TestParseMetricsAPIMetadata(t *testing.T) { } func TestGetValueFromResponse(t *testing.T) { - d := []byte(`{"components":[{"id": "82328e93e", "tasks": 32}],"count":2.43}`) + d := []byte(`{"components":[{"id": "82328e93e", "tasks": 32, "str": "64", "k":"1k","wrong":"NaN"}],"count":2.43}`) v, err := GetValueFromResponse(d, "components.0.tasks") if err != nil { t.Error("Expected success but got error", err) } - if v != 32 { - t.Errorf("Expected %d got %d", 32, v) + if v.CmpInt64(32) != 0 { + t.Errorf("Expected %d got %d", 32, v.AsDec()) } v, err = GetValueFromResponse(d, "count") if err != nil { t.Error("Expected success but got error", err) } - if v != 2 { - t.Errorf("Expected %d got %d", 2, v) + if v.CmpInt64(2) != 0 { + t.Errorf("Expected %d got %d", 2, v.AsDec()) + } + + v, err = GetValueFromResponse(d, "components.0.str") + if err != nil { + t.Error("Expected success but got error", err) + } + if v.CmpInt64(64) != 0 { + t.Errorf("Expected %d got %d", 64, v.AsDec()) + } + + v, err = GetValueFromResponse(d, "components.0.k") + if err != nil { + t.Error("Expected success but got error", err) + } + if v.CmpInt64(1000) != 0 { + t.Errorf("Expected %d got %d", 1000, v.AsDec()) + } + + _, err = GetValueFromResponse(d, "components.0.wrong") + if err == nil { + t.Error("Expected error but got success", err) } }