Skip to content

Commit abab62e

Browse files
committed
Add support for automatically generating a presigned URL in CopyDBClusterSnapshot and CreateDBClusterPresignInterceptor when SourceRegion is specified.
1 parent f6e3d9f commit abab62e

File tree

9 files changed

+286
-16
lines changed

9 files changed

+286
-16
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"type": "feature",
3+
"category": "Amazon RDS",
4+
"description": "Add SourceRegion to CopyDBClusterSnapshot and CreateDBCluster operations. As with CopyDBSnapshot and CreateDBInstanceReadReplica, specifying this field will automatically populate the PresignedURL field with a valid value."
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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.rds.internal;
17+
18+
import software.amazon.awssdk.annotations.SdkInternalApi;
19+
import software.amazon.awssdk.http.SdkHttpFullRequest;
20+
import software.amazon.awssdk.services.rds.model.CopyDbClusterSnapshotRequest;
21+
import software.amazon.awssdk.services.rds.transform.CopyDbClusterSnapshotRequestMarshaller;
22+
23+
/**
24+
* Handler for pre-signing {@link CopyDbClusterSnapshotRequest}.
25+
*/
26+
@SdkInternalApi
27+
public final class CopyDbClusterSnapshotPresignInterceptor extends RdsPresignInterceptor<CopyDbClusterSnapshotRequest> {
28+
29+
public static final CopyDbClusterSnapshotRequestMarshaller MARSHALLER =
30+
new CopyDbClusterSnapshotRequestMarshaller(PROTOCOL_FACTORY);
31+
32+
public CopyDbClusterSnapshotPresignInterceptor() {
33+
super(CopyDbClusterSnapshotRequest.class);
34+
}
35+
36+
@Override
37+
protected PresignableRequest adaptRequest(final CopyDbClusterSnapshotRequest originalRequest) {
38+
return new PresignableRequest() {
39+
@Override
40+
public String getSourceRegion() {
41+
return originalRequest.sourceRegion();
42+
}
43+
44+
@Override
45+
public SdkHttpFullRequest marshall() {
46+
return MARSHALLER.marshall(originalRequest);
47+
}
48+
};
49+
}
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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.rds.internal;
17+
18+
import software.amazon.awssdk.annotations.SdkInternalApi;
19+
import software.amazon.awssdk.http.SdkHttpFullRequest;
20+
import software.amazon.awssdk.services.rds.model.CreateDbClusterRequest;
21+
import software.amazon.awssdk.services.rds.transform.CreateDbClusterRequestMarshaller;
22+
23+
/**
24+
* Handler for pre-signing {@link CreateDbClusterRequest}.
25+
*/
26+
@SdkInternalApi
27+
public final class CreateDbClusterPresignInterceptor extends RdsPresignInterceptor<CreateDbClusterRequest> {
28+
29+
public static final CreateDbClusterRequestMarshaller MARSHALLER =
30+
new CreateDbClusterRequestMarshaller(PROTOCOL_FACTORY);
31+
32+
public CreateDbClusterPresignInterceptor() {
33+
super(CreateDbClusterRequest.class);
34+
}
35+
36+
@Override
37+
protected PresignableRequest adaptRequest(final CreateDbClusterRequest originalRequest) {
38+
return new PresignableRequest() {
39+
@Override
40+
public String getSourceRegion() {
41+
return originalRequest.sourceRegion();
42+
}
43+
44+
@Override
45+
public SdkHttpFullRequest marshall() {
46+
return MARSHALLER.marshall(originalRequest);
47+
}
48+
};
49+
}
50+
}

services/rds/src/main/java/software/amazon/awssdk/services/rds/internal/RdsPresignInterceptor.java

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@
2121
import java.time.Clock;
2222
import software.amazon.awssdk.annotations.SdkInternalApi;
2323
import software.amazon.awssdk.auth.signer.Aws4Signer;
24+
import software.amazon.awssdk.auth.signer.AwsSignerExecutionAttribute;
2425
import software.amazon.awssdk.auth.signer.params.Aws4PresignerParams;
2526
import software.amazon.awssdk.awscore.endpoint.DefaultServiceEndpointBuilder;
26-
import software.amazon.awssdk.awscore.util.AwsHostNameUtils;
2727
import software.amazon.awssdk.core.Protocol;
2828
import software.amazon.awssdk.core.SdkRequest;
2929
import software.amazon.awssdk.core.client.config.SdkClientConfiguration;
@@ -103,16 +103,10 @@ public final SdkHttpRequest modifyHttpRequest(Context.ModifyHttpRequest context,
103103
return request;
104104
}
105105

106-
String destinationRegion =
107-
AwsHostNameUtils.parseSigningRegion(request.host(), SERVICE_NAME)
108-
.orElseThrow(() -> new IllegalArgumentException("Could not determine region for " +
109-
request.host()))
110-
.id();
106+
String destinationRegion = executionAttributes.getAttribute(AwsSignerExecutionAttribute.SIGNING_REGION).id();
111107

112108
URI endpoint = createEndpoint(sourceRegion, SERVICE_NAME);
113-
SdkHttpFullRequest.Builder marshalledRequest = presignableRequest.marshall()
114-
.toBuilder()
115-
.uri(endpoint);
109+
SdkHttpFullRequest.Builder marshalledRequest = presignableRequest.marshall().toBuilder().uri(endpoint);
116110

117111
SdkHttpFullRequest requestToPresign =
118112
marshalledRequest.method(SdkHttpMethod.GET)

services/rds/src/main/resources/codegen-resources/customization.config

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
// This is for enabling automatic request presigning only; it should not be marshalled
77
"SourceRegion" : {
88
"shape" : "String",
9-
"documentation" : "The region where the source snapshot is located."
9+
"documentation" : "If PreSignedUrl is not specified, this is the region where the source snapshot is located. A PreSignedUrl will be generated automatically by the SDK."
1010
}
1111
}
1212
]
@@ -17,12 +17,33 @@
1717
// This is for enabling automatic request presigning only; it should not be marshalled
1818
"SourceRegion" : {
1919
"shape" : "String",
20-
"documentation" : "The region where the source instance is located."
20+
"documentation" : "If PreSignedUrl is not specified, this is the region where the source snapshot is located. A PreSignedUrl will be generated automatically by the SDK."
21+
}
22+
}
23+
]
24+
},
25+
"CopyDBClusterSnapshotMessage" : {
26+
"inject" : [
27+
{
28+
// This is for enabling automatic request presigning only; it should not be marshalled
29+
"SourceRegion" : {
30+
"shape" : "String",
31+
"documentation" : "If PreSignedUrl is not specified, this is the region where the source snapshot is located. A PreSignedUrl will be generated automatically by the SDK."
32+
}
33+
}
34+
]
35+
},
36+
"CreateDBClusterMessage" : {
37+
"inject" : [
38+
{
39+
// This is for enabling automatic request presigning only; it should not be marshalled
40+
"SourceRegion" : {
41+
"shape" : "String",
42+
"documentation" : "If PreSignedUrl is not specified, this is the region where the source snapshot is located. A PreSignedUrl will be generated automatically by the SDK."
2143
}
2244
}
2345
]
2446
}
25-
2647
},
2748
"blacklistedSimpleMethods" : ["failoverDBCluster"],
2849
"deprecatedShapes" : [
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
1+
software.amazon.awssdk.services.rds.internal.CopyDbClusterSnapshotPresignInterceptor
12
software.amazon.awssdk.services.rds.internal.CopyDbSnapshotPresignInterceptor
2-
software.amazon.awssdk.services.rds.internal.CreateDbInstanceReadReplicaPresignInterceptor
3+
software.amazon.awssdk.services.rds.internal.CreateDbClusterPresignInterceptor
4+
software.amazon.awssdk.services.rds.internal.CreateDbInstanceReadReplicaPresignInterceptor

services/rds/src/test/java/software/amazon/awssdk/services/rds/internal/PresignRequestHandlerTest.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,6 @@
4040
import software.amazon.awssdk.http.SdkHttpFullRequest;
4141
import software.amazon.awssdk.http.SdkHttpRequest;
4242
import software.amazon.awssdk.regions.Region;
43-
import software.amazon.awssdk.services.rds.internal.CopyDbSnapshotPresignInterceptor;
44-
import software.amazon.awssdk.services.rds.internal.RdsPresignInterceptor;
4543
import software.amazon.awssdk.services.rds.model.CopyDbSnapshotRequest;
4644
import software.amazon.awssdk.services.rds.model.RdsRequest;
4745
import software.amazon.awssdk.services.rds.transform.CopyDbSnapshotRequestMarshaller;
@@ -163,7 +161,8 @@ private SdkHttpFullRequest marshallRequest(CopyDbSnapshotRequest request) {
163161
}
164162

165163
private ExecutionAttributes executionAttributes() {
166-
return new ExecutionAttributes().putAttribute(AwsSignerExecutionAttribute.AWS_CREDENTIALS, CREDENTIALS);
164+
return new ExecutionAttributes().putAttribute(AwsSignerExecutionAttribute.AWS_CREDENTIALS, CREDENTIALS)
165+
.putAttribute(AwsSignerExecutionAttribute.SIGNING_REGION, DESTINATION_REGION);
167166
}
168167

169168
private CopyDbSnapshotRequest makeTestRequest() {
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
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.rds.internal;
17+
18+
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
19+
import static com.github.tomakehurst.wiremock.client.WireMock.any;
20+
import static com.github.tomakehurst.wiremock.client.WireMock.anyRequestedFor;
21+
import static com.github.tomakehurst.wiremock.client.WireMock.anyUrl;
22+
import static com.github.tomakehurst.wiremock.client.WireMock.findAll;
23+
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
24+
import static java.nio.charset.StandardCharsets.UTF_8;
25+
import static org.assertj.core.api.Assertions.assertThat;
26+
27+
import com.github.tomakehurst.wiremock.junit.WireMockRule;
28+
import com.github.tomakehurst.wiremock.verification.LoggedRequest;
29+
import java.net.URI;
30+
import java.util.List;
31+
import org.junit.Before;
32+
import org.junit.BeforeClass;
33+
import org.junit.ClassRule;
34+
import org.junit.Test;
35+
import org.junit.runner.RunWith;
36+
import org.mockito.runners.MockitoJUnitRunner;
37+
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
38+
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
39+
import software.amazon.awssdk.regions.Region;
40+
import software.amazon.awssdk.services.rds.RdsClient;
41+
42+
@RunWith(MockitoJUnitRunner.class)
43+
public class PresignRequestWireMockTest {
44+
@ClassRule
45+
public static final WireMockRule WIRE_MOCK = new WireMockRule(0);
46+
47+
public static RdsClient client;
48+
49+
@BeforeClass
50+
public static void setup() {
51+
client = RdsClient.builder()
52+
.credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create("akid", "skid")))
53+
.region(Region.US_EAST_1)
54+
.endpointOverride(URI.create("http://localhost:" + WIRE_MOCK.port()))
55+
.build();
56+
}
57+
58+
@Before
59+
public void reset() {
60+
WIRE_MOCK.resetAll();
61+
}
62+
63+
@Test
64+
public void copyDbClusterSnapshotWithSourceRegionSendsPresignedUrl() {
65+
verifyMethodCallSendsPresignedUrl(() -> client.copyDBClusterSnapshot(r -> r.sourceRegion("us-west-2")),
66+
"CopyDBClusterSnapshot");
67+
}
68+
69+
@Test
70+
public void copyDBSnapshotWithSourceRegionSendsPresignedUrl() {
71+
verifyMethodCallSendsPresignedUrl(() -> client.copyDBSnapshot(r -> r.sourceRegion("us-west-2")),
72+
"CopyDBSnapshot");
73+
}
74+
75+
@Test
76+
public void createDbClusterWithSourceRegionSendsPresignedUrl() {
77+
verifyMethodCallSendsPresignedUrl(() -> client.createDBCluster(r -> r.sourceRegion("us-west-2")),
78+
"CreateDBCluster");
79+
}
80+
81+
@Test
82+
public void createDBInstanceReadReplicaWithSourceRegionSendsPresignedUrl() {
83+
verifyMethodCallSendsPresignedUrl(() -> client.createDBInstanceReadReplica(r -> r.sourceRegion("us-west-2")),
84+
"CreateDBInstanceReadReplica");
85+
}
86+
87+
public void verifyMethodCallSendsPresignedUrl(Runnable methodCall, String actionName) {
88+
stubFor(any(anyUrl()).willReturn(aResponse().withStatus(200).withBody("<body/>")));
89+
90+
methodCall.run();
91+
92+
List<LoggedRequest> requests = findAll(anyRequestedFor(anyUrl()));
93+
94+
assertThat(requests).isNotEmpty();
95+
96+
LoggedRequest lastRequest = requests.get(0);
97+
String lastRequestBody = new String(lastRequest.getBody(), UTF_8);
98+
assertThat(lastRequestBody).contains("PreSignedUrl=https%3A%2F%2Frds.us-west-2.amazonaws.com%3FAction%3D" + actionName
99+
+ "%26Version%3D2014-10-31%26DestinationRegion%3Dus-east-1%26");
100+
}
101+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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.rds.internal;
17+
18+
import java.time.Duration;
19+
import java.util.List;
20+
import org.junit.AfterClass;
21+
import org.junit.BeforeClass;
22+
import org.junit.Test;
23+
import software.amazon.awssdk.regions.Region;
24+
import software.amazon.awssdk.services.rds.RdsClient;
25+
import software.amazon.awssdk.services.rds.model.DBCluster;
26+
import software.amazon.awssdk.services.rds.model.DBInstance;
27+
import software.amazon.awssdk.testutils.Waiter;
28+
import software.amazon.awssdk.testutils.service.AwsIntegrationTestBase;
29+
30+
public class RdsPresignInterceptorIntegrationTest extends AwsIntegrationTestBase {
31+
private static final RdsClient US_WEST_2_CLIENT = RdsClient.builder()
32+
.region(Region.US_WEST_2)
33+
.credentialsProvider(CREDENTIALS_PROVIDER_CHAIN)
34+
.build();
35+
36+
@Test
37+
public void createDbClusterReplicaPresigningWorks() {
38+
US_WEST_2_CLIENT.createDBCluster(r -> r.dbClusterIdentifier("database-2-replica")
39+
.sourceRegion("us-east-1")
40+
.engine("aurora")
41+
.storageEncrypted(true)
42+
.kmsKeyId("aws/rds")
43+
.replicationSourceIdentifier("arn:aws:rds:us-east-1:295513896279:cluster:database-2"));
44+
45+
US_WEST_2_CLIENT.deleteDBCluster(r -> r.dbClusterIdentifier("database-2-replica")
46+
.skipFinalSnapshot(true));
47+
}
48+
}

0 commit comments

Comments
 (0)