Skip to content

Commit 7f35b68

Browse files
committed
Integration test cases for Cross region Sync and Sync Clients
1 parent 5d57416 commit 7f35b68

File tree

4 files changed

+475
-0
lines changed

4 files changed

+475
-0
lines changed
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.services.s3.crossregion;
17+
18+
import java.util.ArrayList;
19+
import java.util.Collections;
20+
import java.util.List;
21+
import java.util.concurrent.atomic.AtomicInteger;
22+
import software.amazon.awssdk.core.interceptor.Context;
23+
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
24+
import software.amazon.awssdk.core.interceptor.ExecutionInterceptor;
25+
import software.amazon.awssdk.http.SdkHttpMethod;
26+
27+
public final class CaptureInterceptor implements ExecutionInterceptor {
28+
private final List<SdkHttpMethod> httpMethods = new ArrayList<>();
29+
private List<String> hosts = new ArrayList<>();
30+
private AtomicInteger serviceCalls = new AtomicInteger(0);
31+
32+
@Override
33+
public void beforeTransmission(Context.BeforeTransmission context, ExecutionAttributes executionAttributes) {
34+
hosts.add(context.httpRequest().host());
35+
httpMethods.add(context.httpRequest().method());
36+
serviceCalls.incrementAndGet();
37+
}
38+
39+
public int getServiceCalls() {
40+
return serviceCalls.get();
41+
}
42+
43+
public List<String> hosts() {
44+
return Collections.unmodifiableList(this.hosts);
45+
}
46+
47+
public List<SdkHttpMethod> httpMethods() {
48+
return Collections.unmodifiableList(this.httpMethods);
49+
}
50+
51+
52+
public void reset() {
53+
this.hosts = new ArrayList<>();
54+
}
55+
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.services.s3.crossregion;
17+
18+
import static software.amazon.awssdk.testutils.service.S3BucketUtils.temporaryBucketName;
19+
20+
import java.util.ArrayList;
21+
import java.util.List;
22+
import java.util.concurrent.CompletableFuture;
23+
import org.junit.jupiter.api.AfterAll;
24+
import org.junit.jupiter.api.AfterEach;
25+
import org.junit.jupiter.api.BeforeAll;
26+
import org.junit.jupiter.api.BeforeEach;
27+
import software.amazon.awssdk.core.ResponseBytes;
28+
import software.amazon.awssdk.core.async.AsyncRequestBody;
29+
import software.amazon.awssdk.core.async.AsyncResponseTransformer;
30+
import software.amazon.awssdk.services.s3.S3AsyncClient;
31+
import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
32+
import software.amazon.awssdk.services.s3.model.DeleteObjectResponse;
33+
import software.amazon.awssdk.services.s3.model.DeleteObjectsRequest;
34+
import software.amazon.awssdk.services.s3.model.DeleteObjectsResponse;
35+
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
36+
import software.amazon.awssdk.services.s3.model.GetObjectResponse;
37+
import software.amazon.awssdk.services.s3.model.HeadBucketRequest;
38+
import software.amazon.awssdk.services.s3.model.HeadBucketResponse;
39+
import software.amazon.awssdk.services.s3.model.ListObjectsV2Request;
40+
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
41+
import software.amazon.awssdk.services.s3.model.PutObjectResponse;
42+
import software.amazon.awssdk.services.s3.model.S3Object;
43+
import software.amazon.awssdk.services.s3.paginators.ListObjectsV2Publisher;
44+
45+
public class S3AsyncCrossRegionTest extends S3CrossRegionTestBase {
46+
private S3AsyncClient crossRegionS3Client ;
47+
48+
@BeforeEach
49+
public void initialize() {
50+
captureInterceptor = new CaptureInterceptor();
51+
crossRegionS3Client = S3AsyncClient.builder()
52+
.region(CROSS_REGION)
53+
.serviceConfiguration(s -> s.crossRegionAccessEnabled(true))
54+
.overrideConfiguration(o -> o.addExecutionInterceptor(captureInterceptor))
55+
.build();
56+
}
57+
58+
@AfterEach
59+
void reset(){
60+
captureInterceptor.reset();
61+
}
62+
@BeforeAll
63+
static void setUpClass(){
64+
// Bucket Created in CROSS Region
65+
s3 = s3ClientBuilder().build();
66+
createBucket(BUCKET);
67+
}
68+
69+
@AfterAll
70+
static void clearClass(){
71+
deleteBucketAndAllContents(BUCKET);
72+
}
73+
74+
private static final String BUCKET = temporaryBucketName(S3AsyncCrossRegionTest.class);
75+
76+
@Override
77+
protected List<S3Object> paginatedAPICall(ListObjectsV2Request listObjectsV2Request) {
78+
List<S3Object> resultObjects = new ArrayList<>();
79+
ListObjectsV2Publisher publisher = crossRegionS3Client.listObjectsV2Paginator(listObjectsV2Request);
80+
CompletableFuture<Void> subscribe = publisher.subscribe(response -> {
81+
response.contents().forEach(a -> resultObjects.add(a));
82+
});
83+
subscribe.join();
84+
return resultObjects;
85+
}
86+
87+
@Override
88+
protected DeleteObjectsResponse postObjectAPICall(DeleteObjectsRequest deleteObjectsRequest) {
89+
return crossRegionS3Client.deleteObjects(deleteObjectsRequest).join();
90+
}
91+
92+
@Override
93+
protected HeadBucketResponse headAPICall(HeadBucketRequest headBucketRequest) {
94+
return crossRegionS3Client.headBucket(headBucketRequest).join();
95+
}
96+
97+
@Override
98+
protected DeleteObjectResponse deleteObjectAPICall(DeleteObjectRequest deleteObjectRequest) {
99+
return crossRegionS3Client.deleteObject(deleteObjectRequest).join();
100+
}
101+
102+
@Override
103+
protected PutObjectResponse putAPICall(PutObjectRequest putObjectRequest, String testString) {
104+
return crossRegionS3Client.putObject(putObjectRequest, AsyncRequestBody.fromString(testString)).join();
105+
}
106+
107+
@Override
108+
protected ResponseBytes<GetObjectResponse> getAPICall(GetObjectRequest getObjectRequest) {
109+
return crossRegionS3Client.getObject(getObjectRequest, AsyncResponseTransformer.toBytes()).join();
110+
}
111+
112+
@Override
113+
protected String bucketName() {
114+
return BUCKET;
115+
}
116+
117+
}
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.services.s3.crossregion;
17+
18+
import static org.assertj.core.api.Assertions.assertThat;
19+
20+
import java.util.Arrays;
21+
import java.util.Collections;
22+
import java.util.List;
23+
import java.util.stream.Collectors;
24+
import java.util.stream.IntStream;
25+
import org.junit.jupiter.api.Test;
26+
import software.amazon.awssdk.core.ResponseBytes;
27+
import software.amazon.awssdk.core.sync.RequestBody;
28+
import software.amazon.awssdk.http.SdkHttpMethod;
29+
import software.amazon.awssdk.regions.Region;
30+
import software.amazon.awssdk.services.s3.S3IntegrationTestBase;
31+
import software.amazon.awssdk.services.s3.S3ServiceClientConfiguration;
32+
import software.amazon.awssdk.services.s3.endpoints.S3EndpointProvider;
33+
import software.amazon.awssdk.services.s3.model.ChecksumAlgorithm;
34+
import software.amazon.awssdk.services.s3.model.ChecksumMode;
35+
import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
36+
import software.amazon.awssdk.services.s3.model.DeleteObjectResponse;
37+
import software.amazon.awssdk.services.s3.model.DeleteObjectsRequest;
38+
import software.amazon.awssdk.services.s3.model.DeleteObjectsResponse;
39+
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
40+
import software.amazon.awssdk.services.s3.model.GetObjectResponse;
41+
import software.amazon.awssdk.services.s3.model.HeadBucketRequest;
42+
import software.amazon.awssdk.services.s3.model.HeadBucketResponse;
43+
import software.amazon.awssdk.services.s3.model.ListObjectsV2Request;
44+
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
45+
import software.amazon.awssdk.services.s3.model.PutObjectResponse;
46+
import software.amazon.awssdk.services.s3.model.S3Object;
47+
48+
public abstract class S3CrossRegionTestBase extends S3IntegrationTestBase {
49+
50+
public static final String X_AMZ_BUCKET_REGION = "x-amz-bucket-region";
51+
public static final Region OVERRIDE_CONFIGURED_REGION = Region.US_WEST_2;
52+
protected static final Region CROSS_REGION = Region.of("eu-central-1");
53+
protected static final String CROSS_REGION_BUCKET = "anyBucket";
54+
protected static final String CHANGED_CROSS_REGION = "us-west-1";
55+
protected static final List<S3Object> S3_OBJECTS = Collections.singletonList(S3Object.builder().key("keyObject").build());
56+
protected static final S3ServiceClientConfiguration CONFIGURED_ENDPOINT_PROVIDER =
57+
S3ServiceClientConfiguration.builder().endpointProvider(S3EndpointProvider.defaultProvider()).build();
58+
private static final String KEY = "key";
59+
protected CaptureInterceptor captureInterceptor;
60+
61+
@Test
62+
void getApi_CrossRegionCall() {
63+
s3.putObject(p -> p.bucket(bucketName()).checksumAlgorithm(ChecksumAlgorithm.CRC32).key(KEY), RequestBody.fromString(
64+
"TEST_STRING"));
65+
GetObjectRequest getObjectRequest =
66+
GetObjectRequest.builder().bucket(bucketName()).checksumMode(ChecksumMode.ENABLED).key(KEY).build();
67+
ResponseBytes<GetObjectResponse> response = getAPICall(getObjectRequest);
68+
assertThat(regionsIntercepted(captureInterceptor.hosts())).isEqualTo(Arrays.asList(CROSS_REGION.id(),
69+
DEFAULT_REGION.id()));
70+
assertThat(captureInterceptor.httpMethods()).isEqualTo(Arrays.asList(SdkHttpMethod.GET, SdkHttpMethod.GET));
71+
assertThat(new String(response.asByteArray())).isEqualTo("TEST_STRING");
72+
assertThat(captureInterceptor.getServiceCalls()).isEqualTo(2);
73+
}
74+
75+
@Test
76+
void putApi_CrossRegionCall() {
77+
s3.putObject(p -> p.bucket(bucketName()).checksumAlgorithm(ChecksumAlgorithm.CRC32).key(KEY), RequestBody.fromString(
78+
"TEST_STRING"));
79+
PutObjectRequest putObjectRequest =
80+
PutObjectRequest.builder().bucket(bucketName()).checksumAlgorithm(ChecksumAlgorithm.CRC32).key(KEY).build();
81+
PutObjectResponse response = putAPICall(putObjectRequest, "TEST_STRING");
82+
assertThat(regionsIntercepted(captureInterceptor.hosts())).isEqualTo(Arrays.asList(CROSS_REGION.id(),
83+
DEFAULT_REGION.id()));
84+
assertThat(captureInterceptor.httpMethods()).isEqualTo(Arrays.asList(SdkHttpMethod.PUT, SdkHttpMethod.PUT));
85+
assertThat(response.checksumCRC32()).isEqualTo("S9ke8w==");
86+
assertThat(captureInterceptor.getServiceCalls()).isEqualTo(2);
87+
}
88+
89+
@Test
90+
void deleteApi_CrossRegionCall() {
91+
s3.putObject(p -> p.bucket(bucketName()).checksumAlgorithm(ChecksumAlgorithm.CRC32).key(KEY), RequestBody.fromString(
92+
"TEST_STRING"));
93+
DeleteObjectRequest deleteObjectRequest = DeleteObjectRequest.builder().bucket(bucketName()).key(KEY).build();
94+
DeleteObjectResponse response = deleteObjectAPICall(deleteObjectRequest);
95+
assertThat(regionsIntercepted(captureInterceptor.hosts())).isEqualTo(Arrays.asList(CROSS_REGION.id(),
96+
DEFAULT_REGION.id()));
97+
assertThat(captureInterceptor.httpMethods()).isEqualTo(Arrays.asList(SdkHttpMethod.DELETE, SdkHttpMethod.DELETE));
98+
assertThat(response).isNotNull();
99+
assertThat(captureInterceptor.getServiceCalls()).isEqualTo(2);
100+
}
101+
102+
@Test
103+
void postApi_CrossRegionCall() {
104+
s3.putObject(p -> p.bucket(bucketName()).checksumAlgorithm(ChecksumAlgorithm.CRC32).key(KEY), RequestBody.fromString(
105+
"TEST_STRING"));
106+
s3.putObject(p -> p.bucket(bucketName()).checksumAlgorithm(ChecksumAlgorithm.CRC32).key(KEY + "_1"),
107+
RequestBody.fromString("TEST_STRING"));
108+
DeleteObjectsRequest deleteObjectsRequest =
109+
DeleteObjectsRequest.builder().bucket(bucketName()).delete(d -> d.objects(o -> o.key(KEY), o -> o.key(KEY + "_1"))).build();
110+
DeleteObjectsResponse response = postObjectAPICall(deleteObjectsRequest);
111+
assertThat(regionsIntercepted(captureInterceptor.hosts())).isEqualTo(Arrays.asList(CROSS_REGION.id(),
112+
DEFAULT_REGION.id()));
113+
assertThat(captureInterceptor.httpMethods()).isEqualTo(Arrays.asList(SdkHttpMethod.POST, SdkHttpMethod.POST));
114+
assertThat(response).isNotNull();
115+
assertThat(captureInterceptor.getServiceCalls()).isEqualTo(2);
116+
}
117+
118+
@Test
119+
void cachedRegionGetsUsed_when_CrossRegionCall() {
120+
putAPICall(PutObjectRequest.builder().bucket(bucketName()).checksumAlgorithm(ChecksumAlgorithm.CRC32).key(KEY).build(),
121+
"TEST_STRING");
122+
getAPICall(GetObjectRequest.builder().bucket(bucketName()).checksumMode(ChecksumMode.ENABLED).key(KEY).build());
123+
assertThat(regionsIntercepted(captureInterceptor.hosts())).isEqualTo(Arrays.asList(CROSS_REGION.id(),
124+
DEFAULT_REGION.id(),
125+
DEFAULT_REGION.id()));
126+
assertThat(captureInterceptor.httpMethods()).isEqualTo(Arrays.asList(SdkHttpMethod.PUT,
127+
SdkHttpMethod.PUT,
128+
SdkHttpMethod.GET));
129+
assertThat(captureInterceptor.getServiceCalls()).isEqualTo(3);
130+
}
131+
132+
@Test
133+
void paginatedApi_CrossRegionCall() {
134+
s3.deleteObject(p -> p.bucket(bucketName()).key(KEY));
135+
int maxKeys = 3;
136+
int totalKeys = maxKeys * 2 ;
137+
IntStream.range(0, totalKeys )
138+
.forEach(
139+
i ->
140+
s3.putObject(p -> p.bucket(bucketName()).checksumAlgorithm(ChecksumAlgorithm.CRC32).key(KEY + "_" + i),
141+
RequestBody.fromString("TEST_STRING"))
142+
);
143+
ListObjectsV2Request listObjectsV2Request = ListObjectsV2Request.builder().bucket(bucketName()).maxKeys(maxKeys).build();
144+
List<S3Object> s3ObjectList = paginatedAPICall(listObjectsV2Request);
145+
assertThat(regionsIntercepted(captureInterceptor.hosts())).isEqualTo(Arrays.asList(CROSS_REGION.id(),
146+
DEFAULT_REGION.id(),
147+
DEFAULT_REGION.id()));
148+
149+
IntStream.range(0, totalKeys ).forEach(i -> s3.deleteObject(p -> p.bucket(bucketName()).key(KEY + "_" + i)));
150+
}
151+
152+
@Test
153+
void headApi_CrossRegionCall() {
154+
s3.putObject(p -> p.bucket(bucketName()).checksumAlgorithm(ChecksumAlgorithm.CRC32).key(KEY), RequestBody.fromString(
155+
"TEST_STRING"));
156+
HeadBucketRequest headBucketRequest = HeadBucketRequest.builder().bucket(bucketName()).build();
157+
HeadBucketResponse response = headAPICall(headBucketRequest);
158+
assertThat(regionsIntercepted(captureInterceptor.hosts())).isEqualTo(Arrays.asList(CROSS_REGION.id(),
159+
DEFAULT_REGION.id()));
160+
assertThat(captureInterceptor.httpMethods()).isEqualTo(Arrays.asList(SdkHttpMethod.HEAD, SdkHttpMethod.HEAD));
161+
assertThat(response).isNotNull();
162+
assertThat(captureInterceptor.getServiceCalls()).isEqualTo(2);
163+
}
164+
165+
protected abstract List<S3Object> paginatedAPICall(ListObjectsV2Request listObjectsV2Request);
166+
167+
protected abstract DeleteObjectsResponse postObjectAPICall(DeleteObjectsRequest deleteObjectsRequest);
168+
169+
protected abstract HeadBucketResponse headAPICall(HeadBucketRequest headBucketRequest);
170+
171+
protected abstract DeleteObjectResponse deleteObjectAPICall(DeleteObjectRequest deleteObjectRequest);
172+
173+
protected abstract PutObjectResponse putAPICall(PutObjectRequest putObjectRequest, String testString);
174+
175+
protected abstract ResponseBytes<GetObjectResponse> getAPICall(GetObjectRequest getObjectRequest);
176+
177+
protected abstract String bucketName();
178+
179+
private List<String> regionsIntercepted(List<String> hosts) {
180+
return hosts.stream()
181+
.map(req -> req.substring(bucketName().length() + 4, req.length() - 14))
182+
.collect(Collectors.toList());
183+
}
184+
185+
}

0 commit comments

Comments
 (0)