From c8e7066e5c788276a89bd0332d157879cbe16311 Mon Sep 17 00:00:00 2001 From: Chris Larsen Date: Tue, 28 Mar 2023 12:15:33 -0700 Subject: [PATCH] aws2: support getting client by region (#568) Add a parameter so the region can be customized when getting or creating a new client. --- .../netflix/iep/aws2/AwsClientFactory.java | 53 +++++++++++++++++-- .../iep/aws2/AwsClientFactoryTest.java | 18 +++++++ 2 files changed, 66 insertions(+), 5 deletions(-) diff --git a/iep-spring-aws2/src/main/java/com/netflix/iep/aws2/AwsClientFactory.java b/iep-spring-aws2/src/main/java/com/netflix/iep/aws2/AwsClientFactory.java index 099191ee..868587c7 100644 --- a/iep-spring-aws2/src/main/java/com/netflix/iep/aws2/AwsClientFactory.java +++ b/iep-spring-aws2/src/main/java/com/netflix/iep/aws2/AwsClientFactory.java @@ -43,6 +43,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.Map; +import java.util.Optional; import java.util.ServiceLoader; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; @@ -344,22 +345,44 @@ public T newInstance(Class cls, String accountId) { */ @SuppressWarnings("unchecked") public T newInstance(String name, Class cls, String accountId) { + return newInstance(name, cls, accountId, Optional.empty()); + } + + /** + * Create a new instance of an AWS client. This method will always create a new instance. + * If you want to create or reuse an existing instance, then see + * {@link #getInstance(String, Class, String, Optional)}. + * + * @param name + * Name of the client. This is used to load config settings specific to the name. + * @param cls + * Class for the AWS client type to create, e.g. {@code Ec2Client.class}. + * @param accountId + * The AWS account id to use when assuming to a role. If null, then the account + * id should be specified directly in the role-arn setting or leave out the setting + * to use the default credentials provider. + * @param region + * An optional region to override that of the configuration. + * @return + * AWS client instance. + */ + public T newInstance(String name, Class cls, String accountId, Optional region) { try { SdkHttpService service = createSyncHttpService(name); Method builderMethod = cls.getMethod("builder"); AwsClientBuilder builder = ((AwsClientBuilder) builderMethod.invoke(null)) .credentialsProvider(createCredentialsProvider(name, accountId, service)) - .region(chooseRegion(name, cls)) + .region(region.orElseGet(() -> chooseRegion(name, cls))) .overrideConfiguration(createClientConfig(name)); AttributeMap attributeMap = getSdkHttpConfigurationOptions(name); if (builder instanceof AwsSyncClientBuilder) { ((AwsSyncClientBuilder) builder) - .httpClient(service.createHttpClientBuilder().buildWithDefaults(attributeMap)); + .httpClient(service.createHttpClientBuilder().buildWithDefaults(attributeMap)); } else if (builder instanceof AwsAsyncClientBuilder) { SdkAsyncHttpService asyncService = createAsyncHttpService(name); ((AwsAsyncClientBuilder) builder) - .httpClient(asyncService.createAsyncHttpClientFactory().buildWithDefaults(attributeMap)); + .httpClient(asyncService.createAsyncHttpClientFactory().buildWithDefaults(attributeMap)); } return (T) builder.build(); @@ -432,10 +455,30 @@ public T getInstance(Class cls, String accountId) { */ @SuppressWarnings("unchecked") public T getInstance(String name, Class cls, String accountId) { + return getInstance(name, cls, accountId, Optional.empty()); + } + + /** + * Get a shared instance of an AWS client. + * + * @param name + * Name of the client. This is used to load config settings specific to the name. + * @param cls + * Class for the AWS client type to create, e.g. {@code Ec2Client.class}. + * @param accountId + * The AWS account id to use when assuming to a role. If null, then the account + * id should be specified directly in the role-arn setting or leave out the setting + * to use the default credentials provider. + * @param region + * An optional region to override that of the configuration. + * @return + * AWS client instance. + */ + public T getInstance(String name, Class cls, String accountId, Optional region) { try { - final String key = name + ":" + cls.getName() + ":" + accountId; + final String key = name + ":" + cls.getName() + ":" + accountId + ":" + region.orElseGet(() -> chooseRegion(name, cls)); return (T) clients.computeIfAbsent(key, - k -> (SdkAutoCloseable) newInstance(name, cls, accountId)); + k -> (SdkAutoCloseable) newInstance(name, cls, accountId, region)); } catch (Exception e) { throw new RuntimeException("failed to get instance of " + cls.getName(), e); } diff --git a/iep-spring-aws2/src/test/java/com/netflix/iep/aws2/AwsClientFactoryTest.java b/iep-spring-aws2/src/test/java/com/netflix/iep/aws2/AwsClientFactoryTest.java index 6611cbd3..a2a05e06 100644 --- a/iep-spring-aws2/src/test/java/com/netflix/iep/aws2/AwsClientFactoryTest.java +++ b/iep-spring-aws2/src/test/java/com/netflix/iep/aws2/AwsClientFactoryTest.java @@ -28,6 +28,7 @@ import software.amazon.awssdk.http.SdkHttpConfigurationOption; import software.amazon.awssdk.http.SdkHttpService; import software.amazon.awssdk.http.apache.ApacheSdkHttpService; +import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.ec2.Ec2AsyncClient; import software.amazon.awssdk.services.ec2.Ec2Client; import software.amazon.awssdk.services.ec2.model.DescribeAddressesRequest; @@ -41,6 +42,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.function.Supplier; @RunWith(JUnit4.class) @@ -164,6 +166,13 @@ public void newInstanceName() throws Exception { Assert.assertNotNull(ec2); } + @Test + public void newInstanceRegion() throws Exception { + AwsClientFactory factory = new AwsClientFactory(config); + Ec2Client ec2 = factory.newInstance("ec2-test", Ec2Client.class, "123", Optional.of(Region.of("us-east-1"))); + Assert.assertNotNull(ec2); + } + @Test public void newInstanceInterfaceAsync() throws Exception { AwsClientFactory factory = new AwsClientFactory(config); @@ -210,6 +219,15 @@ public void getInstanceName() throws Exception { Assert.assertNotSame(ec2, factory.getInstance(Ec2Client.class)); } + @Test + public void getInstanceRegion() throws Exception { + AwsClientFactory factory = new AwsClientFactory(config); + Ec2Client ec2 = factory.getInstance("ec2-test", Ec2Client.class, "123", Optional.of(Region.of("us-east-1"))); + Assert.assertNotNull(ec2); + Assert.assertSame(ec2, factory.getInstance("ec2-test", Ec2Client.class, "123", Optional.of(Region.of("us-east-1")))); + Assert.assertNotSame(ec2, factory.getInstance(Ec2Client.class)); + } + @Test public void closeClients() throws Exception { // Verifies the close completes without throwing