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

Update secure settings for the repository azure repository plugin #29319

Conversation

albertzaharovits
Copy link
Contributor

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 the ReInitializablePlugin 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 over java.net.HttpURLConnection . This has two implications. One, the client is not Closeable 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 both AmazonEC2 and AmazonS3 clients that have to shutdown to be collected and are thread safe. Consequently AwsEc2Service and AwsS3Service interfaces, which return a "reference counted wrapper", are different to AzureStorageService#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 internal CloudBlobClient instance, yet I think this is intricate code-wise and trades performance (due to contention over a single HttpURLConnection). 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.

@elasticmachine
Copy link
Collaborator

Pinging @elastic/es-distributed

Copy link
Member

@jaymode jaymode left a 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);
Copy link
Member

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?

Copy link
Contributor Author

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() {
Copy link
Member

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?

Copy link
Contributor Author

@albertzaharovits albertzaharovits Apr 5, 2018

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) {
Copy link
Member

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?

Copy link
Contributor Author

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'";
Copy link
Member

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?

Copy link
Contributor Author

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);
Copy link
Member

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?

Copy link
Contributor Author

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 [{}]",
Copy link
Member

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?

Copy link
Contributor Author

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() {
Copy link
Member

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.

Copy link
Contributor Author

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 😛

Copy link
Contributor

@s1monw s1monw left a 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() {
Copy link
Contributor

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.");
Copy link
Contributor

Choose a reason for hiding this comment

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

++

@albertzaharovits
Copy link
Contributor Author

I appreciate the feedbacks ! Thank you 👍

@jaymode I have addressed all your comments. Could take another glance at the look of things?

Copy link
Member

@jaymode jaymode left a comment

Choose a reason for hiding this comment

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

LGTM

@albertzaharovits albertzaharovits merged commit 057e388 into elastic:reload-secure-store-action Apr 6, 2018
@albertzaharovits albertzaharovits deleted the update-azure-secure-settings branch April 6, 2018 07:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
:Distributed/Snapshot/Restore Anything directly related to the `_snapshot/*` APIs >feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants