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

Add an elasticsearch scaler based on search template #2304

Closed

Conversation

nicolasadeo
Copy link

@nicolasadeo nicolasadeo commented Nov 19, 2021

Signed-off-by: Nicolas L nicolas.lassalle@zenika.com

I am pleased to propose you the implementation of an Elasticsearch scaler based on a search template. Using a search template make things easy on the scaler configuration. The user must provide a search template name and eventually its params. The search result can be parsed using the gjson library like the metrics api scaler does.
For now, the authentication is based on username/password ; on the future we could imagine provide a CA certificate to validate a self signed certificate or ECE credentials.

I had a great time working on this scaler and I would be very pleased to get your code review's feedback.

Checklist

  • Commits are signed with Developer Certificate of Origin (DCO - learn more)
  • Tests have been added
  • A PR is opened to update our Helm chart (repo) (if applicable, ie. when deployment manifests are modified)
  • A PR is opened to update the documentation on (repo) (if applicable)
  • Changelog has been updated

Fixes #1756

Signed-off-by: Nicolas L <nicolas.lassalle@zenika.com>
@nicolasadeo nicolasadeo changed the title Adds an elasticsearch scaler based on search template Add an elasticsearch scaler based on search template Nov 19, 2021
@tomkerkhove
Copy link
Member

Thanks for the PR! Would you mind adding a new changelog entry please?

Signed-off-by: Nicolas L <nicolas.lassalle@zenika.com>
Signed-off-by: Nicolas L <nicolas.lassalle@zenika.com>
@nicolasadeo
Copy link
Author

the changelog is in progress. Ive opened the PR with a wrong github account. Would you be OK if I reopen it with the proper github account?

Signed-off-by: Nicolas L <nicolas.lassalle@zenika.com>
Signed-off-by: Nicolas L <nicolas.lassalle@zenika.com>
Signed-off-by: Nicolas L <nicolas.lassalle@zenika.com>
Signed-off-by: Nicolas L <nicolas.lassalle@zenika.com>
Signed-off-by: Nicolas L <nicolas.lassalle@zenika.com>
Signed-off-by: Nicolas L <nicolas.lassalle@zenika.com>
Signed-off-by: Nicolas L <nicolas.lassalle@zenika.com>
Signed-off-by: Nicolas L <nicolas.lassalle@zenika.com>
Signed-off-by: Nicolas L <nicolas.lassalle@zenika.com>
Copy link
Member

@JorTurFer JorTurFer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi,
In general looks good, only some small changes :)
Thanks a ton for your contribution ❤️

if val, ok := config.TriggerMetadata["unsafeSsl"]; ok {
meta.unsafeSsl, err = strconv.ParseBool(val)
if err != nil {
return nil, fmt.Errorf("error parsing unsafeSsL: %s", err)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return nil, fmt.Errorf("error parsing unsafeSsL: %s", err)
return nil, fmt.Errorf("error parsing unsafeSsl: %s", err)

Does the last L need to be in capital letter?

Comment on lines 136 to 141
if meta.unsafeSsl {
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
config.Transport = tr
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not an expert in elastic and maybe it's not possible but, could we avoid the if block using something like this?

Suggested change
if meta.unsafeSsl {
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
config.Transport = tr
}
config.Transport = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: meta.unsafeSsl},
}

We are using that approach inside utils package for the HTTPClient

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make senses. I dig further to check

// Run the templated search
res, err := s.esClient.SearchTemplate(
&body,
s.esClient.SearchTemplate.WithIndex(s.metadata.indexes...),
Copy link
Member

@JorTurFer JorTurFer Nov 21, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should pass the context until here because you could add .WithContext. With this change, the request context can be managed from outside the scaler.


externalMetric := &v2beta2.ExternalMetricSource{
Metric: v2beta2.MetricIdentifier{
Name: GenerateMetricNameWithIndex(s.metadata.targetValue, metricName),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This name should be generated using the ScalerIndex and the "metricName". The ScalerIndex is a parameter that all scalers receive in the constructor and which should be propagated to here in order to avoid conflicts. Also, it'd be tested during the unit test to ensure that it's correctly propagated.
This point is explained more in depth here.
All the other scalers propagate it, so you can check any other :)

@JorTurFer
Copy link
Member

/run-e2e elasticsearch*

fs.writeFileSync(elasticsearchTmpFile.name, elasticsearchStatefulsetYaml.replace('{{ELASTIC_PASSWORD}}', elasticPassword))

t.is(0, sh.exec(`kubectl apply --namespace ${elasticsearchNamespace} -f ${elasticsearchTmpFile.name}`).code, 'creating an elasticsearch statefulset should work.')
t.is(0, waitForRollout('statefulset', "elasticsearch", elasticsearchNamespace))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think e2e tests are failing because this could need a bit more time. WDYT?

Suggested change
t.is(0, waitForRollout('statefulset', "elasticsearch", elasticsearchNamespace))
t.is(0, waitForRollout('statefulset', "elasticsearch", elasticsearchNamespace, 300))

Copy link
Contributor

@orphaner orphaner Nov 22, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the elasticsearch image is huge (~1.2GB) and on my 6 cores laptop it takes ~20-30 seconds to start.
Let's check with 300 iterations

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will run again e2e tests when you increase the waiting timeout :)

Signed-off-by: Nicolas L <nicolas.lassalle@zenika.com>
@orphaner
Copy link
Contributor

the changelog is in progress. Ive opened the PR with a wrong github account. Would you be OK if I reopen it with the proper github account?

could you let me know about this question? I really would like that the PR is attached to my proper GH account 🙏

@orphaner
Copy link
Contributor

Hi, In general looks good, only some small changes :) Thanks a ton for your contribution heart

Small changes are pushed. My pleasure, I had a great time coding on keda!

@JorTurFer
Copy link
Member

could you let me know about this question? I really would like that the PR is attached to my proper GH account 🙏

I think that it's not a problem, go on :)
The only thing that you should remember is updating the PR in docs to point to the new PR

Copy link
Member

@JorTurFer JorTurFer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi,
another small nits:
Could you review the comment about the metric name and add atl least 1 test with ScalerIndex > 0?
Thanks! ❤️


externalMetric := &v2beta2.ExternalMetricSource{
Metric: v2beta2.MetricIdentifier{
Name: GenerateMetricNameWithIndex(s.metadata.targetValue, metricName),
Name: GenerateMetricNameWithIndex(s.metadata.targetValue, s.metadata.metricName),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should call only once to GenerateMetricNameWithIndex. This index is not related with any inside the scaler, it's related with the trigger index inside the ScaledObject to avoid conflicts with duplicated names in the same ScaledObject.
You could call to the function here or in the constructor like you are doing but don't do it both, because the final metric name with current code will be like s{s.metadata.targetValue}-s{config.ScalerIndex}-elasticsearch-%s. It should be like s{config.ScalerIndex}-elasticsearch-%s

@JorTurFer
Copy link
Member

/run-e2e elasticsearch*

@nicolasadeo
Copy link
Author

Duplicate of #2311

@JorTurFer
Copy link
Member

I have commented again on the new PR, you can ignore this :)

@nicolasadeo
Copy link
Author

thanks a lot :)

@zroubalik zroubalik modified the milestone: v2.5.0 Nov 23, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Provide scaler for Elasticsearch
5 participants