Skip to content

Instantly share code, notes, and snippets.

@mpatnode
Last active May 2, 2023 14:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mpatnode/e06e2dba5d2c5d59a49f0bbe04d36e52 to your computer and use it in GitHub Desktop.
Save mpatnode/e06e2dba5d2c5d59a49f0bbe04d36e52 to your computer and use it in GitHub Desktop.
Generic Wrapper for AWSClient Builder to override the endpoint URL using environment variables
package com.britive.util;
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* This class allows global and selective endpoint override for the following services via
* an environment variable
*
* DynamoDB - AWS_DYNAMO_ENDPOINT_URL
* SNS - AWS_SNS_ENDPOINT_URL
* SQS - AWS_SQS_ENDPOINT_URL
* SES - AWS_SES_ENDPOINT_URL
* S3 - AWS_S3_ENDPOINT_URL
* KMS - AWS_KMS_ENDPOINT_URL
* Cognito - AWS_IAM_ENDPOINT_URL
*
* You can globally override for all services by setting AWS_ENDPOINT_URL, but the specific
* ones above will override when necessary. For example:
*
* AWS_ENDPOINT_URL=http://localstack:4566
* AWS_S3_ENDPOINT_URL=http://s3.localhost.localstack.cloud:4566
*
* The region for each endpoint can be set as well:
*
* AWS_IAM_REGION=us-west-2
* AWS_KMS_REGION=us-east-1
*
* Otherwise the value of AWS_REGION is used.
*
* Note you must wrap your current code with this decorator as described below in the build() method.
**/
public class AWSClientEnvConfig {
private AWSClientEnvConfig() {};
private static Map<String, String> envMap = Collections.unmodifiableMap(
new HashMap<String, String>() {{
// By not using <classname>.class.getSimpleName() we avoid the need
// to import all the packages and dependencies into this module
put("AmazonDynamoDBClientBuilder", "AWS_DYNAMO_ENDPOINT_URL");
put("AmazonIdentityManagementClientBuilder", "AWS_IAM_ENDPOINT_URL");
put("AWSCognitoIdentityProviderClientBuilder", "AWS_IAM_ENDPOINT_URL");
put("AmazonKinesisFirehoseClientBuilder", "AWS_KFIRE_ENDPOINT_URL");
put("AmazonS3ClientBuilder", "AWS_S3_ENDPOINT_URL");
put("AmazonSNSAsyncClientBuilder", "AWS_SNS_ENDPOINT_URL");
put("AmazonSNSClientBuilder", "AWS_SNS_ENDPOINT_URL");
put("AmazonSQSAsyncClientBuilder", "AWS_SQS_ENDPOINT_URL");
put("AmazonSQSClientBuilder", "AWS_SQS_ENDPOINT_URL");
put("AmazonSimpleEmailServiceClientBuilder", "AWS_SES_ENDPOINT_URL");
put("AWSKMSClientBuilder", "AWS_KMS_ENDPOINT_URL");
put("AWSSimpleSystemsManagementClientBuilder", "AWS_SSM_ENDPOINT_URL");
// The above was determined by grepping all our source. Feel free to add more as needed
}});
private static String getEnvEndpoint(String classname) {
String globalEndpoint = System.getenv("AWS_ENDPOINT_URL");
if (envMap.containsKey(classname)) {
String serviceEndpoint = System.getenv(envMap.get(classname));
if (serviceEndpoint != null && !serviceEndpoint.isEmpty()) {
if (serviceEndpoint.contentEquals("default")) {
return null;
}
return serviceEndpoint;
}
}
return globalEndpoint;
}
/**
* Allow for environment endpoint URL override when creating an Amazon client (Dynamo, Dax, SNS, etc..)
*
* Example old code
* dynamoDB = AmazonDynamoDBClientBuilder.standard().build()
* snsClient = AmazonSNSAsyncClientBuilder.standard().build()
* sqs = AmazonSQSClientBuilder.defaultClient();
* New code
* dynamoDB = AWSClientEnvConfig.build(AmazonDynamoDBClientBuilder.standard());
* snsClient = AWSClientEnvConfig.build(AmazonSNSAsyncClientBuilder.standard());
* sqs = AWSClientEnvConfig.build(AmazonSQSClientBuilder.standard());
*
* @param builder
* @return an AWS Client of the desired type
* @param <T> The return type (IE: AWSDynamoDB)
* @param <B> The builder for the type. Typically the return from AWS*ClientBuilder.standard()
*/
public static <T, B extends AwsClientBuilder<B, T>> T build(B builder) {
String endpointURL = getEnvEndpoint(builder.getClass().getSimpleName());
if (endpointURL != null && !endpointURL.isEmpty()) {
if (endpointURL.contains("localstack")) {
// Reset the credentials.
// XXX: This should check an environment variable for the case where you want to test localstack IAM
AWSCredentialsProvider creds = new AWSStaticCredentialsProvider(new BasicAWSCredentials("none", "none"));
builder.setCredentials(creds);
}
String region = getEnvRegion(builder.getClass().getSimpleName());
AwsClientBuilder.EndpointConfiguration epc = new AwsClientBuilder.EndpointConfiguration(endpointURL, region);
return builder.withEndpointConfiguration(epc).build();
} else {
return builder.build();
}
}
private static String getEnvRegion(String classname) {
String envVarName = envMap.get(classname);
String region = null;
if (envVarName != null) {
envVarName = envVarName.replace("ENDPOINT_URL", "REGION");
region = System.getenv(envVarName);
}
return defaultIfNull(region,
defaultIfNull(System.getenv("AWS_REGION"), "us-east-1")
);
}
}
@mpatnode
Copy link
Author

mpatnode commented May 2, 2023

Added per-service region support

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment