diff --git a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/defaultsmode/DefaultsModeConfigurationGenerator.java b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/defaultsmode/DefaultsModeConfigurationGenerator.java index 558779909187..dfff8bc408cb 100644 --- a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/defaultsmode/DefaultsModeConfigurationGenerator.java +++ b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/defaultsmode/DefaultsModeConfigurationGenerator.java @@ -51,6 +51,7 @@ public class DefaultsModeConfigurationGenerator implements PoetClass { private static final Map HTTP_CONFIGURATION_MAPPING = new HashMap<>(); private static final String CONNECT_TIMEOUT_IN_MILLIS = "connectTimeoutInMillis"; private static final String TLS_NEGOTIATION_TIMEOUT_IN_MILLIS = "tlsNegotiationTimeoutInMillis"; + private static final String S3_US_EAST_1_REGIONAL_ENDPOINTS = "s3UsEast1RegionalEndpoints"; private final String basePackage; private final String defaultsModeBase; @@ -68,6 +69,12 @@ public class DefaultsModeConfigurationGenerator implements PoetClass { "TLS_NEGOTIATION_TIMEOUT"))); CONFIGURATION_MAPPING.put("retryMode", new OptionMetadata(ClassName.get("software.amazon.awssdk.core.retry", "RetryMode" ), ClassName.get("software.amazon.awssdk.core.client.config", "SdkClientOption", "DEFAULT_RETRY_MODE"))); + + CONFIGURATION_MAPPING.put(S3_US_EAST_1_REGIONAL_ENDPOINTS, + new OptionMetadata(ClassName.get(String.class), + ClassName.get("software.amazon.awssdk.regions", + "ServiceMetadataAdvancedOption", + "DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT"))); } public DefaultsModeConfigurationGenerator(String basePackage, String defaultsModeBase, DefaultConfiguration configuration) { @@ -121,8 +128,8 @@ private void addStaticEnumMapBlock(TypeSpec.Builder builder) { private void addEnumMapField(TypeSpec.Builder builder, String name) { ParameterizedTypeName map = ParameterizedTypeName.get(ClassName.get(Map.class), - defaultsModeClassName(), - ClassName.get(AttributeMap.class)); + defaultsModeClassName(), + ClassName.get(AttributeMap.class)); FieldSpec field = FieldSpec.builder(map, name, PRIVATE, STATIC, FINAL) .initializer("new $T<>(DefaultsMode.class)", EnumMap.class).build(); builder.addField(field); @@ -179,6 +186,9 @@ private void attributeMapBuilder(String option, String value, CodeBlock.Builder attributeBuilder.add(".put($T, $T.$N)", optionMetadata.attribute, optionMetadata.type, value.toUpperCase(Locale.US)); break; + case S3_US_EAST_1_REGIONAL_ENDPOINTS: + attributeBuilder.add(".put($T, $S)", optionMetadata.attribute, value); + break; default: throw new IllegalStateException("Unsupported option " + option); } diff --git a/codegen-lite/src/test/resources/software/amazon/awssdk/codegen/lite/defaultsmode/defaults-mode-configuration.java b/codegen-lite/src/test/resources/software/amazon/awssdk/codegen/lite/defaultsmode/defaults-mode-configuration.java index a6bc4763376a..d1e73a79daa7 100644 --- a/codegen-lite/src/test/resources/software/amazon/awssdk/codegen/lite/defaultsmode/defaults-mode-configuration.java +++ b/codegen-lite/src/test/resources/software/amazon/awssdk/codegen/lite/defaultsmode/defaults-mode-configuration.java @@ -8,6 +8,7 @@ import software.amazon.awssdk.core.client.config.SdkClientOption; import software.amazon.awssdk.core.retry.RetryMode; import software.amazon.awssdk.http.SdkHttpConfigurationOption; +import software.amazon.awssdk.regions.ServiceMetadataAdvancedOption; import software.amazon.awssdk.utils.AttributeMap; /** @@ -17,28 +18,32 @@ @Generated("software.amazon.awssdk:codegen") public final class DefaultsModeConfiguration { private static final AttributeMap STANDARD_DEFAULTS = AttributeMap.builder() - .put(SdkClientOption.DEFAULT_RETRY_MODE, RetryMode.STANDARD).build(); + .put(SdkClientOption.DEFAULT_RETRY_MODE, RetryMode.STANDARD) + .put(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT, "regional").build(); private static final AttributeMap STANDARD_HTTP_DEFAULTS = AttributeMap.builder() .put(SdkHttpConfigurationOption.CONNECTION_TIMEOUT, Duration.ofMillis(2000)) .put(SdkHttpConfigurationOption.TLS_NEGOTIATION_TIMEOUT, Duration.ofMillis(2000)).build(); private static final AttributeMap MOBILE_DEFAULTS = AttributeMap.builder() - .put(SdkClientOption.DEFAULT_RETRY_MODE, RetryMode.ADAPTIVE).build(); + .put(SdkClientOption.DEFAULT_RETRY_MODE, RetryMode.ADAPTIVE) + .put(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT, "regional").build(); private static final AttributeMap MOBILE_HTTP_DEFAULTS = AttributeMap.builder() .put(SdkHttpConfigurationOption.CONNECTION_TIMEOUT, Duration.ofMillis(10000)) .put(SdkHttpConfigurationOption.TLS_NEGOTIATION_TIMEOUT, Duration.ofMillis(11000)).build(); private static final AttributeMap CROSS_REGION_DEFAULTS = AttributeMap.builder() - .put(SdkClientOption.DEFAULT_RETRY_MODE, RetryMode.STANDARD).build(); + .put(SdkClientOption.DEFAULT_RETRY_MODE, RetryMode.STANDARD) + .put(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT, "regional").build(); private static final AttributeMap CROSS_REGION_HTTP_DEFAULTS = AttributeMap.builder() .put(SdkHttpConfigurationOption.CONNECTION_TIMEOUT, Duration.ofMillis(2800)) .put(SdkHttpConfigurationOption.TLS_NEGOTIATION_TIMEOUT, Duration.ofMillis(2800)).build(); private static final AttributeMap IN_REGION_DEFAULTS = AttributeMap.builder() - .put(SdkClientOption.DEFAULT_RETRY_MODE, RetryMode.STANDARD).build(); + .put(SdkClientOption.DEFAULT_RETRY_MODE, RetryMode.STANDARD) + .put(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT, "regional").build(); private static final AttributeMap IN_REGION_HTTP_DEFAULTS = AttributeMap.builder() .put(SdkHttpConfigurationOption.CONNECTION_TIMEOUT, Duration.ofMillis(1000)) diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/builder/AwsDefaultClientBuilder.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/builder/AwsDefaultClientBuilder.java index 0c798391d6a5..c9d7bddf9bd6 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/builder/AwsDefaultClientBuilder.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/builder/AwsDefaultClientBuilder.java @@ -47,6 +47,7 @@ import software.amazon.awssdk.profiles.ProfileFile; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.regions.ServiceMetadata; +import software.amazon.awssdk.regions.ServiceMetadataAdvancedOption; import software.amazon.awssdk.regions.providers.DefaultAwsRegionProviderChain; import software.amazon.awssdk.utils.AttributeMap; import software.amazon.awssdk.utils.CollectionUtils; @@ -188,12 +189,15 @@ protected final SdkClientConfiguration finalizeChildConfiguration(SdkClientConfi private SdkClientConfiguration mergeSmartDefaults(SdkClientConfiguration configuration) { DefaultsMode defaultsMode = resolveDefaultsMode(configuration); - RetryMode retryMode = DefaultsModeConfiguration.defaultConfig(defaultsMode).get(SdkClientOption.DEFAULT_RETRY_MODE); - + AttributeMap defaultConfig = DefaultsModeConfiguration.defaultConfig(defaultsMode); return configuration.toBuilder() .option(DEFAULTS_MODE, defaultsMode) .build() - .merge(c -> c.option(SdkClientOption.DEFAULT_RETRY_MODE, retryMode)); + .merge(c -> c.option(SdkClientOption.DEFAULT_RETRY_MODE, + defaultConfig.get(SdkClientOption.DEFAULT_RETRY_MODE)) + .option(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT, + defaultConfig.get( + ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT))); } /** @@ -232,6 +236,8 @@ private URI endpointFromConfig(SdkClientConfiguration config) { .withRegion(config.option(AwsClientOption.AWS_REGION)) .withProfileFile(config.option(SdkClientOption.PROFILE_FILE)) .withProfileName(config.option(SdkClientOption.PROFILE_NAME)) + .putAdvancedOption(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT, + config.option(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT)) .getServiceEndpoint(); } diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/endpoint/DefaultServiceEndpointBuilder.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/endpoint/DefaultServiceEndpointBuilder.java index 0320e3f7dfa2..842942ecb3b5 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/endpoint/DefaultServiceEndpointBuilder.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/endpoint/DefaultServiceEndpointBuilder.java @@ -17,13 +17,16 @@ import java.net.URI; import java.net.URISyntaxException; +import java.util.HashMap; import java.util.List; +import java.util.Map; import software.amazon.awssdk.annotations.NotThreadSafe; import software.amazon.awssdk.annotations.SdkProtectedApi; import software.amazon.awssdk.core.exception.SdkClientException; import software.amazon.awssdk.profiles.ProfileFile; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.regions.ServiceMetadata; +import software.amazon.awssdk.regions.ServiceMetadataAdvancedOption; import software.amazon.awssdk.utils.Validate; /** @@ -40,6 +43,7 @@ public final class DefaultServiceEndpointBuilder { private Region region; private ProfileFile profileFile; private String profileName; + private final Map, Object> advancedOptions = new HashMap<>(); public DefaultServiceEndpointBuilder(String serviceName, String protocol) { this.serviceName = Validate.paramNotNull(serviceName, "serviceName"); @@ -64,10 +68,16 @@ public DefaultServiceEndpointBuilder withProfileName(String profileName) { return this; } + public DefaultServiceEndpointBuilder putAdvancedOption(ServiceMetadataAdvancedOption option, T value) { + advancedOptions.put(option, value); + return this; + } + public URI getServiceEndpoint() { ServiceMetadata serviceMetadata = ServiceMetadata.of(serviceName) .reconfigure(c -> c.profileFile(() -> profileFile) - .profileName(profileName)); + .profileName(profileName) + .advancedOptions(advancedOptions)); URI endpoint = addProtocolToServiceEndpoint(serviceMetadata.endpointFor(region)); if (endpoint.getHost() == null) { diff --git a/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/builder/DefaultsModeTest.java b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/builder/DefaultsModeTest.java index 258ff046e9cc..9565b93bb14d 100644 --- a/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/builder/DefaultsModeTest.java +++ b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/builder/DefaultsModeTest.java @@ -23,6 +23,7 @@ import static software.amazon.awssdk.awscore.client.config.AwsClientOption.DEFAULTS_MODE; import static software.amazon.awssdk.core.client.config.SdkClientOption.DEFAULT_RETRY_MODE; import static software.amazon.awssdk.core.client.config.SdkClientOption.RETRY_POLICY; +import static software.amazon.awssdk.regions.ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT; import java.time.Duration; import org.junit.Test; @@ -75,6 +76,7 @@ public void defaultClient_shouldUseLegacyModeWithExistingDefaults() { assertThat(client.clientConfiguration.option(DEFAULTS_MODE)).isEqualTo(DefaultsMode.LEGACY); assertThat(client.clientConfiguration.option(RETRY_POLICY).retryMode()).isEqualTo(RetryMode.defaultRetryMode()); + assertThat(client.clientConfiguration.option(DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT)).isNull(); } @Test @@ -96,6 +98,7 @@ public void nonLegacyDefaultsMode_shouldApplySdkDefaultsAndHttpDefaults() { AttributeMap attributes = DefaultsModeConfiguration.defaultConfig(targetMode); assertThat(client.clientConfiguration.option(RETRY_POLICY).retryMode()).isEqualTo(attributes.get(DEFAULT_RETRY_MODE)); + assertThat(client.clientConfiguration.option(DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT)).isEqualTo("regional"); } @Test diff --git a/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/endpoint/DefaultServiceEndpointBuilderTest.java b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/endpoint/DefaultServiceEndpointBuilderTest.java index 90fa1f40034d..38b65f87a653 100644 --- a/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/endpoint/DefaultServiceEndpointBuilderTest.java +++ b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/endpoint/DefaultServiceEndpointBuilderTest.java @@ -20,6 +20,7 @@ import org.junit.Test; import software.amazon.awssdk.awscore.endpoint.DefaultServiceEndpointBuilder; import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.regions.ServiceMetadataAdvancedOption; public class DefaultServiceEndpointBuilderTest { @@ -43,4 +44,12 @@ public void getServiceEndpoint_S3NonStandardRegion_HttpProtocol() throws Excepti .withRegion(Region.EU_CENTRAL_1); assertEquals("http://s3.eu-central-1.amazonaws.com", endpointBuilder.getServiceEndpoint().toString()); } + + @Test + public void getServiceEndpoint_regionalOption_shouldUseRegionalEndpoint() throws Exception { + DefaultServiceEndpointBuilder endpointBuilder = new DefaultServiceEndpointBuilder("s3", "http") + .withRegion(Region.US_EAST_1).putAdvancedOption(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT, + "regional"); + assertEquals("http://s3.us-east-1.amazonaws.com", endpointBuilder.getServiceEndpoint().toString()); + } } diff --git a/core/regions/src/main/java/software/amazon/awssdk/regions/ServiceMetadataAdvancedOption.java b/core/regions/src/main/java/software/amazon/awssdk/regions/ServiceMetadataAdvancedOption.java new file mode 100644 index 000000000000..89cc510a9866 --- /dev/null +++ b/core/regions/src/main/java/software/amazon/awssdk/regions/ServiceMetadataAdvancedOption.java @@ -0,0 +1,45 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package software.amazon.awssdk.regions; + +import software.amazon.awssdk.annotations.SdkPublicApi; +import software.amazon.awssdk.core.SdkSystemSetting; +import software.amazon.awssdk.core.client.config.ClientOption; +import software.amazon.awssdk.profiles.ProfileProperty; + + +/** + * A collection of advanced options that can be configured on a {@link ServiceMetadata} via + * {@link ServiceMetadataConfiguration.Builder#putAdvancedOption(ServiceMetadataAdvancedOption, Object)}. + * + * @param The type of value associated with the option. + */ +@SdkPublicApi +public class ServiceMetadataAdvancedOption extends ClientOption { + + /** + * The default S3 regional endpoint setting for the {@code us-east-1} region to use. Setting + * the value to {@code regional} causes the SDK to use the {@code s3.us-east-1.amazonaws.com} endpoint when using the + * {@link Region#US_EAST_1} region instead of the global {@code s3.amazonaws.com} by default if it's not configured otherwise + * via {@link SdkSystemSetting#AWS_S3_US_EAST_1_REGIONAL_ENDPOINT} or {@link ProfileProperty#S3_US_EAST_1_REGIONAL_ENDPOINT} + */ + public static final ServiceMetadataAdvancedOption DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT = + new ServiceMetadataAdvancedOption<>(String.class); + + protected ServiceMetadataAdvancedOption(Class valueClass) { + super(valueClass); + } +} diff --git a/core/regions/src/main/java/software/amazon/awssdk/regions/ServiceMetadataConfiguration.java b/core/regions/src/main/java/software/amazon/awssdk/regions/ServiceMetadataConfiguration.java index 7639020c470d..aa26bdab9562 100644 --- a/core/regions/src/main/java/software/amazon/awssdk/regions/ServiceMetadataConfiguration.java +++ b/core/regions/src/main/java/software/amazon/awssdk/regions/ServiceMetadataConfiguration.java @@ -15,10 +15,13 @@ package software.amazon.awssdk.regions; +import java.util.Map; +import java.util.Optional; import java.util.function.Supplier; import software.amazon.awssdk.annotations.SdkPublicApi; import software.amazon.awssdk.profiles.ProfileFile; import software.amazon.awssdk.profiles.ProfileFileSystemSetting; +import software.amazon.awssdk.utils.AttributeMap; /** * Configuration for a {@link ServiceMetadata}. This allows modifying the values used by default when a metadata instance is @@ -30,10 +33,12 @@ public final class ServiceMetadataConfiguration { private final Supplier profileFile; private final String profileName; + private final AttributeMap advancedOptions; private ServiceMetadataConfiguration(Builder builder) { this.profileFile = builder.profileFile; this.profileName = builder.profileName; + this.advancedOptions = builder.advancedOptions.build(); } /** @@ -57,9 +62,19 @@ public String profileName() { return profileName; } + /** + * Load the optional requested advanced option that was configured on the service metadata builder. + * + * @see ServiceMetadataConfiguration.Builder#putAdvancedOption(ServiceMetadataAdvancedOption, Object) + */ + public Optional advancedOption(ServiceMetadataAdvancedOption option) { + return Optional.ofNullable(advancedOptions.get(option)); + } + public static final class Builder { private Supplier profileFile; private String profileName; + private AttributeMap.Builder advancedOptions = AttributeMap.builder(); private Builder() { } @@ -85,6 +100,24 @@ public Builder profileName(String profileName) { return this; } + /** + * Configure the map of advanced override options. This will override all values currently configured. The values in the + * map must match the key type of the map, or a runtime exception will be raised. + */ + public Builder putAdvancedOption(ServiceMetadataAdvancedOption option, T value) { + this.advancedOptions.put(option, value); + return this; + } + + /** + * Configure an advanced override option. + * @see ServiceMetadataAdvancedOption + */ + public Builder advancedOptions(Map, ?> advancedOptions) { + this.advancedOptions.putAll(advancedOptions); + return this; + } + /** * Build the {@link ServiceMetadata} instance with the updated configuration. */ diff --git a/core/regions/src/main/java/software/amazon/awssdk/regions/servicemetadata/EnhancedS3ServiceMetadata.java b/core/regions/src/main/java/software/amazon/awssdk/regions/servicemetadata/EnhancedS3ServiceMetadata.java index a3467944d5ee..72d63478770d 100644 --- a/core/regions/src/main/java/software/amazon/awssdk/regions/servicemetadata/EnhancedS3ServiceMetadata.java +++ b/core/regions/src/main/java/software/amazon/awssdk/regions/servicemetadata/EnhancedS3ServiceMetadata.java @@ -25,6 +25,7 @@ import software.amazon.awssdk.profiles.ProfileProperty; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.regions.ServiceMetadata; +import software.amazon.awssdk.regions.ServiceMetadataAdvancedOption; import software.amazon.awssdk.regions.ServiceMetadataConfiguration; import software.amazon.awssdk.regions.ServicePartitionMetadata; import software.amazon.awssdk.utils.Lazy; @@ -53,7 +54,7 @@ private EnhancedS3ServiceMetadata(ServiceMetadataConfiguration config) { Supplier profileName = config.profileName() != null ? () -> config.profileName() : ProfileFileSystemSetting.AWS_PROFILE::getStringValueOrThrow; - this.useUsEast1RegionalEndpoint = new Lazy<>(() -> useUsEast1RegionalEndpoint(profileFile, profileName)); + this.useUsEast1RegionalEndpoint = new Lazy<>(() -> useUsEast1RegionalEndpoint(profileFile, profileName, config)); this.s3ServiceMetadata = new S3ServiceMetadata().reconfigure(config); } @@ -80,7 +81,8 @@ public List servicePartitions() { return s3ServiceMetadata.servicePartitions(); } - private boolean useUsEast1RegionalEndpoint(Supplier profileFile, Supplier profileName) { + private boolean useUsEast1RegionalEndpoint(Supplier profileFile, Supplier profileName, + ServiceMetadataConfiguration config) { String env = envVarSetting(); if (env != null) { @@ -93,7 +95,8 @@ private boolean useUsEast1RegionalEndpoint(Supplier profileFile, Su return REGIONAL_SETTING.equalsIgnoreCase(profile); } - return false; + return config.advancedOption(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT) + .filter(REGIONAL_SETTING::equalsIgnoreCase).isPresent(); } private static String envVarSetting() { diff --git a/core/regions/src/test/java/software/amazon/awssdk/regions/servicemetadata/EnhancedS3ServiceMetadataTest.java b/core/regions/src/test/java/software/amazon/awssdk/regions/servicemetadata/EnhancedS3ServiceMetadataTest.java index 062592471266..3462090224d3 100644 --- a/core/regions/src/test/java/software/amazon/awssdk/regions/servicemetadata/EnhancedS3ServiceMetadataTest.java +++ b/core/regions/src/test/java/software/amazon/awssdk/regions/servicemetadata/EnhancedS3ServiceMetadataTest.java @@ -18,83 +18,119 @@ import static org.assertj.core.api.Assertions.assertThat; import java.net.URI; -import java.net.URISyntaxException; +import java.nio.file.Files; import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Collection; import org.junit.After; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; import software.amazon.awssdk.core.SdkSystemSetting; import software.amazon.awssdk.profiles.ProfileFile; -import software.amazon.awssdk.profiles.ProfileFileSystemSetting; import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.regions.ServiceMetadata; +import software.amazon.awssdk.regions.ServiceMetadataAdvancedOption; import software.amazon.awssdk.testutils.EnvironmentVariableHelper; +import software.amazon.awssdk.utils.Validate; +@RunWith(Parameterized.class) public class EnhancedS3ServiceMetadataTest { private static final EnvironmentVariableHelper ENVIRONMENT_VARIABLE_HELPER = new EnvironmentVariableHelper(); private static final URI S3_GLOBAL_ENDPOINT = URI.create("s3.amazonaws.com"); private static final URI S3_IAD_REGIONAL_ENDPOINT = URI.create("s3.us-east-1.amazonaws.com"); - private EnhancedS3ServiceMetadata enhancedMetadata = new EnhancedS3ServiceMetadata(); + private ServiceMetadata enhancedMetadata = new EnhancedS3ServiceMetadata(); + + @Parameterized.Parameter + public TestData testData; + + @Parameterized.Parameters + public static Collection data() { + return Arrays.asList(new Object[] { + // Test defaults + new TestData(null, null, null, null, S3_GLOBAL_ENDPOINT), + + // Test precedence + new TestData("regional", null, null, null, S3_IAD_REGIONAL_ENDPOINT), + new TestData("test", "regional", "/profileconfig/s3_regional_config_profile.tst", "regional", + S3_GLOBAL_ENDPOINT), + new TestData(null, "regional", "/profileconfig/s3_regional_config_profile.tst", "non-regional", + S3_IAD_REGIONAL_ENDPOINT), + new TestData(null, null, "/profileconfig/s3_regional_config_profile.tst", "non-regional", S3_IAD_REGIONAL_ENDPOINT), + new TestData(null, null, null, "regional", S3_IAD_REGIONAL_ENDPOINT), + + // Test capitalization standardization + new TestData("rEgIONal", null, null, null, S3_IAD_REGIONAL_ENDPOINT), + new TestData(null, "rEgIONal", null, null, S3_IAD_REGIONAL_ENDPOINT), + new TestData(null, null, "/profileconfig/s3_regional_config_profile_mixed_case.tst", null, S3_IAD_REGIONAL_ENDPOINT), + new TestData(null, null, null, "rEgIONal", S3_IAD_REGIONAL_ENDPOINT), + + // Test other value + new TestData("othervalue", null, null, null, S3_GLOBAL_ENDPOINT), + new TestData(null, "dafsad", null, null, S3_GLOBAL_ENDPOINT), + new TestData(null, null, "/profileconfig/s3_regional_config_profile_non_regional.tst", null, S3_GLOBAL_ENDPOINT), + new TestData(null, null, null, "somehtingelse", S3_GLOBAL_ENDPOINT), + }); + } + @After public void methodSetup() { ENVIRONMENT_VARIABLE_HELPER.reset(); - System.clearProperty(ProfileFileSystemSetting.AWS_PROFILE.property()); - System.clearProperty(ProfileFileSystemSetting.AWS_CONFIG_FILE.property()); - - enhancedMetadata = new EnhancedS3ServiceMetadata(); - } - - @Test - public void optionNotSet_returnsGlobalEndpoint() { - assertThat(enhancedMetadata.endpointFor(Region.US_EAST_1)).isEqualTo(S3_GLOBAL_ENDPOINT); + System.clearProperty(SdkSystemSetting.AWS_S3_US_EAST_1_REGIONAL_ENDPOINT.property()); } @Test - public void regionalSet_profile_returnsRegionalEndpoint() throws URISyntaxException { - String testFile = "/profileconfig/s3_regional_config_profile.tst"; - - System.setProperty(ProfileFileSystemSetting.AWS_PROFILE.property(), "regional_s3_endpoint"); - System.setProperty(ProfileFileSystemSetting.AWS_CONFIG_FILE.property(), Paths.get(getClass().getResource(testFile).toURI()).toString()); - - assertThat(enhancedMetadata.endpointFor(Region.US_EAST_1)).isEqualTo(S3_IAD_REGIONAL_ENDPOINT); + public void differentCombinationOfConfigs_shouldResolveCorrectly() { + enhancedMetadata = + new EnhancedS3ServiceMetadata().reconfigure(c -> c.putAdvancedOption(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT, + testData.advancedOption)); + if (testData.envVarValue != null) { + ENVIRONMENT_VARIABLE_HELPER.set(SdkSystemSetting.AWS_S3_US_EAST_1_REGIONAL_ENDPOINT.environmentVariable(), + testData.envVarValue); + } + + if (testData.systemProperty != null) { + System.setProperty(SdkSystemSetting.AWS_S3_US_EAST_1_REGIONAL_ENDPOINT.property(), testData.systemProperty); + } + + if (testData.configFile != null) { + String diskLocationForFile = diskLocationForConfig(testData.configFile); + Validate.isTrue(Files.isReadable(Paths.get(diskLocationForFile)), diskLocationForFile + " is not readable."); + + ProfileFile file = ProfileFile.builder() + .content(Paths.get(diskLocationForFile)) + .type(ProfileFile.Type.CONFIGURATION) + .build(); + + enhancedMetadata = enhancedMetadata.reconfigure(c -> c.profileFile(() -> file) + .profileName("regional_s3_endpoint") + .putAdvancedOption(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT, + testData.advancedOption)); + } + + URI result = enhancedMetadata.endpointFor(Region.US_EAST_1); + assertThat(result).isEqualTo(testData.expected); } - @Test - public void reconfiguredProfile_returnsRegionalEndpoint() throws URISyntaxException { - String testFile = "/profileconfig/s3_regional_config_profile.tst"; - - ProfileFile file = ProfileFile.builder() - .content(Paths.get(getClass().getResource(testFile).toURI())) - .type(ProfileFile.Type.CONFIGURATION) - .build(); - - assertThat(enhancedMetadata.reconfigure(c -> c.profileFile(() -> file) - .profileName("regional_s3_endpoint")) - .endpointFor(Region.US_EAST_1)) - .isEqualTo(S3_IAD_REGIONAL_ENDPOINT); + private String diskLocationForConfig(String configFileName) { + return getClass().getResource(configFileName).getFile(); } - @Test - public void regionalSet_env_returnsRegionalEndpoint() { - ENVIRONMENT_VARIABLE_HELPER.set(SdkSystemSetting.AWS_S3_US_EAST_1_REGIONAL_ENDPOINT.environmentVariable(), "regional"); - assertThat(enhancedMetadata.endpointFor(Region.US_EAST_1)).isEqualTo(S3_IAD_REGIONAL_ENDPOINT); - } - - @Test - public void regionalSet_mixedCase_env_returnsRegionalEndpoint() { - ENVIRONMENT_VARIABLE_HELPER.set(SdkSystemSetting.AWS_S3_US_EAST_1_REGIONAL_ENDPOINT.environmentVariable(), "rEgIoNaL"); - assertThat(enhancedMetadata.endpointFor(Region.US_EAST_1)).isEqualTo(S3_IAD_REGIONAL_ENDPOINT); - } - - @Test - public void global_env_returnsGlobalEndpoint() { - ENVIRONMENT_VARIABLE_HELPER.set(SdkSystemSetting.AWS_S3_US_EAST_1_REGIONAL_ENDPOINT.environmentVariable(), "non_regional"); - assertThat(enhancedMetadata.endpointFor(Region.US_EAST_1)).isEqualTo(S3_GLOBAL_ENDPOINT); - } - - @Test - public void valueNotEqualToRegional_env_returnsGlobalEndpoint() { - ENVIRONMENT_VARIABLE_HELPER.set(SdkSystemSetting.AWS_S3_US_EAST_1_REGIONAL_ENDPOINT.environmentVariable(), "some-nonsense-value"); - assertThat(enhancedMetadata.endpointFor(Region.US_EAST_1)).isEqualTo(S3_GLOBAL_ENDPOINT); + private static class TestData { + private final String envVarValue; + private final String systemProperty; + private final String configFile; + private final String advancedOption; + private final URI expected; + + TestData(String systemProperty, String envVarValue, String configFile, String advancedOption, URI expected) { + this.envVarValue = envVarValue; + this.systemProperty = systemProperty; + this.configFile = configFile; + this.advancedOption = advancedOption; + this.expected = expected; + } } } diff --git a/core/regions/src/test/resources/profileconfig/s3_regional_config_profile_mixed_case.tst b/core/regions/src/test/resources/profileconfig/s3_regional_config_profile_mixed_case.tst new file mode 100644 index 000000000000..2fd93950a604 --- /dev/null +++ b/core/regions/src/test/resources/profileconfig/s3_regional_config_profile_mixed_case.tst @@ -0,0 +1,2 @@ +[profile regional_s3_endpoint] +s3_us_east_1_regional_endpoint=REgiONal \ No newline at end of file diff --git a/core/regions/src/test/resources/profileconfig/s3_regional_config_profile_non_regional.tst b/core/regions/src/test/resources/profileconfig/s3_regional_config_profile_non_regional.tst new file mode 100644 index 000000000000..a517714117fe --- /dev/null +++ b/core/regions/src/test/resources/profileconfig/s3_regional_config_profile_non_regional.tst @@ -0,0 +1,2 @@ +[profile regional_s3_endpoint] +s3_us_east_1_regional_endpoint=somethingelse \ No newline at end of file diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/S3EndpointResolutionTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/S3EndpointResolutionTest.java index 0fa440bd8a34..9eb8c770bcbf 100644 --- a/services/s3/src/test/java/software/amazon/awssdk/services/s3/S3EndpointResolutionTest.java +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/S3EndpointResolutionTest.java @@ -27,6 +27,7 @@ import org.junit.Test; import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.awscore.defaultsmode.DefaultsMode; import software.amazon.awssdk.core.SdkSystemSetting; import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption; @@ -418,6 +419,24 @@ public void regionalSettingUnset_usesGlobalEndpoint() throws UnsupportedEncoding assertThat(mockHttpClient.getLastRequest().getUri().getHost()).isEqualTo("s3.amazonaws.com"); } + @Test + public void standardDefaultsMode_usesRegionalIadEndpoint() throws UnsupportedEncodingException { + mockHttpClient.stubNextResponse(mockListObjectsResponse()); + + S3Client s3Client = S3Client.builder() + .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create("akid", + "skid"))) + .httpClient(mockHttpClient) + .defaultsMode(DefaultsMode.STANDARD) + .serviceConfiguration(S3Configuration.builder() + .pathStyleAccessEnabled(true) + .build()) + .region(Region.US_EAST_1) + .build(); + s3Client.listObjects(ListObjectsRequest.builder().bucket(BUCKET).build()); + assertThat(mockHttpClient.getLastRequest().getUri().getHost()).isEqualTo("s3.us-east-1.amazonaws.com"); + } + /** * Assert that the provided request would have gone to the given endpoint. *