Skip to content

Commit 9727a77

Browse files
author
Keynan Pratt
committed
[CFN-27] Working towards a more extensible DBInstance create flow. Static methods and error rules have been pulled out of the base handler class; The instance create factory is now constructed by factory according to the model.
[CFN-27] Continuing to seperate db-instance create flows. This pulls out the create from snapshot logic at the cost of some more copy pasta to be addressed in the next commit. [CFN-27] The 'safeAddTags' method is still c/p but the core change in pattern has finished resulting in a trim create step in the create handler (line 103). ...
1 parent fc6dbea commit 9727a77

File tree

13 files changed

+733
-366
lines changed

13 files changed

+733
-366
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package software.amazon.rds;
2+
3+
public class ProgressStep {
4+
}

aws-rds-dbinstance/pom.xml

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<project
3-
xmlns="http://maven.apache.org/POM/4.0.0"
4-
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5-
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
3+
xmlns="http://maven.apache.org/POM/4.0.0"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
66
<modelVersion>4.0.0</modelVersion>
77

88
<groupId>software.amazon.rds.dbinstance</groupId>
@@ -267,7 +267,7 @@
267267
<limit>
268268
<counter>BRANCH</counter>
269269
<value>COVEREDRATIO</value>
270-
<minimum>0.8</minimum>
270+
<minimum>0.82</minimum>
271271
</limit>
272272
<limit>
273273
<counter>INSTRUCTION</counter>

aws-rds-dbinstance/src/main/java/software/amazon/rds/dbinstance/BaseHandlerStd.java

