Skip to content

Commit

Permalink
Merge branch 'main' into spiritzhou/fulltriggername
Browse files Browse the repository at this point in the history
Signed-off-by: SpiritZhou <[email protected]>
  • Loading branch information
SpiritZhou authored Oct 24, 2024
2 parents 2643f66 + 2cf3c4c commit f20078b
Show file tree
Hide file tree
Showing 9 changed files with 308 additions and 201 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,10 @@ Here is an overview of all new **experimental** features:

### Improvements

- **General**: Prevent multiple ScaledObjects managing one HPA ([#6130](https://github.com/kedacore/keda/issues/6130))
- **General**: Show full triggers'names and authentications' names in status ([#6187](https://github.com/kedacore/keda/issues/6187))
- **AWS CloudWatch Scaler**: Add support for ignoreNullValues ([#5352](https://github.com/kedacore/keda/issues/5352))
- **Elasticsearch Scaler**: Support Query at the Elasticsearch scaler ([#6216](https://github.com/kedacore/keda/issues/6216))
- **Etcd Scaler**: Add username and password support for etcd ([#6199](https://github.com/kedacore/keda/pull/6199))
- **GCP Scalers**: Added custom time horizon in GCP scalers ([#5778](https://github.com/kedacore/keda/issues/5778))
- **GitHub Scaler**: Fixed pagination, fetching repository list ([#5738](https://github.com/kedacore/keda/issues/5738))
Expand Down
16 changes: 16 additions & 0 deletions apis/keda/v1alpha1/scaledobject_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ func verifyScaledObjects(incomingSo *ScaledObject, action string, _ bool) error
return err
}

incomingSoHpaName := getHpaName(*incomingSo)
for _, so := range soList.Items {
if so.Name == incomingSo.Name {
continue
Expand All @@ -315,6 +316,13 @@ func verifyScaledObjects(incomingSo *ScaledObject, action string, _ bool) error
metricscollector.RecordScaledObjectValidatingErrors(incomingSo.Namespace, action, "other-scaled-object")
return err
}

if getHpaName(so) == incomingSoHpaName {
err = fmt.Errorf("the HPA '%s' is already managed by the ScaledObject '%s'", so.Spec.Advanced.HorizontalPodAutoscalerConfig.Name, so.Name)
scaledobjectlog.Error(err, "validation error")
metricscollector.RecordScaledObjectValidatingErrors(incomingSo.Namespace, action, "other-scaled-object-hpa")
return err
}
}

// verify ScalingModifiers structure if defined in ScaledObject
Expand Down Expand Up @@ -572,3 +580,11 @@ func isContainerResourceLimitSet(ctx context.Context, namespace string, triggerT

return false
}

func getHpaName(so ScaledObject) string {
if so.Spec.Advanced == nil || so.Spec.Advanced.HorizontalPodAutoscalerConfig == nil || so.Spec.Advanced.HorizontalPodAutoscalerConfig.Name == "" {
return fmt.Sprintf("keda-hpa-%s", so.Name)
}

return so.Spec.Advanced.HorizontalPodAutoscalerConfig.Name
}
48 changes: 37 additions & 11 deletions pkg/scalers/elasticsearch_scaler.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"strings"

"github.com/elastic/go-elasticsearch/v7"
"github.com/elastic/go-elasticsearch/v7/esapi"
"github.com/go-logr/logr"
"github.com/tidwall/gjson"
v2 "k8s.io/api/autoscaling/v2"
Expand All @@ -34,7 +35,8 @@ type elasticsearchMetadata struct {
CloudID string `keda:"name=cloudID, order=authParams;triggerMetadata, optional"`
APIKey string `keda:"name=apiKey, order=authParams;triggerMetadata, optional"`
Index []string `keda:"name=index, order=authParams;triggerMetadata, separator=;"`
SearchTemplateName string `keda:"name=searchTemplateName, order=authParams;triggerMetadata"`
SearchTemplateName string `keda:"name=searchTemplateName, order=authParams;triggerMetadata, optional"`
Query string `keda:"name=query, order=authParams;triggerMetadata, optional"`
Parameters []string `keda:"name=parameters, order=triggerMetadata, optional, separator=;"`
ValueLocation string `keda:"name=valueLocation, order=authParams;triggerMetadata"`
TargetValue float64 `keda:"name=targetValue, order=authParams;triggerMetadata"`
Expand All @@ -57,6 +59,13 @@ func (m *elasticsearchMetadata) Validate() error {
if len(m.Addresses) > 0 && (m.Username == "" || m.Password == "") {
return fmt.Errorf("both username and password must be provided when addresses is used")
}
if m.SearchTemplateName == "" && m.Query == "" {
return fmt.Errorf("either searchTemplateName or query must be provided")
}
if m.SearchTemplateName != "" && m.Query != "" {
return fmt.Errorf("cannot provide both searchTemplateName and query")
}

return nil
}

Expand Down Expand Up @@ -93,7 +102,12 @@ func parseElasticsearchMetadata(config *scalersconfig.ScalerConfig) (elasticsear
return meta, err
}

meta.MetricName = GenerateMetricNameWithIndex(config.TriggerIndex, util.NormalizeString(fmt.Sprintf("elasticsearch-%s", meta.SearchTemplateName)))
if meta.SearchTemplateName != "" {
meta.MetricName = GenerateMetricNameWithIndex(config.TriggerIndex, util.NormalizeString(fmt.Sprintf("elasticsearch-%s", meta.SearchTemplateName)))
} else {
meta.MetricName = GenerateMetricNameWithIndex(config.TriggerIndex, "elasticsearch-query")
}

meta.TriggerIndex = config.TriggerIndex

return meta, nil
Expand Down Expand Up @@ -137,17 +151,29 @@ func (s *elasticsearchScaler) Close(_ context.Context) error {
// getQueryResult returns result of the scaler query
func (s *elasticsearchScaler) getQueryResult(ctx context.Context) (float64, error) {
// Build the request body.
var body bytes.Buffer
if err := json.NewEncoder(&body).Encode(buildQuery(&s.metadata)); err != nil {
s.logger.Error(err, "Error encoding query: %s", err)
var res *esapi.Response
var err error

if s.metadata.SearchTemplateName != "" {
// Using SearchTemplateName
var body bytes.Buffer
if err := json.NewEncoder(&body).Encode(buildQuery(&s.metadata)); err != nil {
s.logger.Error(err, "Error encoding query: %s", err)
}
res, err = s.esClient.SearchTemplate(
&body,
s.esClient.SearchTemplate.WithIndex(s.metadata.Index...),
s.esClient.SearchTemplate.WithContext(ctx),
)
} else {
// Using Query
res, err = s.esClient.Search(
s.esClient.Search.WithIndex(s.metadata.Index...),
s.esClient.Search.WithBody(strings.NewReader(s.metadata.Query)),
s.esClient.Search.WithContext(ctx),
)
}

// Run the templated search
res, err := s.esClient.SearchTemplate(
&body,
s.esClient.SearchTemplate.WithIndex(s.metadata.Index...),
s.esClient.SearchTemplate.WithContext(ctx),
)
if err != nil {
s.logger.Error(err, fmt.Sprintf("Could not query elasticsearch: %s", err))
return 0, err
Expand Down
56 changes: 51 additions & 5 deletions pkg/scalers/elasticsearch_scaler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,34 @@ var testCases = []parseElasticsearchMetadataTestData{
expectedError: fmt.Errorf("missing required parameter \"index\""),
},
{
name: "no searchTemplateName given",
name: "query and searchTemplateName provided",
metadata: map[string]string{
"addresses": "http://localhost:9200",
"index": "index1",
"addresses": "http://localhost:9200",
"index": "index1",
"query": `{"match": {"field": "value"}}`,
"searchTemplateName": "myTemplate",
"valueLocation": "hits.total.value",
"targetValue": "12",
},
authParams: map[string]string{"username": "admin"},
expectedError: fmt.Errorf("missing required parameter \"searchTemplateName\""),
authParams: map[string]string{
"username": "admin",
"password": "password",
},
expectedError: fmt.Errorf("cannot provide both searchTemplateName and query"),
},
{
name: "neither query nor searchTemplateName provided",
metadata: map[string]string{
"addresses": "http://localhost:9200",
"index": "index1",
"valueLocation": "hits.total.value",
"targetValue": "12",
},
authParams: map[string]string{
"username": "admin",
"password": "password",
},
expectedError: fmt.Errorf("either searchTemplateName or query must be provided"),
},
{
name: "no valueLocation given",
Expand Down Expand Up @@ -306,6 +327,31 @@ var testCases = []parseElasticsearchMetadataTestData{
},
expectedError: nil,
},
{
name: "valid query parameter",
metadata: map[string]string{
"addresses": "http://localhost:9200",
"index": "index1",
"query": `{"match": {"field": "value"}}`,
"valueLocation": "hits.total.value",
"targetValue": "12",
},
authParams: map[string]string{
"username": "admin",
"password": "password",
},
expectedMetadata: &elasticsearchMetadata{
Addresses: []string{"http://localhost:9200"},
Index: []string{"index1"},
Username: "admin",
Password: "password",
Query: `{"match": {"field": "value"}}`,
ValueLocation: "hits.total.value",
TargetValue: 12,
MetricName: "s0-elasticsearch-query",
},
expectedError: nil,
},
}

func TestParseElasticsearchMetadata(t *testing.T) {
Expand Down
Loading

0 comments on commit f20078b

Please sign in to comment.