Skip to content

Commit

Permalink
feat: Add activationTarget for formulas (kedacore#4998)
Browse files Browse the repository at this point in the history
Signed-off-by: Jorge Turrado <jorge_turrado@hotmail.es>
Signed-off-by: Jorge Turrado <jorge.turrado@scrm.lidl>
Signed-off-by: anton.lysina <alysina@gmail.com>
  • Loading branch information
JorTurFer authored and toniiiik committed Jan 15, 2024
1 parent b614596 commit 44ddd22
Show file tree
Hide file tree
Showing 34 changed files with 291 additions and 119 deletions.
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

0 comments on commit 44ddd22

Please sign in to comment.