+6-4
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@ public abstract class BaseHandlerStd extends BaseHandler<CallbackContext> {
8080

8181
protected static final RuntimeException MISSING_METHOD_VERSION_EXCEPTION = new RuntimeException("Missing method version");
8282

83-
8483
protected static final String ILLEGAL_DELETION_POLICY_ERROR = "DeletionPolicy:Snapshot cannot be specified for a cluster instance, use deletion policy on the cluster instead.";
8584

8685
protected static final String UNKNOWN_SOURCE_REGION_ERROR = "Unknown source region";
@@ -395,8 +394,10 @@ protected boolean isInstanceStabilizedAfterReplicationStop(
395394
return DBInstancePredicates.isInstanceStabilizedAfterReplicationStop(dbInstance, model);
396395
}
397396

398-
protected boolean isInstanceStabilizedAfterReplicationStart(final ProxyClient<RdsClient> rdsProxyClient,
399-
final ResourceModel model) {
397+
protected boolean isInstanceStabilizedAfterReplicationStart(
398+
final ProxyClient<RdsClient> rdsProxyClient,
399+
final ResourceModel model
400+
) {
400401
final DBInstance dbInstance = fetchDBInstance(rdsProxyClient, model);
401402

402403
return DBInstancePredicates.isInstanceStabilizedAfterReplicationStart(dbInstance, model);
@@ -519,6 +520,7 @@ protected ProgressEvent<ResourceModel, CallbackContext> addNewRoles(
519520
requestLogger
520521
))
521522
.success();
523+
522524
if (!progressEvent.isSuccess()) {
523525
return progressEvent;
524526
}
@@ -547,7 +549,7 @@ protected ProgressEvent<ResourceModel, CallbackContext> removeOldRoles(
547549
.handleError((request, exception, proxyInvocation, resourceModel, context) -> Commons.handleException(
548550
ProgressEvent.progress(resourceModel, context),
549551
exception,
550-
software.amazon.rds.dbinstance.common.ErrorRuleSets.UPDATE_ASSOCIATED_ROLES,
552+
ErrorRuleSets.UPDATE_ASSOCIATED_ROLES,
551553
requestLogger
552554
))
553555
.success();

aws-rds-dbinstance/src/main/java/software/amazon/rds/dbinstance/CreateHandler.java

+156-357
Large diffs are not rendered by default.

aws-rds-dbinstance/src/main/java/software/amazon/rds/dbinstance/Translator.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ public static CreateDbInstanceRequest createDbInstanceRequest(
345345
return builder.build();
346346
}
347347

348-
static RestoreDbInstanceToPointInTimeRequest restoreDbInstanceToPointInTimeRequest(
348+
public static RestoreDbInstanceToPointInTimeRequest restoreDbInstanceToPointInTimeRequest(
349349
final ResourceModel model,
350350
final Tagging.TagSet tagSet
351351
) {

aws-rds-dbinstance/src/main/java/software/amazon/rds/dbinstance/client/ApiVersionDispatcher.java

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package software.amazon.rds.dbinstance.client;
22

3+
import javax.annotation.Nonnull;
34
import java.util.Comparator;
45
import java.util.Map;
56
import java.util.TreeMap;
@@ -24,6 +25,7 @@ public ApiVersionDispatcher<M, C> register(final ApiVersion version, final BiPre
2425
* @param context An instance of Context.
2526
* @return Returns the highest {@code ApiVersion} matching the input model and context. If no matchers triggered, DEFAULT version is returned (hence no need to register a match-all tester for DEFAULT version).
2627
*/
28+
@Nonnull
2729
public ApiVersion dispatch(final M model, final C context) {
2830
for (final Map.Entry<ApiVersion, BiPredicate<M, C>> entry : versionTesters.entrySet()) {
2931
if (entry.getValue().test(model, context)) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package software.amazon.rds.dbinstance.common;
2+
3+
import lombok.AllArgsConstructor;
4+
import software.amazon.awssdk.services.rds.RdsClient;
5+
import software.amazon.awssdk.services.rds.model.DBInstance;
6+
import software.amazon.awssdk.services.rds.model.DBSnapshot;
7+
import software.amazon.awssdk.services.rds.model.DescribeDbInstancesResponse;
8+
import software.amazon.awssdk.services.rds.model.DescribeDbSnapshotsResponse;
9+
import software.amazon.cloudformation.proxy.ProxyClient;
10+
import software.amazon.rds.dbinstance.ResourceModel;
11+
import software.amazon.rds.dbinstance.Translator;
12+
13+
@AllArgsConstructor
14+
public class Fetch {
15+
private final ProxyClient<RdsClient> rdsProxyClient;
16+
17+
public DBInstance dbInstance(final ResourceModel model) {
18+
final DescribeDbInstancesResponse response = rdsProxyClient.injectCredentialsAndInvokeV2(
19+
Translator.describeDbInstancesRequest(model),
20+
rdsProxyClient.client()::describeDBInstances
21+
);
22+
return response.dbInstances().get(0);
23+
}
24+
25+
public DBSnapshot dbSnapshot(final ResourceModel model) {
26+
final DescribeDbSnapshotsResponse response = rdsProxyClient.injectCredentialsAndInvokeV2(
27+
Translator.describeDbSnapshotsRequest(model),
28+
rdsProxyClient.client()::describeDBSnapshots
29+
);
30+
return response.dbSnapshots().get(0);
31+
}
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package software.amazon.rds.dbinstance.common.create;
2+
3+
import software.amazon.cloudformation.proxy.ProgressEvent;
4+
import software.amazon.rds.dbinstance.CallbackContext;
5+
import software.amazon.rds.dbinstance.ResourceModel;
6+
import software.amazon.rds.dbinstance.util.ResourceModelHelper;
7+
8+
public interface DBInstanceFactory {
9+
10+
ProgressEvent<ResourceModel, CallbackContext> create(
11+
ProgressEvent<ResourceModel, CallbackContext> input
12+
);
13+
14+
boolean modelSatisfiesConstructor(ResourceModel model);
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package software.amazon.rds.dbinstance.common.create;
2+
3+
import lombok.AllArgsConstructor;
4+
import software.amazon.awssdk.services.rds.RdsClient;
5+
import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy;
6+
import software.amazon.rds.common.handler.HandlerConfig;
7+
import software.amazon.rds.common.handler.Tagging;
8+
import software.amazon.rds.common.logging.RequestLogger;
9+
import software.amazon.rds.dbinstance.CallbackContext;
10+
import software.amazon.rds.dbinstance.ResourceModel;
11+
import software.amazon.rds.dbinstance.client.ApiVersionDispatcher;
12+
import software.amazon.rds.dbinstance.client.VersionedProxyClient;
13+
import software.amazon.rds.dbinstance.util.ResourceModelHelper;
14+
15+
import java.util.ArrayList;
16+
import java.util.Collection;
17+
import java.util.Optional;
18+
19+
@AllArgsConstructor
20+
public class DBInstanceFactoryFactory {
21+
22+
private final Collection<DBInstanceFactory> factories;
23+
private final DBInstanceFactory defaultFactory;
24+
25+
public DBInstanceFactoryFactory(
26+
final AmazonWebServicesClientProxy proxy,
27+
final VersionedProxyClient<RdsClient> rdsProxyClient,
28+
final Tagging.TagSet allTags,
29+
final RequestLogger requestLogger,
30+
final HandlerConfig config,
31+
final ApiVersionDispatcher<ResourceModel, CallbackContext> apiVersionDispatcher
32+
) {
33+
34+
factories = new ArrayList<>();
35+
// The order of this list matters. Do NOT re-order.
36+
factories.add(new FromPointInTime(
37+
proxy,
38+
rdsProxyClient,
39+
allTags,
40+
requestLogger,
41+
config
42+
));
43+
factories.add(new ReadReplica(
44+
proxy,
45+
rdsProxyClient,
46+
allTags,
47+
requestLogger,
48+
config
49+
));
50+
factories.add(new FromSnapshot(
51+
proxy,
52+
rdsProxyClient,
53+
allTags,
54+
requestLogger,
55+
config,
56+
apiVersionDispatcher
57+
));
58+
59+
defaultFactory = new FreshInstance(
60+
proxy,
61+
rdsProxyClient,
62+
allTags,
63+
requestLogger,
64+
config,
65+
apiVersionDispatcher
66+
);
67+
}
68+
69+
public DBInstanceFactory createFactory(ResourceModel model) {
70+
return discernFactoryType(model).orElse(defaultFactory);
71+
}
72+
73+
private Optional<DBInstanceFactory> discernFactoryType(ResourceModel model) {
74+
for (DBInstanceFactory fac : factories) {
75+
if (fac.modelSatisfiesConstructor(model)) {
76+
return Optional.of(fac);
77+
}
78+
}
79+
return Optional.empty();
80+
}
81+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
package software.amazon.rds.dbinstance.common.create;
2+
3+
import lombok.AllArgsConstructor;
4+
import software.amazon.awssdk.services.rds.RdsClient;
5+
import software.amazon.awssdk.services.rds.model.DBInstance;
6+
import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy;
7+
import software.amazon.cloudformation.proxy.ProgressEvent;
8+
import software.amazon.cloudformation.proxy.ProxyClient;
9+
import software.amazon.rds.common.handler.Commons;
10+
import software.amazon.rds.common.handler.HandlerConfig;
11+
import software.amazon.rds.common.handler.HandlerMethod;
12+
import software.amazon.rds.common.handler.Tagging;
13+
import software.amazon.rds.common.logging.RequestLogger;
14+
import software.amazon.rds.dbinstance.CallbackContext;
15+
import software.amazon.rds.dbinstance.DBInstancePredicates;
16+
import software.amazon.rds.dbinstance.ResourceModel;
17+
import software.amazon.rds.dbinstance.Translator;
18+
import software.amazon.rds.dbinstance.client.ApiVersion;
19+
import software.amazon.rds.dbinstance.client.ApiVersionDispatcher;
20+
import software.amazon.rds.dbinstance.client.VersionedProxyClient;
21+
import software.amazon.rds.dbinstance.common.ErrorRuleSets;
22+
import software.amazon.rds.dbinstance.common.Fetch;
23+
24+
@AllArgsConstructor
25+
public class FreshInstance implements DBInstanceFactory {
26+
27+
private final AmazonWebServicesClientProxy proxy;
28+
private final VersionedProxyClient<RdsClient> rdsProxyClient;
29+
private final Tagging.TagSet allTags;
30+
private final RequestLogger requestLogger;
31+
private final HandlerConfig config;
32+
private final ApiVersionDispatcher<ResourceModel, CallbackContext> apiVersionDispatcher;
33+
34+
@Override
35+
public ProgressEvent<ResourceModel, CallbackContext> create(ProgressEvent<ResourceModel, CallbackContext> progress) {
36+
final ResourceModel model = progress.getResourceModel();
37+
final CallbackContext callbackContext = progress.getCallbackContext();
38+
final ApiVersion apiVersion = selectVersion(model, callbackContext);
39+
40+
if (apiVersion == ApiVersion.V12) {
41+
return createDbInstanceV12(proxy, rdsProxyClient.forVersion(apiVersion), progress, allTags);
42+
}
43+
44+
return safeAddTags(this::createDbInstance).invoke(proxy, rdsProxyClient.forVersion(apiVersion), progress, allTags);
45+
}
46+
47+
@Override
48+
public boolean modelSatisfiesConstructor(ResourceModel model) {
49+
return true;
50+
}
51+
52+
private ProgressEvent<ResourceModel, CallbackContext> createDbInstance(
53+
final AmazonWebServicesClientProxy proxy,
54+
final ProxyClient<RdsClient> rdsProxyClient,
55+
final ProgressEvent<ResourceModel, CallbackContext> progress,
56+
final Tagging.TagSet tagSet
57+
) {
58+
final Fetch fetch = new Fetch(rdsProxyClient);
59+
return proxy.initiate(
60+
"rds::create-db-instance",
61+
rdsProxyClient,
62+
progress.getResourceModel(),
63+
progress.getCallbackContext()
64+
).translateToServiceRequest(model -> Translator.createDbInstanceRequest(model, tagSet))
65+
.backoffDelay(config.getBackoff())
66+
.makeServiceCall((createRequest, proxyInvocation) -> proxyInvocation.injectCredentialsAndInvokeV2(
67+
createRequest,
68+
proxyInvocation.client()::createDBInstance
69+
))
70+
.stabilize((request, response, proxyInvocation, model, context) -> {
71+
final DBInstance dbInstance = fetch.dbInstance(model);
72+
return DBInstancePredicates.isDBInstanceStabilizedAfterMutate(dbInstance, model, context, requestLogger);
73+
})
74+
.handleError((request, exception, client, model, context) -> Commons.handleException(
75+
ProgressEvent.progress(model, context),
76+
exception,
77+
software.amazon.rds.dbinstance.common.ErrorRuleSets.CREATE_DB_INSTANCE,
78+
requestLogger
79+
))
80+
.progress();
81+
}
82+
83+
private ProgressEvent<ResourceModel, CallbackContext> createDbInstanceV12(
84+
final AmazonWebServicesClientProxy proxy,
85+
final ProxyClient<RdsClient> rdsProxyClient,
86+
final ProgressEvent<ResourceModel, CallbackContext> progress,
87+
final Tagging.TagSet tagSet
88+
) {
89+
requestLogger.log("CreateDbInstanceAPIv12Invoked");
90+
requestLogger.log("API version 12 create detected",
91+
"This indicates that the customer is using DBSecurityGroup, which may result in certain features not" +
92+
" functioning properly. Please refer to the API model for supported parameters");
93+
final Fetch fetch = new Fetch(rdsProxyClient);
94+
return proxy.initiate(
95+
"rds::create-db-instance-v12",
96+
rdsProxyClient,
97+
progress.getResourceModel(),
98+
progress.getCallbackContext()
99+
).translateToServiceRequest(Translator::createDbInstanceRequestV12)
100+
.backoffDelay(config.getBackoff())
101+
.makeServiceCall((createRequest, proxyInvocation) -> proxyInvocation.injectCredentialsAndInvokeV2(
102+
createRequest,
103+
proxyInvocation.client()::createDBInstance
104+
))
105+
.stabilize((request, response, proxyInvocation, model, context) -> {
106+
final DBInstance dbInstance = fetch.dbInstance(model);
107+
return DBInstancePredicates.isDBInstanceStabilizedAfterMutate(dbInstance, model, context, requestLogger);
108+
})
109+
.handleError((request, exception, client, model, context) -> Commons.handleException(
110+
ProgressEvent.progress(model, context),
111+
exception,
112+
ErrorRuleSets.CREATE_DB_INSTANCE,
113+
requestLogger
114+
))
115+
.progress();
116+
}
117+
118+
private ApiVersion selectVersion(ResourceModel model, CallbackContext callbackContext) {
119+
return apiVersionDispatcher.dispatch(model, callbackContext);
120+
}
121+
122+
private HandlerMethod<ResourceModel, CallbackContext> safeAddTags(final HandlerMethod<ResourceModel, CallbackContext> handlerMethod) {
123+
return (proxy, rdsProxyClient, progress, tagSet) -> progress.then(p -> Tagging.createWithTaggingFallback(proxy, rdsProxyClient, handlerMethod, progress, tagSet));
124+
}
125+
}

0 commit comments

Comments
 (0)