From 1f965a4988ad25bd09c5d337001120d01fff985e Mon Sep 17 00:00:00 2001 From: Saranya Somepalli Date: Mon, 11 May 2026 14:38:03 -0700 Subject: [PATCH 1/2] Update S3 and S3Control tests for auth scheme and endpoint resolution pipeline migration --- .../codegen-resources/customization.config | 1 + .../S3CrossRegionAsyncClientTest.java | 13 +++--- .../S3CrossRegionSyncClientTest.java | 13 +++--- .../ListOfStringsAuthParamPluginTest.java | 41 +++++++++++++++---- .../S3ControlWireMockRerouteInterceptor.java | 13 +++++- .../signing/UrlEncodingTest.java | 2 +- 6 files changed, 62 insertions(+), 21 deletions(-) diff --git a/services/s3/src/main/resources/codegen-resources/customization.config b/services/s3/src/main/resources/codegen-resources/customization.config index 4dee2d9e88d9..6df5a5bf4c49 100644 --- a/services/s3/src/main/resources/codegen-resources/customization.config +++ b/services/s3/src/main/resources/codegen-resources/customization.config @@ -206,6 +206,7 @@ "hasChecksumValidationEnabledProperty":true }, "skipEndpointTests": { + "Access points when Access points explicitly disabled (used for CreateBucket)": "CreateBucketInterceptor validates bucket name before endpoint resolution stage runs", "Invalid access point ARN: Not S3": "Test assumes UseArnRegion is true but SDK defaults to false", "Invalid access point ARN: AccountId is invalid": "Test assumes UseArnRegion is true but SDK defaults to false", "Invalid access point ARN: access point name is invalid": "Test assumes UseArnRegion is true but SDK defaults to false", diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/crossregion/S3CrossRegionAsyncClientTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/crossregion/S3CrossRegionAsyncClientTest.java index 1a45adee7a98..4d001dcd47c9 100644 --- a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/crossregion/S3CrossRegionAsyncClientTest.java +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/crossregion/S3CrossRegionAsyncClientTest.java @@ -71,6 +71,7 @@ import software.amazon.awssdk.services.s3.internal.crossregion.endpointprovider.BucketEndpointProvider; import software.amazon.awssdk.services.s3.model.GetObjectRequest; import software.amazon.awssdk.services.s3.model.GetObjectResponse; +import software.amazon.awssdk.services.s3.model.HeadBucketRequest; import software.amazon.awssdk.services.s3.model.ListObjectsV2Response; import software.amazon.awssdk.services.s3.model.S3Exception; import software.amazon.awssdk.services.s3.model.S3Response; @@ -487,10 +488,10 @@ void given_globalRegion_Client_Updates_region_to_useast1_and_useGlobalEndpointFl assertThat(captureInterceptor.endpointProvider).isInstanceOf(BucketEndpointProvider.class); ArgumentCaptor collectionCaptor = ArgumentCaptor.forClass(S3EndpointParams.class); verify(mockEndpointProvider, atLeastOnce()).resolveEndpoint(collectionCaptor.capture()); - collectionCaptor.getAllValues().forEach(resolvedParams -> { - assertThat(resolvedParams.region()).isEqualTo(Region.US_EAST_1); - assertThat(resolvedParams.useGlobalEndpoint()).isFalse(); - }); + S3EndpointParams lastResolvedParams = collectionCaptor.getAllValues() + .get(collectionCaptor.getAllValues().size() - 1); + assertThat(lastResolvedParams.region()).isEqualTo(Region.US_EAST_1); + assertThat(lastResolvedParams.useGlobalEndpoint()).isFalse(); } @@ -529,7 +530,9 @@ private static final class CaptureInterceptor implements ExecutionInterceptor { @Override public void beforeMarshalling(Context.BeforeMarshalling context, ExecutionAttributes executionAttributes) { - endpointProvider = executionAttributes.getAttribute(SdkInternalExecutionAttribute.ENDPOINT_PROVIDER); + if (!(context.request() instanceof HeadBucketRequest)) { + endpointProvider = executionAttributes.getAttribute(SdkInternalExecutionAttribute.ENDPOINT_PROVIDER); + } } } } diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/crossregion/S3CrossRegionSyncClientTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/crossregion/S3CrossRegionSyncClientTest.java index e1187124ecb9..fdec8f328aca 100644 --- a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/crossregion/S3CrossRegionSyncClientTest.java +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/crossregion/S3CrossRegionSyncClientTest.java @@ -61,6 +61,7 @@ import software.amazon.awssdk.services.s3.endpoints.internal.DefaultS3EndpointProvider; import software.amazon.awssdk.services.s3.internal.crossregion.endpointprovider.BucketEndpointProvider; import software.amazon.awssdk.services.s3.model.GetObjectRequest; +import software.amazon.awssdk.services.s3.model.HeadBucketRequest; import software.amazon.awssdk.services.s3.model.ListObjectsV2Response; import software.amazon.awssdk.services.s3.model.S3Exception; import software.amazon.awssdk.services.s3.paginators.ListObjectsV2Iterable; @@ -303,10 +304,10 @@ void given_globalRegion_Client_Updates_region_to_useast1_and_useGlobalEndpointFl assertThat(captureInterceptor.endpointProvider).isInstanceOf(BucketEndpointProvider.class); ArgumentCaptor collectionCaptor = ArgumentCaptor.forClass(S3EndpointParams.class); verify(mockEndpointProvider, atLeastOnce()).resolveEndpoint(collectionCaptor.capture()); - collectionCaptor.getAllValues().forEach(resolvedParams ->{ - assertThat(resolvedParams.region()).isEqualTo(Region.US_EAST_1); - assertThat(resolvedParams.useGlobalEndpoint()).isFalse(); - }); + S3EndpointParams lastResolvedParams = collectionCaptor.getAllValues() + .get(collectionCaptor.getAllValues().size() - 1); + assertThat(lastResolvedParams.region()).isEqualTo(Region.US_EAST_1); + assertThat(lastResolvedParams.useGlobalEndpoint()).isFalse(); } private static GetObjectRequest.Builder getObjectBuilder() { @@ -509,7 +510,9 @@ private static final class CaptureInterceptor implements ExecutionInterceptor { @Override public void beforeMarshalling(Context.BeforeMarshalling context, ExecutionAttributes executionAttributes) { - endpointProvider = executionAttributes.getAttribute(SdkInternalExecutionAttribute.ENDPOINT_PROVIDER); + if (!(context.request() instanceof HeadBucketRequest)) { + endpointProvider = executionAttributes.getAttribute(SdkInternalExecutionAttribute.ENDPOINT_PROVIDER); + } } } } diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/plugins/ListOfStringsAuthParamPluginTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/plugins/ListOfStringsAuthParamPluginTest.java index 2f3a2b6f2194..b5e72ed58e9b 100644 --- a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/plugins/ListOfStringsAuthParamPluginTest.java +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/plugins/ListOfStringsAuthParamPluginTest.java @@ -27,17 +27,26 @@ import org.junit.jupiter.api.Test; import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.awscore.AwsExecutionAttribute; +import software.amazon.awssdk.core.ClientEndpointProvider; import software.amazon.awssdk.core.SdkPlugin; import software.amazon.awssdk.core.SdkServiceClientConfiguration; +import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.core.interceptor.SdkExecutionAttribute; +import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute; import software.amazon.awssdk.http.auth.spi.scheme.AuthSchemeOption; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.s3.S3AsyncClient; +import software.amazon.awssdk.utils.AttributeMap; import software.amazon.awssdk.services.s3.S3ServiceClientConfiguration; import software.amazon.awssdk.services.s3.auth.scheme.S3AuthSchemeParams; import software.amazon.awssdk.services.s3.auth.scheme.S3AuthSchemeProvider; +import software.amazon.awssdk.services.s3.endpoints.S3EndpointParams; +import software.amazon.awssdk.services.s3.endpoints.internal.S3EndpointResolverUtils; import software.amazon.awssdk.services.s3.model.Delete; import software.amazon.awssdk.services.s3.model.DeleteObjectsRequest; import software.amazon.awssdk.services.s3.model.ObjectIdentifier; +import software.amazon.awssdk.utils.AttributeMap; @WireMockTest class ListOfStringsAuthParamPluginTest { @@ -69,30 +78,44 @@ void callingDeleteObjects_requestWithCompleteListOfKey_returnsRightValues() { @Test void callingDeleteObjects_requestWithInCompleteListOfKey_returnsRightValues() { - s3Client.deleteObjects(r -> r.bucket("test").delete(o -> o.objects( + DeleteObjectsRequest request = DeleteObjectsRequest.builder().bucket("test").delete(d -> d.objects( ObjectIdentifier.builder().versionId("1").build(), ObjectIdentifier.builder().key("y").versionId("2").build() - ))); - assertThat(plugin.storedKeys()).isEqualTo(asList("y")); + )).build(); + S3EndpointParams params = S3EndpointResolverUtils.ruleParams(request, deleteObjectsAttributes()); + assertThat(params.deleteObjectKeys()).isEqualTo(asList("y")); } @Test void callingDeleteObjects_requestWithNoList_returnsRightValues() { - s3Client.deleteObjects(DeleteObjectsRequest.builder().delete(Delete.builder().build()).build()); - assertThat(plugin.storedKeys()).asList().isEmpty(); + DeleteObjectsRequest request = DeleteObjectsRequest.builder().delete(Delete.builder().build()).build(); + S3EndpointParams params = S3EndpointResolverUtils.ruleParams(request, deleteObjectsAttributes()); + assertThat(params.deleteObjectKeys()).asList().isEmpty(); } @Test void callingDeleteObjects_requestWithoutEmptyList_returnsRightValues() { - s3Client.deleteObjects(DeleteObjectsRequest.builder().delete(Delete.builder().objects(Collections.emptyList()).build()).build()); - assertThat(plugin.storedKeys()).asList().isEmpty(); + DeleteObjectsRequest request = DeleteObjectsRequest.builder().delete(Delete.builder().objects(Collections.emptyList()).build()).build(); + S3EndpointParams params = S3EndpointResolverUtils.ruleParams(request, deleteObjectsAttributes()); + assertThat(params.deleteObjectKeys()).asList().isEmpty(); } @Test void callingDeleteObjects_requestWithListButNoKey_returnsRightValues() { List objects = asList(ObjectIdentifier.builder().build()); - s3Client.deleteObjects(DeleteObjectsRequest.builder().delete(Delete.builder().objects(objects).build()).build()); - assertThat(plugin.storedKeys()).asList().isEmpty(); + DeleteObjectsRequest request = DeleteObjectsRequest.builder().delete(Delete.builder().objects(objects).build()).build(); + S3EndpointParams params = S3EndpointResolverUtils.ruleParams(request, deleteObjectsAttributes()); + assertThat(params.deleteObjectKeys()).asList().isEmpty(); + } + + private static ExecutionAttributes deleteObjectsAttributes() { + ExecutionAttributes attrs = new ExecutionAttributes(); + attrs.putAttribute(SdkExecutionAttribute.OPERATION_NAME, "DeleteObjects"); + attrs.putAttribute(AwsExecutionAttribute.AWS_REGION, Region.US_EAST_1); + attrs.putAttribute(SdkInternalExecutionAttribute.CLIENT_ENDPOINT_PROVIDER, + ClientEndpointProvider.forEndpointOverride(URI.create("https://s3.us-east-1.amazonaws.com"))); + attrs.putAttribute(SdkInternalExecutionAttribute.CLIENT_CONTEXT_PARAMS, AttributeMap.empty()); + return attrs; } private static class ListOfStringsParamPlugin implements SdkPlugin { diff --git a/services/s3control/src/test/java/software/amazon/awssdk/services/s3control/internal/functionaltests/arns/S3ControlWireMockRerouteInterceptor.java b/services/s3control/src/test/java/software/amazon/awssdk/services/s3control/internal/functionaltests/arns/S3ControlWireMockRerouteInterceptor.java index a561c919bab2..fb209138ab2f 100644 --- a/services/s3control/src/test/java/software/amazon/awssdk/services/s3control/internal/functionaltests/arns/S3ControlWireMockRerouteInterceptor.java +++ b/services/s3control/src/test/java/software/amazon/awssdk/services/s3control/internal/functionaltests/arns/S3ControlWireMockRerouteInterceptor.java @@ -6,6 +6,8 @@ import software.amazon.awssdk.core.interceptor.Context; import software.amazon.awssdk.core.interceptor.ExecutionAttributes; import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; +import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute; +import software.amazon.awssdk.endpoints.Endpoint; import software.amazon.awssdk.http.SdkHttpRequest; /** @@ -25,12 +27,21 @@ public class S3ControlWireMockRerouteInterceptor implements ExecutionInterceptor public SdkHttpRequest modifyHttpRequest(Context.ModifyHttpRequest context, ExecutionAttributes executionAttributes) { SdkHttpRequest request = context.httpRequest(); - recordedEndpoints.add(request.getUri()); recordedRequests.add(request); return request.toBuilder().uri(rerouteEndpoint).build(); } + @Override + public void beforeTransmission(Context.BeforeTransmission context, ExecutionAttributes executionAttributes) { + Endpoint resolvedEndpoint = executionAttributes.getAttribute(SdkInternalExecutionAttribute.RESOLVED_ENDPOINT); + if (resolvedEndpoint != null) { + recordedEndpoints.add(resolvedEndpoint.url()); + } else { + recordedEndpoints.add(context.httpRequest().getUri()); + } + } + public List getRecordedRequests() { return recordedRequests; } diff --git a/services/s3control/src/test/java/software/amazon/awssdk/services/s3control/internal/functionaltests/signing/UrlEncodingTest.java b/services/s3control/src/test/java/software/amazon/awssdk/services/s3control/internal/functionaltests/signing/UrlEncodingTest.java index b5688ad41c51..03765e9c8534 100644 --- a/services/s3control/src/test/java/software/amazon/awssdk/services/s3control/internal/functionaltests/signing/UrlEncodingTest.java +++ b/services/s3control/src/test/java/software/amazon/awssdk/services/s3control/internal/functionaltests/signing/UrlEncodingTest.java @@ -84,7 +84,7 @@ private static class ExecutionAttributeInterceptor implements ExecutionIntercept } @Override - public void beforeExecution(Context.BeforeExecution context, ExecutionAttributes executionAttributes) { + public void beforeTransmission(Context.BeforeTransmission context, ExecutionAttributes executionAttributes) { signerDoubleUrlEncode = executionAttributes.getAttribute(AwsSignerExecutionAttribute.SIGNER_DOUBLE_URL_ENCODE); } From 4bba23df4c21683a74cb5421c4f359999c59f8fa Mon Sep 17 00:00:00 2001 From: Saranya Somepalli Date: Tue, 12 May 2026 13:18:32 -0700 Subject: [PATCH 2/2] Address PR feedback --- .../ListOfStringsAuthParamPluginTest.java | 76 +------------------ 1 file changed, 4 insertions(+), 72 deletions(-) diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/plugins/ListOfStringsAuthParamPluginTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/plugins/ListOfStringsAuthParamPluginTest.java index b5e72ed58e9b..12cd20fac3cf 100644 --- a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/plugins/ListOfStringsAuthParamPluginTest.java +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/plugins/ListOfStringsAuthParamPluginTest.java @@ -18,29 +18,16 @@ import static java.util.Arrays.asList; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; -import com.github.tomakehurst.wiremock.junit5.WireMockTest; import java.net.URI; import java.util.Collections; import java.util.List; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; -import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; import software.amazon.awssdk.awscore.AwsExecutionAttribute; import software.amazon.awssdk.core.ClientEndpointProvider; -import software.amazon.awssdk.core.SdkPlugin; -import software.amazon.awssdk.core.SdkServiceClientConfiguration; import software.amazon.awssdk.core.interceptor.ExecutionAttributes; import software.amazon.awssdk.core.interceptor.SdkExecutionAttribute; import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute; -import software.amazon.awssdk.http.auth.spi.scheme.AuthSchemeOption; import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.services.s3.S3AsyncClient; -import software.amazon.awssdk.utils.AttributeMap; -import software.amazon.awssdk.services.s3.S3ServiceClientConfiguration; -import software.amazon.awssdk.services.s3.auth.scheme.S3AuthSchemeParams; -import software.amazon.awssdk.services.s3.auth.scheme.S3AuthSchemeProvider; import software.amazon.awssdk.services.s3.endpoints.S3EndpointParams; import software.amazon.awssdk.services.s3.endpoints.internal.S3EndpointResolverUtils; import software.amazon.awssdk.services.s3.model.Delete; @@ -48,32 +35,16 @@ import software.amazon.awssdk.services.s3.model.ObjectIdentifier; import software.amazon.awssdk.utils.AttributeMap; -@WireMockTest class ListOfStringsAuthParamPluginTest { - private static S3AsyncClient s3Client; - private ListOfStringsParamPlugin plugin; - - @BeforeEach - public void init(WireMockRuntimeInfo wm) { - plugin = new ListOfStringsParamPlugin(); - - AwsBasicCredentials credentials = AwsBasicCredentials.create("key", "secret"); - s3Client = S3AsyncClient.builder() - .region(Region.US_EAST_1) - .endpointOverride(URI.create(wm.getHttpBaseUrl())) - .addPlugin(plugin) - .credentialsProvider(StaticCredentialsProvider.create(credentials)) - .build(); - } - @Test void callingDeleteObjects_requestWithCompleteListOfKey_returnsRightValues() { - s3Client.deleteObjects(r -> r.bucket("test").delete(o -> o.objects( + DeleteObjectsRequest request = DeleteObjectsRequest.builder().bucket("test").delete(d -> d.objects( ObjectIdentifier.builder().key("x").versionId("1").build(), ObjectIdentifier.builder().key("y").versionId("2").build() - ))); - assertThat(plugin.storedKeys()).isEqualTo(asList("x", "y")); + )).build(); + S3EndpointParams params = S3EndpointResolverUtils.ruleParams(request, deleteObjectsAttributes()); + assertThat(params.deleteObjectKeys()).isEqualTo(asList("x", "y")); } @Test @@ -117,43 +88,4 @@ private static ExecutionAttributes deleteObjectsAttributes() { attrs.putAttribute(SdkInternalExecutionAttribute.CLIENT_CONTEXT_PARAMS, AttributeMap.empty()); return attrs; } - - private static class ListOfStringsParamPlugin implements SdkPlugin { - - private ListOfStringsParamAuthSchemeProvider localProvider; - - @Override - public void configureClient(SdkServiceClientConfiguration.Builder config) { - S3ServiceClientConfiguration.Builder serviceClientConfiguration = (S3ServiceClientConfiguration.Builder) config; - S3AuthSchemeProvider defaultProvider = serviceClientConfiguration.authSchemeProvider(); - localProvider = new ListOfStringsParamAuthSchemeProvider(defaultProvider); - serviceClientConfiguration.authSchemeProvider(localProvider); - } - - List storedKeys() { - return localProvider.storedKeys; - } - - private static class ListOfStringsParamAuthSchemeProvider implements S3AuthSchemeProvider { - - private List storedKeys; - S3AuthSchemeProvider delegate; - - public ListOfStringsParamAuthSchemeProvider(S3AuthSchemeProvider authSchemeProvider) { - this.delegate = authSchemeProvider; - } - - @Override - public List resolveAuthScheme(S3AuthSchemeParams authSchemeParams) { - List availableAuthSchemes = delegate.resolveAuthScheme(authSchemeParams); - - List keys = authSchemeParams.deleteObjectKeys(); - if (keys != null) { - storedKeys = keys; - } - return availableAuthSchemes; - } - } - } - }