Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add activationTarget for formulas #4998

Merged
merged 26 commits into from
Sep 26, 2023
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ To learn more about active deprecations, we recommend checking [GitHub Discussio

Here is an overview of all new **experimental** features:

- **General**: Add support for formula based evaluation of metric values ([#2440](https://github.com/kedacore/keda/issues/2440))
- **General**: Add support for formula based evaluation of metric values ([#2440](https://github.com/kedacore/keda/issues/2440)|[#4998](https://github.com/kedacore/keda/pull/4998))

### Improvements
- **General**: Add apiserver Prometheus metrics to KEDA Metric Server ([#4460](https://github.com/kedacore/keda/issues/4460))
Expand Down
4 changes: 4 additions & 0 deletions apis/keda/v1alpha1/scaledobject_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,10 @@ type AdvancedConfig struct {
type ScalingModifiers struct {
Formula string `json:"formula,omitempty"`
Target string `json:"target,omitempty"`
// +optional
ActivationTarget string `json:"activationTarget,omitempty"`
// +optional
MetricType autoscalingv2.MetricTargetType `json:"metricType,omitempty"`
}

// HorizontalPodAutoscalerConfig specifies horizontal scale config
Expand Down
26 changes: 1 addition & 25 deletions apis/keda/v1alpha1/scaledobject_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -392,31 +392,7 @@ func validateScalingModifiersTarget(so *ScaledObject) error {
return fmt.Errorf("error converting target for scalingModifiers (string->float) to valid target: %w", err)
}

// if target is given, composite-scaler will be passed to HPA -> all types
// need to be the same - make sure all metrics are of the same metricTargetType

var trigType autoscalingv2.MetricTargetType

// gauron99: possible TODO: more sofisticated check for trigger could be used here
// as well if solution is found (check just the right triggers that are used)
for _, trig := range so.Spec.Triggers {
if trig.Type == cpuString || trig.Type == memoryString || trig.Name == "" {
continue
}
var current autoscalingv2.MetricTargetType
if trig.MetricType == "" {
current = autoscalingv2.AverageValueMetricType // default is AverageValue
} else {
current = trig.MetricType
}
if trigType == "" {
trigType = current
} else if trigType != current {
err := fmt.Errorf("error trigger types are not the same for composite scaler: %s & %s", trigType, current)
return err
}
}
if trigType == autoscalingv2.UtilizationMetricType {
if so.Spec.Advanced.ScalingModifiers.MetricType == autoscalingv2.UtilizationMetricType {
err := fmt.Errorf("error trigger type is Utilization, but it needs to be AverageValue or Value for external metrics")
return err
}
Expand Down
5 changes: 4 additions & 1 deletion apis/keda/v1alpha1/scaledobject_webhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package v1alpha1

import (
"context"
"time"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
Expand Down Expand Up @@ -416,7 +417,9 @@ var _ = It("shouldn't create so when stabilizationWindowSeconds exceeds 3600", f

Eventually(func() error {
return k8sClient.Create(context.Background(), so)
}).Should(HaveOccurred())
}).
WithTimeout(5 * time.Second).
Should(HaveOccurred())
})

var _ = It("should validate the so creation with ScalingModifiers.Formula", func() {
Expand Down
7 changes: 7 additions & 0 deletions config/crd/bases/keda.sh_scaledobjects.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,15 @@ spec:
description: ScalingModifiers describes advanced scaling logic
options like formula
properties:
activationTarget:
type: string
formula:
type: string
metricType:
description: MetricTargetType specifies the type of metric
being targeted, and should be either "Value", "AverageValue",
or "Utilization"
type: string
target:
type: string
type: object
Expand Down
23 changes: 8 additions & 15 deletions controllers/keda/hpa.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,19 +258,12 @@ func (r *ScaledObjectReconciler) getScaledObjectMetricSpecs(ctx context.Context,
validNumTarget, _ := strconv.ParseFloat(scaledObject.Spec.Advanced.ScalingModifiers.Target, 64)

// check & get metric specs type
var validMetricType autoscalingv2.MetricTargetType
for _, metric := range metricSpecs {
if metric.External == nil {
continue
}
if validMetricType == "" {
validMetricType = metric.External.Target.Type
} else if metric.External.Target.Type != validMetricType {
err := fmt.Errorf("error metric target type is not the same for composite scaler: %s & %s", validMetricType, metric.External.Target.Type)
return nil, err
}
metricType := autoscalingv2.AverageValueMetricType
if scaledObject.Spec.Advanced.ScalingModifiers.MetricType != "" {
metricType = scaledObject.Spec.Advanced.ScalingModifiers.MetricType
}
if validMetricType == autoscalingv2.UtilizationMetricType {

if metricType == autoscalingv2.UtilizationMetricType {
err := fmt.Errorf("error metric target type is Utilization, but it needs to be AverageValue or Value for external metrics")
return nil, err
}
Expand All @@ -280,11 +273,11 @@ func (r *ScaledObjectReconciler) getScaledObjectMetricSpecs(ctx context.Context,
quan := resource.NewMilliQuantity(int64(validNumTarget*1000), resource.DecimalSI)

correctHpaTarget := autoscalingv2.MetricTarget{
Type: validMetricType,
Type: metricType,
}
if validMetricType == autoscalingv2.AverageValueMetricType {
if metricType == autoscalingv2.AverageValueMetricType {
correctHpaTarget.AverageValue = quan
} else if validMetricType == autoscalingv2.ValueMetricType {
} else if metricType == autoscalingv2.ValueMetricType {
correctHpaTarget.Value = quan
}
compMetricName := kedav1alpha1.CompositeMetricName
Expand Down
3 changes: 3 additions & 0 deletions pkg/eventreason/eventreason.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ const (
// KEDAScalerFailed is for event when a scaler fails for a ScaledJob or a ScaledObject
KEDAScalerFailed = "KEDAScalerFailed"

// KEDAMetricSourceFailed is for event when a scaler fails as metric source for custom formula
KEDAMetricSourceFailed = "KEDAMetricSourceFailed"

// KEDAScaleTargetActivated is for event when the scale target of ScaledObject was activated
KEDAScaleTargetActivated = "KEDAScaleTargetActivated"

Expand Down
6 changes: 5 additions & 1 deletion pkg/scalers/arangodb_scaler.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,11 @@ func parseArangoDBMetadata(config *ScalerConfig) (*arangoDBMetadata, error) {
}
meta.queryValue = queryValue
} else {
return nil, fmt.Errorf("no queryValue given")
if config.AsMetricSource {
meta.queryValue = 0
} else {
return nil, fmt.Errorf("no queryValue given")
}
}

meta.activationQueryValue = 0
Expand Down
6 changes: 5 additions & 1 deletion pkg/scalers/aws_dynamodb_scaler.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,11 @@ func parseAwsDynamoDBMetadata(config *ScalerConfig) (*awsDynamoDBMetadata, error

meta.targetValue = n
} else {
return nil, ErrAwsDynamoNoTargetValue
if config.AsMetricSource {
meta.targetValue = 0
} else {
return nil, ErrAwsDynamoNoTargetValue
}
}

if val, ok := config.TriggerMetadata["activationTargetValue"]; ok && val != "" {
Expand Down
6 changes: 5 additions & 1 deletion pkg/scalers/azure_app_insights_scaler.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,11 @@ func parseAzureAppInsightsMetadata(config *ScalerConfig, logger logr.Logger) (*a

val, err := getParameterFromConfig(config, azureAppInsightsTargetValueName, false)
if err != nil {
return nil, err
if config.AsMetricSource {
meta.targetValue = 0
} else {
return nil, err
}
}
targetValue, err := strconv.ParseFloat(val, 64)
if err != nil {
Expand Down
6 changes: 5 additions & 1 deletion pkg/scalers/azure_log_analytics_scaler.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,11 @@ func parseAzureLogAnalyticsMetadata(config *ScalerConfig) (*azureLogAnalyticsMet
// Getting threshold, observe that we don't check AuthParams for threshold
val, err := getParameterFromConfig(config, "threshold", false)
if err != nil {
return nil, err
if config.AsMetricSource {
val = "0"
} else {
return nil, err
}
}
threshold, err := strconv.ParseFloat(val, 64)
if err != nil {
Expand Down
6 changes: 5 additions & 1 deletion pkg/scalers/azure_monitor_scaler.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,11 @@ func parseAzureMonitorMetadata(config *ScalerConfig, logger logr.Logger) (*azure
}
meta.targetValue = targetValue
} else {
return nil, fmt.Errorf("no targetValue given")
if config.AsMetricSource {
meta.targetValue = 0
} else {
return nil, fmt.Errorf("no targetValue given")
}
}

if val, ok := config.TriggerMetadata[activationTargetValueName]; ok && val != "" {
Expand Down
6 changes: 5 additions & 1 deletion pkg/scalers/cassandra_scaler.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,11 @@ func parseCassandraMetadata(config *ScalerConfig) (*CassandraMetadata, error) {
}
meta.targetQueryValue = targetQueryValue
} else {
return nil, fmt.Errorf("no targetQueryValue given")
if config.AsMetricSource {
meta.targetQueryValue = 0
} else {
return nil, fmt.Errorf("no targetQueryValue given")
}
}

meta.activationTargetQueryValue = 0
Expand Down
6 changes: 5 additions & 1 deletion pkg/scalers/couchdb_scaler.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,11 @@ func parseCouchDBMetadata(config *ScalerConfig) (*couchDBMetadata, string, error
}
meta.queryValue = queryValue
} else {
return nil, "", fmt.Errorf("no queryValue given")
if config.AsMetricSource {
meta.queryValue = 0
} else {
return nil, "", fmt.Errorf("no queryValue given")
}
}

meta.activationQueryValue = 0
Expand Down
6 changes: 5 additions & 1 deletion pkg/scalers/datadog_scaler.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,11 @@ func parseDatadogMetadata(config *ScalerConfig, logger logr.Logger) (*datadogMet
}
meta.queryValue = queryValue
} else {
return nil, fmt.Errorf("no queryValue given")
if config.AsMetricSource {
meta.queryValue = 0
} else {
return nil, fmt.Errorf("no queryValue given")
}
}

if val, ok := config.TriggerMetadata["queryAggregator"]; ok && val != "" {
Expand Down
6 changes: 5 additions & 1 deletion pkg/scalers/elasticsearch_scaler.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,11 @@ func parseElasticsearchMetadata(config *ScalerConfig) (*elasticsearchMetadata, e

targetValueString, err := GetFromAuthOrMeta(config, "targetValue")
if err != nil {
return nil, err
if config.AsMetricSource {
targetValueString = "0"
} else {
return nil, err
}
}
targetValue, err := strconv.ParseFloat(targetValueString, 64)
if err != nil {
Expand Down
1 change: 0 additions & 1 deletion pkg/scalers/gcp_cloud_tasks_scaler.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ func parseGcpCloudTasksMetadata(config *ScalerConfig) (*gcpCloudTaskMetadata, er
if val == "" {
return nil, fmt.Errorf("no queue name given")
}

meta.queueName = val
} else {
return nil, fmt.Errorf("no queue name given")
Expand Down
6 changes: 5 additions & 1 deletion pkg/scalers/influxdb_scaler.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,11 @@ func parseInfluxDBMetadata(config *ScalerConfig) (*influxDBMetadata, error) {
}
thresholdValue = value
} else {
return nil, fmt.Errorf("no threshold value given")
if config.AsMetricSource {
thresholdValue = 0
} else {
return nil, fmt.Errorf("no threshold value given")
}
}
unsafeSsl = false
if val, ok := config.TriggerMetadata["unsafeSsl"]; ok {
Expand Down
6 changes: 5 additions & 1 deletion pkg/scalers/kubernetes_workload_scaler.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,11 @@ func parseWorkloadMetadata(config *ScalerConfig) (*kubernetesWorkloadMetadata, e
meta.podSelector = podSelector
value, err := strconv.ParseFloat(config.TriggerMetadata[valueKey], 64)
if err != nil || value == 0 {
return nil, fmt.Errorf("value must be a float greater than 0")
if config.AsMetricSource {
value = 0
} else {
return nil, fmt.Errorf("value must be a float greater than 0")
}
}
meta.value = value

Expand Down
6 changes: 5 additions & 1 deletion pkg/scalers/loki_scaler.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,11 @@ func parseLokiMetadata(config *ScalerConfig) (meta *lokiMetadata, err error) {

meta.threshold = t
} else {
return nil, fmt.Errorf("no %s given", lokiThreshold)
if config.AsMetricSource {
meta.threshold = 0
} else {
return nil, fmt.Errorf("no %s given", lokiThreshold)
}
}

meta.activationThreshold = 0
Expand Down
6 changes: 5 additions & 1 deletion pkg/scalers/metrics_api_scaler.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,11 @@ func parseMetricsAPIMetadata(config *ScalerConfig) (*metricsAPIScalerMetadata, e
}
meta.targetValue = targetValue
} else {
return nil, fmt.Errorf("no targetValue given in metadata")
if config.AsMetricSource {
meta.targetValue = 0
} else {
return nil, fmt.Errorf("no targetValue given in metadata")
}
}

meta.activationTargetValue = 0
Expand Down
6 changes: 5 additions & 1 deletion pkg/scalers/mongo_scaler.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,11 @@ func parseMongoDBMetadata(config *ScalerConfig) (*mongoDBMetadata, string, error
}
meta.queryValue = queryValue
} else {
return nil, "", fmt.Errorf("no queryValue given")
if config.AsMetricSource {
meta.queryValue = 0
} else {
return nil, "", fmt.Errorf("no queryValue given")
}
}

meta.activationQueryValue = 0
Expand Down
6 changes: 5 additions & 1 deletion pkg/scalers/mssql_scaler.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,11 @@ func parseMSSQLMetadata(config *ScalerConfig) (*mssqlMetadata, error) {
}
meta.targetValue = targetValue
} else {
return nil, ErrMsSQLNoTargetValue
if config.AsMetricSource {
meta.targetValue = 0
} else {
return nil, ErrMsSQLNoTargetValue
}
}

// Activation target value
Expand Down
6 changes: 5 additions & 1 deletion pkg/scalers/mysql_scaler.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,11 @@ func parseMySQLMetadata(config *ScalerConfig) (*mySQLMetadata, error) {
}
meta.queryValue = queryValue
} else {
return nil, fmt.Errorf("no queryValue given")
if config.AsMetricSource {
meta.queryValue = 0
} else {
return nil, fmt.Errorf("no queryValue given")
}
}

meta.activationQueryValue = 0
Expand Down
6 changes: 5 additions & 1 deletion pkg/scalers/newrelic_scaler.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,11 @@ func parseNewRelicMetadata(config *ScalerConfig, logger logr.Logger) (*newrelicM
}
meta.threshold = t
} else {
return nil, fmt.Errorf("missing %s value", threshold)
if config.AsMetricSource {
meta.threshold = 0
} else {
return nil, fmt.Errorf("missing %s value", threshold)
}
}

meta.activationThreshold = 0
Expand Down
6 changes: 5 additions & 1 deletion pkg/scalers/postgresql_scaler.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,11 @@ func parsePostgreSQLMetadata(config *ScalerConfig) (*postgreSQLMetadata, error)
}
meta.targetQueryValue = targetQueryValue
} else {
return nil, fmt.Errorf("no targetQueryValue given")
if config.AsMetricSource {
meta.targetQueryValue = 0
} else {
return nil, fmt.Errorf("no targetQueryValue given")
}
}

meta.activationTargetQueryValue = 0
Expand Down
6 changes: 5 additions & 1 deletion pkg/scalers/predictkube_scaler.go
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,11 @@ func parsePredictKubeMetadata(config *ScalerConfig) (result *predictKubeMetadata
}
meta.threshold = threshold
} else {
return nil, fmt.Errorf("no threshold given")
if config.AsMetricSource {
meta.threshold = 0
} else {
return nil, fmt.Errorf("no threshold given")
}
}

meta.activationThreshold = 0
Expand Down
Loading
Loading