-
Notifications
You must be signed in to change notification settings - Fork 24.8k
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
Update secure settings for the repository azure repository plugin #29319
Update secure settings for the repository azure repository plugin #29319
Conversation
Pinging @elastic/es-distributed |
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 is looking pretty good. I left a few comments.
{ | ||
return this.client.doesContainerExist(this.clientName, this.locMode, container); | ||
public boolean doesContainerExist() { | ||
logger.trace("containerExists({})", container); |
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.
is this trace logging necessary?
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.
Agreed. I have removed it. Trace logging statements have been sprinkled without much restrain. Surely some nasty bug fighting went on here 🐛
} catch (URISyntaxException | StorageException e) { | ||
logger.warn("can not remove [{}] in container {{}}: {}", keyPath, container, e.getMessage()); | ||
logger.warn("cannot access [{}] in container {{}}: {}", keyPath, container, e.getMessage()); | ||
throw new IOException(e); | ||
} | ||
} | ||
|
||
@Override | ||
public void close() { |
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.
not something you changed but I think we can remove this override?
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.
Looks like it's needed to implement Closeable#close
.
logger.trace("containerExists({})", container); | ||
try { | ||
return service.doesContainerExist(clientName, container); | ||
} catch (URISyntaxException | StorageException e) { |
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.
every other method throws these exceptions, is there a reason why this one cannot also throw?
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 have adjusted it to also throw exception.
I guess the original reasoning was that boolean exists
should swallow the exception and return false, making code simpler and presumably because if indeed there is a connection problem the exception will be rethrown later, when trying the create/access associated operation. Yet, this is not consistent across the plugin code, and your suggestion is indeed in tone with the surrounding.
if (clientsSettings.isEmpty()) { | ||
throw new IllegalArgumentException("If you want to use an azure repository, you need to define a client configuration."); | ||
} | ||
assert clientsSettings.containsKey("default") : "always have 'default'"; |
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.
Can we make this an exception instead of an assert?
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 have moved the check as suggested in the below comment, but kept the assert there since it would truly be a code problem instead of an external one.
if (storageSettings.isEmpty()) { | ||
// If someone did not register any settings, they basically can't use the plugin | ||
// eagerly load client settings so that secure settings are read | ||
final Map<String, AzureStorageSettings> clientsSettings = AzureStorageSettings.load(settings); |
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.
how about moving the validation (empty and default checks) into AzureStorageSettings#load
so it is not duplicated in the plugin reinit as well?
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 have moved the validation inside the AzureStorageSettings#load
method, where settings are constructed.
I have contemplated this duplication before commiting it. I have tried to keep settings validation closer to the "use-point" instead of "construction point" since, in principle, they might change in other layers (which is not the case here). This was the closest I could get, with only duplication, because of the override location mode mechanism which temporarily updates with invalid settings (empty).
|
||
if (client == null) { | ||
throw new IllegalArgumentException("Can not find an azure client named [" + azureStorageSettings.getAccount() + "]"); | ||
logger.trace(() -> new ParameterizedMessage("creating new Azure storage client using account [{}], endpoint suffix [{}]", |
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.
Is this trace logging necessary?
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.
Removed.
@@ -140,15 +153,33 @@ public Proxy getProxy() { | |||
return proxy; | |||
} | |||
|
|||
public String getConnectionString() { |
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.
Should we precompute and store this in a member variable? It does not look like it will change as when we reinit we get a new object.
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 have renamed it to buildConnectionString
.
I slightly prefer to keep AzureStorageSettings
with only primal members, no derivated/computed ones. It's only a taste decision, because if the connection string would have been build several times, or there would be other such fields, then I would go for it and taint the struct with cached members 😛
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 looked over it and I think it LGTM thanks for doing it albert
@@ -140,15 +153,33 @@ public Proxy getProxy() { | |||
return proxy; | |||
} | |||
|
|||
public String buildConnectionString() { |
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.
++
final long timeout = azureStorageSettings.getTimeout().getMillis(); | ||
if (timeout > 0) { | ||
if (timeout > Integer.MAX_VALUE) { | ||
throw new IllegalArgumentException("Timeout [" + azureStorageSettings.getTimeout() + "] exceeds 2,147,483,647ms."); |
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 appreciate the feedbacks ! Thank you 👍 @jaymode I have addressed all your comments. Could take another glance at the look of things? |
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.
LGTM
Make secure settings for the repository-azure plugin updateable. This is the equivalent of #29134 and #28517 for the ec2-discovery and s3-repository, respectively. The meta issue tracking the progress of all plugins is #29135 .
The approach is similar to the previously mentioned plugins, but it is different in an important way. It is similar because
AzureRepositoryPlugin
now implements theReInitializablePlugin
interface, and there is a 'client service' dictionary of clients that permits changing the settings for the clients that it builds internally.The difference stems from the azure cloud client (
CloudBlobClient
) being a wrapper overjava.net.HttpURLConnection
. This has two implications. One, the client is notCloseable
and, two, it is not thread safe, so it is not cache-able across threads. Over the web, in their code samples, (I couldn't point to a clear statement inside the docs) it is recommended to just create a new instance of the client and not to reuse them. This is in contrast to bothAmazonEC2
andAmazonS3
clients that have toshutdown
to be collected and are thread safe. Consequently AwsEc2Service and AwsS3Service interfaces, which return a "reference counted wrapper", are different toAzureStorageService#client(String)
which returns a new client on each invocation! In the interest of the pattern we could have the same reference counted wrapper that would synchronize an internalCloudBlobClient
instance, yet I think this is intricate code-wise and trades performance (due to contention over a singleHttpURLConnection
). Premature optimization is the root of all evil, so I could be swayed here, but just sticking to the letter of the pattern is a week reason IMO.