-
Notifications
You must be signed in to change notification settings - Fork 313
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
Optionally use generated api_key auth to ES #1098
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -129,7 +129,17 @@ def _is_set(self, client_opts, k): | |
|
||
def create(self): | ||
import elasticsearch | ||
return elasticsearch.Elasticsearch(hosts=self.hosts, ssl_context=self.ssl_context, **self.client_options) | ||
instance = elasticsearch.Elasticsearch(hosts=self.hosts, ssl_context=self.ssl_context, **self.client_options) | ||
if self._is_set(self.client_options, "use_api_key"): | ||
# an api_key is generated per client, which happens after an initial handshake using an existing auth scheme | ||
# once this key is generated, a new client is created using the api_key client option and http_auth is removed | ||
# the reason the options were not removed in place was because of the logic to coalesce the key tuple into a | ||
# header that the client could use. | ||
api_key_response = instance.security.create_api_key({"name": "rally-api-key"}) | ||
self.client_options.pop("http_auth") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know if it possible to use API keys without prior basic auth but |
||
self.client_options["api_key"] = (api_key_response["id"], api_key_response["api_key"]) | ||
instance = elasticsearch.Elasticsearch(hosts=self.hosts, ssl_context=self.ssl_context, **self.client_options) | ||
return instance | ||
|
||
def create_async(self): | ||
import elasticsearch | ||
|
@@ -139,6 +149,10 @@ def create_async(self): | |
|
||
from elasticsearch.serializer import JSONSerializer | ||
|
||
if self._is_set(self.client_options, "use_api_key"): | ||
# Temporarily create a non async version of the client to generate an api_key and put it into client_options | ||
self.create() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that would work fine but how about we structure this a bit differently and instead encapsulate client option post processing into a dedicated method? Here is a summary of all my proposals (including earlier review comments: diff --git a/esrally/client.py b/esrally/client.py
index ff92b75..ce76ccb 100644
--- a/esrally/client.py
+++ b/esrally/client.py
@@ -128,6 +128,22 @@ class EsClientFactory:
return False
def create(self):
+ self._postprocess_client_options()
+ return self._create_sync_client()
+
+ def _postprocess_client_options(self):
+ if self.client_options.get("use_api_key", False):
+ es = None
+ try:
+ es = self._create_sync_client()
+ api_key_response = es.security.create_api_key({"name": "rally-api-key"})
+ self.client_options.pop("http_auth", None)
+ self.client_options["api_key"] = (api_key_response["id"], api_key_response["api_key"])
+ finally:
+ if es:
+ es.close()
+
+ def _create_sync_client(self):
import elasticsearch
return elasticsearch.Elasticsearch(hosts=self.hosts, ssl_context=self.ssl_context, **self.client_options)
@@ -163,6 +179,7 @@ class EsClientFactory:
# ensure that we also stop the timer when a request "ends" with an exception (e.g. a timeout)
trace_config.on_request_exception.append(on_request_end)
+ self._postprocess_client_options()
# override the builtin JSON serializer
self.client_options["serializer"] = LazyJSONSerializer()
self.client_options["trace_config"] = trace_config Wdyt? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like this approach better 👍 |
||
|
||
class LazyJSONSerializer(JSONSerializer): | ||
def loads(self, s): | ||
meta = RallyAsyncElasticsearch.request_context.get() | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would also enter the block if the user specified
use_api_key:false
on the client options. I noticed that we fall into this trap in other places in this class as well and I propose we change this line to:There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That method is slightly misleading. It does not check if its set, it just removes the possibility of a key error. It returns the value, if present. Adding a test like so confirms this (and passes).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that the semantics of the predicate
is_set
are misleading and maybe we should change it. I still propose we implement this line here differently:This ensures correct behavior depending on the value of
use_api_key
.