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

Refactor AWS client creation #332

Merged
merged 16 commits into from
Sep 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.conveyal.datatools.common.utils;

import java.util.HashMap;

import static com.conveyal.datatools.common.utils.AWSUtils.DEFAULT_EXPIRING_AWS_ASSET_VALID_DURATION_MILLIS;

/**
* This abstract class provides a framework for managing the creation of AWS Clients. Three types of clients are stored
* in this class:
* 1. A default client to use when not requesting a client using a specific role and/or region
* 2. A client to use when using a specific region, but not with a role
* 3. A client to use with a specific role and region combination (including null regions)
*
* The {@link AWSClientManager#getClient(String, String)} handles the creation and caching of clients based on the given
* role and region inputs.
*/
public abstract class AWSClientManager<T> {
evansiroky marked this conversation as resolved.
Show resolved Hide resolved
protected final T defaultClient;
private HashMap<String, T> nonRoleClientsByRegion = new HashMap<>();
private HashMap<String, ExpiringAsset<T>> clientsByRoleAndRegion = new HashMap<>();

public AWSClientManager (T defaultClient) {
this.defaultClient = defaultClient;
}

/**
* An abstract method where the implementation will create a client with the specified region, but not with a role.
*/
public abstract T buildDefaultClientWithRegion(String region);

/**
* An abstract method where the implementation will create a client with the specified role and region.
*/
protected abstract T buildCredentialedClientForRoleAndRegion(String role, String region)
throws CheckedAWSException;

/**
* Obtain a potentially cached AWS client for the provided role ARN and region. If the role and region are null, the
* default AWS client will be used. If just the role is null a cached client configured for the specified
* region will be returned. For clients that require using a role, a client will be obtained (either via a cache or
* by creation and then insertion into the cache) that has obtained the proper credentials.
*/
public T getClient(String role, String region) throws CheckedAWSException {
// return default client for null region and role
if (role == null && region == null) {
return defaultClient;
}

// if the role is null, return a potentially cached EC2 client with the region configured
T client;
if (role == null) {
client = nonRoleClientsByRegion.get(region);
if (client == null) {
client = buildDefaultClientWithRegion(region);
nonRoleClientsByRegion.put(region, client);
}
return client;
}

// check for the availability of a client already associated with the given role and region
String roleRegionKey = makeRoleRegionKey(role, region);
ExpiringAsset<T> clientWithRole = clientsByRoleAndRegion.get(roleRegionKey);
if (clientWithRole != null && clientWithRole.isActive()) return clientWithRole.asset;

// Either a new client hasn't been created or it has expired. Create a new client and cache it.
T credentialedClientForRoleAndRegion = buildCredentialedClientForRoleAndRegion(role, region);
clientsByRoleAndRegion.put(
roleRegionKey,
new ExpiringAsset<T>(credentialedClientForRoleAndRegion, DEFAULT_EXPIRING_AWS_ASSET_VALID_DURATION_MILLIS)
);
return credentialedClientForRoleAndRegion;
}

private static String makeRoleRegionKey(String role, String region) {
return String.format("role=%s,region=%s", role, region);
}
}
Loading