From 93c4015f9090725349c3939f6184267e21e2a559 Mon Sep 17 00:00:00 2001 From: Philip Cai Date: Fri, 11 Oct 2024 00:36:49 +0000 Subject: [PATCH] [DBShardGroup] Update handler with tagOnCreate, enable computeRedundancy on updates and refactor for simpler stabilization cr: https://code.amazon.com/reviews/CR-154713945 --- .../aws-rds-dbshardgroup.json | 4 +- aws-rds-dbshardgroup/docs/README.md | 2 +- aws-rds-dbshardgroup/pom.xml | 37 +- .../rds/dbshardgroup/BaseHandlerStd.java | 366 ++++++++++-------- .../rds/dbshardgroup/CallbackContext.java | 24 +- .../rds/dbshardgroup/CreateHandler.java | 64 ++- .../rds/dbshardgroup/DeleteHandler.java | 47 +-- .../rds/dbshardgroup/ResourceStatus.java | 9 - .../amazon/rds/dbshardgroup/Translator.java | 5 +- .../rds/dbshardgroup/UpdateHandler.java | 18 +- .../rds/dbshardgroup/AbstractHandlerTest.java | 284 +++++++------- .../rds/dbshardgroup/CreateHandlerTest.java | 7 +- .../rds/dbshardgroup/UpdateHandlerTest.java | 2 +- 13 files changed, 438 insertions(+), 431 deletions(-) diff --git a/aws-rds-dbshardgroup/aws-rds-dbshardgroup.json b/aws-rds-dbshardgroup/aws-rds-dbshardgroup.json index 33cc1926..e5e8f8c9 100644 --- a/aws-rds-dbshardgroup/aws-rds-dbshardgroup.json +++ b/aws-rds-dbshardgroup/aws-rds-dbshardgroup.json @@ -9,7 +9,7 @@ "rds:RemoveTagsFromResource" ], "taggable": true, - "tagOnCreate": false, + "tagOnCreate": true, "tagUpdatable": true, "tagProperty": "/properties/Tags" }, @@ -32,6 +32,7 @@ }, "ComputeRedundancy": { "description": "Specifies whether to create standby instances for the DB shard group.", + "minimum": 0, "type": "integer" }, "MaxACU": { @@ -95,7 +96,6 @@ "MaxACU" ], "createOnlyProperties": [ - "/properties/ComputeRedundancy", "/properties/DBClusterIdentifier", "/properties/DBShardGroupIdentifier", "/properties/PubliclyAccessible" diff --git a/aws-rds-dbshardgroup/docs/README.md b/aws-rds-dbshardgroup/docs/README.md index 143d055d..2d27c18a 100644 --- a/aws-rds-dbshardgroup/docs/README.md +++ b/aws-rds-dbshardgroup/docs/README.md @@ -76,7 +76,7 @@ _Required_: No _Type_: Integer -_Update requires_: [Replacement](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-replacement) +_Update requires_: [No interruption](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-no-interrupt) #### MaxACU diff --git a/aws-rds-dbshardgroup/pom.xml b/aws-rds-dbshardgroup/pom.xml index 343215a8..6c01c7de 100644 --- a/aws-rds-dbshardgroup/pom.xml +++ b/aws-rds-dbshardgroup/pom.xml @@ -21,6 +21,16 @@ + + org.apache.commons + commons-lang3 + 3.12.0 + + + software.amazon.rds.common + aws-rds-cfn-common + 1.0 + software.amazon.awssdk rds @@ -36,10 +46,15 @@ org.projectlombok lombok - 1.18.22 + ${org.projectlombok.version} provided - + + + software.amazon.awssdk + aws-query-protocol + 2.20.138 + org.assertj @@ -68,12 +83,6 @@ 4.3.1 test - - software.amazon.rds.common - aws-rds-cfn-common - 1.0 - compile - software.amazon.rds.common aws-rds-cfn-test-common @@ -89,6 +98,15 @@ maven-compiler-plugin 3.12.1 + ${java.version} + ${java.version} + + + org.projectlombok + lombok + ${org.projectlombok.version} + + -Xlint:all,-options,-processing @@ -97,7 +115,7 @@ org.apache.maven.plugins maven-shade-plugin - 2.3 + 3.2.4 false @@ -154,6 +172,7 @@ 2.4 + org.apache.maven.plugins maven-surefire-plugin 3.0.0-M3 diff --git a/aws-rds-dbshardgroup/src/main/java/software/amazon/rds/dbshardgroup/BaseHandlerStd.java b/aws-rds-dbshardgroup/src/main/java/software/amazon/rds/dbshardgroup/BaseHandlerStd.java index 44859939..aa2189e0 100644 --- a/aws-rds-dbshardgroup/src/main/java/software/amazon/rds/dbshardgroup/BaseHandlerStd.java +++ b/aws-rds-dbshardgroup/src/main/java/software/amazon/rds/dbshardgroup/BaseHandlerStd.java @@ -15,6 +15,7 @@ import software.amazon.awssdk.services.rds.model.MaxDbShardGroupLimitReachedException; import software.amazon.awssdk.services.rds.model.Tag; import software.amazon.awssdk.services.rds.model.UnsupportedDbEngineVersionException; +import software.amazon.awssdk.utils.ImmutableMap; import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy; import software.amazon.cloudformation.proxy.HandlerErrorCode; import software.amazon.cloudformation.proxy.Logger; @@ -35,60 +36,64 @@ import java.util.Collection; public abstract class BaseHandlerStd extends BaseHandler { - protected final static HandlerConfig DEFAULT_DB_SHARD_GROUP_HANDLER_CONFIG = HandlerConfig.builder() - .backoff(Constant.of().delay(Duration.ofSeconds(30)).timeout(Duration.ofMinutes(180)).build()) - .build(); - - protected final static HandlerConfig DB_SHARD_GROUP_HANDLER_CONFIG_36H = HandlerConfig.builder() - .backoff(Constant.of().delay(Duration.ofSeconds(30)).timeout(Duration.ofHours(36)).build()) - .build(); - - protected static final ErrorRuleSet DEFAULT_DB_SHARD_GROUP_ERROR_RULE_SET = ErrorRuleSet - .extend(Commons.DEFAULT_ERROR_RULE_SET) - .withErrorClasses(ErrorStatus.failWith(HandlerErrorCode.AlreadyExists), - DbShardGroupAlreadyExistsException.class) - .withErrorClasses(ErrorStatus.failWith(HandlerErrorCode.NotFound), - DbShardGroupNotFoundException.class, - DbClusterNotFoundException.class) - .withErrorClasses(ErrorStatus.failWith(HandlerErrorCode.ServiceLimitExceeded), - MaxDbShardGroupLimitReachedException.class) - .withErrorClasses(ErrorStatus.failWith(HandlerErrorCode.InvalidRequest), - UnsupportedDbEngineVersionException.class) - .withErrorClasses(ErrorStatus.failWith(HandlerErrorCode.ResourceConflict), - InvalidDbClusterStateException.class, - InvalidVpcNetworkStateException.class) - .build(); - - private final FilteredJsonPrinter PARAMETERS_FILTER = new FilteredJsonPrinter(); - - /** Custom handler config, mostly to facilitate faster unit test */ - final HandlerConfig config; - protected RequestLogger requestLogger; - protected static final BiFunction, ResourceModel> NOOP_CALL = (model, proxyClient) -> model; - - public BaseHandlerStd() { - this(HandlerConfig.builder().build()); - } - - BaseHandlerStd(HandlerConfig config) { - this.config = config; - } - - @Override - public final ProgressEvent handleRequest( - final AmazonWebServicesClientProxy proxy, - final ResourceHandlerRequest request, - final CallbackContext callbackContext, - final Logger logger) { - return RequestLogger.handleRequest( - logger, - request, - PARAMETERS_FILTER, - requestLogger -> handleRequest( - proxy, - new LoggingProxyClient<>(requestLogger, proxy.newProxy(new ClientProvider()::getClient)), request, - callbackContext != null ? callbackContext : new CallbackContext() - )); + protected final static HandlerConfig DEFAULT_DB_SHARD_GROUP_HANDLER_CONFIG = HandlerConfig.builder() + .backoff(Constant.of().delay(Duration.ofSeconds(30)).timeout(Duration.ofMinutes(180)).build()) + .build(); + + protected final static HandlerConfig DB_SHARD_GROUP_HANDLER_CONFIG_36H = HandlerConfig.builder() + .backoff(Constant.of().delay(Duration.ofSeconds(30)).timeout(Duration.ofHours(36)).build()) + .build(); + + protected static final ErrorRuleSet DEFAULT_DB_SHARD_GROUP_ERROR_RULE_SET = ErrorRuleSet + .extend(Commons.DEFAULT_ERROR_RULE_SET) + .withErrorClasses(ErrorStatus.failWith(HandlerErrorCode.AlreadyExists), + DbShardGroupAlreadyExistsException.class) + .withErrorClasses(ErrorStatus.failWith(HandlerErrorCode.NotFound), + DbShardGroupNotFoundException.class, + DbClusterNotFoundException.class) + .withErrorClasses(ErrorStatus.failWith(HandlerErrorCode.ServiceLimitExceeded), + MaxDbShardGroupLimitReachedException.class) + .withErrorClasses(ErrorStatus.failWith(HandlerErrorCode.InvalidRequest), + UnsupportedDbEngineVersionException.class) + .withErrorClasses(ErrorStatus.failWith(HandlerErrorCode.ResourceConflict), + InvalidDbClusterStateException.class, + InvalidVpcNetworkStateException.class) + .build(); + + private final FilteredJsonPrinter PARAMETERS_FILTER = new FilteredJsonPrinter(); + + /** + * Custom handler config, mostly to facilitate faster unit test + */ + final HandlerConfig config; + protected RequestLogger requestLogger; + protected static final BiFunction, ResourceModel> NOOP_CALL = (model, proxyClient) -> model; + + public BaseHandlerStd() { + this(HandlerConfig.builder().build()); + } + + BaseHandlerStd(HandlerConfig config) { + this.config = config; + } + + @Override + public final ProgressEvent handleRequest( + final AmazonWebServicesClientProxy proxy, + final ResourceHandlerRequest request, + final CallbackContext callbackContext, + final Logger logger) { + return RequestLogger.handleRequest( + logger, + request, + PARAMETERS_FILTER, + requestLogger -> handleRequest( + proxy, + new LoggingProxyClient<>(requestLogger, proxy.newProxy(new ClientProvider()::getClient)), + request, + callbackContext != null ? callbackContext : new CallbackContext(), + requestLogger + )); } protected abstract ProgressEvent handleRequest( @@ -106,136 +111,153 @@ protected ProgressEvent handleRequest( ) { this.requestLogger = requestLogger; return handleRequest(proxy, proxyClient, request, context); - }; - - protected boolean isDBShardGroupStabilized( - final ResourceModel model, - final ProxyClient proxyClient - ) { - final DBShardGroup dbShardGroup = proxyClient.injectCredentialsAndInvokeV2( - Translator.describeDbShardGroupsRequest(model), - proxyClient.client()::describeDBShardGroups - ).dbShardGroups().get(0); - - return isDBShardGroupAvailable(dbShardGroup); - } - - protected boolean isDBClusterStabilized( - final ResourceModel model, - final ProxyClient proxyClient - ) { - final DBCluster dbCluster = proxyClient.injectCredentialsAndInvokeV2( - Translator.describeDbClustersRequest(model), - proxyClient.client()::describeDBClusters - ).dbClusters().get(0); - - return isDBClusterAvailable(dbCluster); - } - - protected boolean isDBShardGroupAvailable(final DBShardGroup dbShardGroup) { - return ResourceStatus.AVAILABLE.equalsString(dbShardGroup.status()); - } - - protected boolean isDBClusterAvailable(final DBCluster dbCluster) { - return ResourceStatus.AVAILABLE.equalsString(dbCluster.status()); - } - - protected ProgressEvent addTags( - final ProxyClient proxyClient, - final ResourceHandlerRequest request, - final ProgressEvent progress, - final Tagging.TagSet desiredTags - ) { - DBShardGroup dbShardGroup; - try { - dbShardGroup = proxyClient.injectCredentialsAndInvokeV2( - Translator.describeDbShardGroupsRequest(progress.getResourceModel()), proxyClient.client()::describeDBShardGroups - ).dbShardGroups().get(0); - } catch (Exception exception) { - return Commons.handleException(progress, exception, DEFAULT_DB_SHARD_GROUP_ERROR_RULE_SET, requestLogger); } - final String arn = assembleArn(request.getAwsPartition(), request.getRegion(), request.getAwsAccountId(), dbShardGroup.dbShardGroupResourceId()); - - try { - Tagging.addTags(proxyClient, arn, Tagging.translateTagsToSdk(desiredTags)); - } catch (Exception exception) { - return Commons.handleException( - progress, - exception, - DEFAULT_DB_SHARD_GROUP_ERROR_RULE_SET.extendWith(Tagging.getUpdateTagsAccessDeniedRuleSet(desiredTags, Tagging.TagSet.emptySet())), - requestLogger - ); + protected boolean isDBShardGroupStabilizedAfterMutate( + final ResourceModel model, + final ProxyClient proxyClient + ) { + boolean isDBShardGroupStabilized = isDBShardGroupStabilized(model, proxyClient); + boolean isDBClusterStabilized = isDBClusterStabilized(model, proxyClient); + + requestLogger.log(String.format("isDBShardGroupStabilizedAfterMutate: %b", isDBShardGroupStabilized && isDBClusterStabilized), + ImmutableMap.of("isDBShardGroupStabilized", isDBShardGroupStabilized, + "isDBClusterStabilized", isDBClusterStabilized), + ImmutableMap.of("Description", "isDBShardGroupStabilizedAfterMutate method will be repeatedly" + + " called with a backoff mechanism after the modify call until it returns true. This" + + " process will continue until all included flags are true.")); + + return isDBShardGroupStabilized && isDBClusterStabilized; + } + + protected boolean isDBShardGroupStabilized( + final ResourceModel model, + final ProxyClient proxyClient + ) { + final DBShardGroup dbShardGroup = proxyClient.injectCredentialsAndInvokeV2( + Translator.describeDbShardGroupsRequest(model), + proxyClient.client()::describeDBShardGroups + ).dbShardGroups().get(0); + + return isDBShardGroupAvailable(dbShardGroup); } - return progress; - } + protected boolean isDBClusterStabilized( + final ResourceModel model, + final ProxyClient proxyClient + ) { + final DBCluster dbCluster = proxyClient.injectCredentialsAndInvokeV2( + Translator.describeDbClustersRequest(model), + proxyClient.client()::describeDBClusters + ).dbClusters().get(0); - protected ProgressEvent updateTags( - final ProxyClient proxyClient, - final ResourceHandlerRequest request, - final ProgressEvent progress, - final Tagging.TagSet previousTags, - final Tagging.TagSet desiredTags - ) { - final Collection effectivePreviousTags = Tagging.translateTagsToSdk(previousTags); - final Collection effectiveDesiredTags = Tagging.translateTagsToSdk(desiredTags); + return isDBClusterAvailable(dbCluster); + } - final Collection tagsToRemove = Tagging.exclude(effectivePreviousTags, effectiveDesiredTags); - final Collection tagsToAdd = Tagging.exclude(effectiveDesiredTags, effectivePreviousTags); + protected boolean isDBShardGroupAvailable(final DBShardGroup dbShardGroup) { + return ResourceStatus.AVAILABLE.equalsString(dbShardGroup.status()); + } - if (tagsToAdd.isEmpty() && tagsToRemove.isEmpty()) { - return progress; + protected boolean isDBClusterAvailable(final DBCluster dbCluster) { + return ResourceStatus.AVAILABLE.equalsString(dbCluster.status()); } - final Tagging.TagSet rulesetTagsToAdd = Tagging.exclude(desiredTags, previousTags); - final Tagging.TagSet rulesetTagsToRemove = Tagging.exclude(previousTags, desiredTags); + protected ProgressEvent addTags( + final ProxyClient proxyClient, + final ResourceHandlerRequest request, + final ProgressEvent progress, + final Tagging.TagSet desiredTags + ) { + DBShardGroup dbShardGroup; + try { + dbShardGroup = proxyClient.injectCredentialsAndInvokeV2( + Translator.describeDbShardGroupsRequest(progress.getResourceModel()), proxyClient.client()::describeDBShardGroups + ).dbShardGroups().get(0); + } catch (Exception exception) { + return Commons.handleException(progress, exception, DEFAULT_DB_SHARD_GROUP_ERROR_RULE_SET, requestLogger); + } + + final String arn = assembleArn(request.getAwsPartition(), request.getRegion(), request.getAwsAccountId(), dbShardGroup.dbShardGroupResourceId()); + + try { + Tagging.addTags(proxyClient, arn, Tagging.translateTagsToSdk(desiredTags)); + } catch (Exception exception) { + return Commons.handleException( + progress, + exception, + DEFAULT_DB_SHARD_GROUP_ERROR_RULE_SET.extendWith(Tagging.getUpdateTagsAccessDeniedRuleSet(desiredTags, Tagging.TagSet.emptySet())), + requestLogger + ); + } + + return progress; + } - DBShardGroup dbShardGroup; - try { - dbShardGroup = proxyClient.injectCredentialsAndInvokeV2( - Translator.describeDbShardGroupsRequest(progress.getResourceModel()), proxyClient.client()::describeDBShardGroups - ).dbShardGroups().get(0); - } catch (Exception exception) { - return Commons.handleException(progress, exception, DEFAULT_DB_SHARD_GROUP_ERROR_RULE_SET, requestLogger); + protected ProgressEvent updateTags( + final ProxyClient proxyClient, + final ResourceHandlerRequest request, + final ProgressEvent progress, + final Tagging.TagSet previousTags, + final Tagging.TagSet desiredTags + ) { + final Collection effectivePreviousTags = Tagging.translateTagsToSdk(previousTags); + final Collection effectiveDesiredTags = Tagging.translateTagsToSdk(desiredTags); + + final Collection tagsToRemove = Tagging.exclude(effectivePreviousTags, effectiveDesiredTags); + final Collection tagsToAdd = Tagging.exclude(effectiveDesiredTags, effectivePreviousTags); + + if (tagsToAdd.isEmpty() && tagsToRemove.isEmpty()) { + return progress; + } + + final Tagging.TagSet rulesetTagsToAdd = Tagging.exclude(desiredTags, previousTags); + final Tagging.TagSet rulesetTagsToRemove = Tagging.exclude(previousTags, desiredTags); + + DBShardGroup dbShardGroup; + try { + dbShardGroup = proxyClient.injectCredentialsAndInvokeV2( + Translator.describeDbShardGroupsRequest(progress.getResourceModel()), proxyClient.client()::describeDBShardGroups + ).dbShardGroups().get(0); + } catch (Exception exception) { + return Commons.handleException(progress, exception, DEFAULT_DB_SHARD_GROUP_ERROR_RULE_SET, requestLogger); + } + + final String arn = assembleArn(request.getAwsPartition(), request.getRegion(), request.getAwsAccountId(), dbShardGroup.dbShardGroupResourceId()); + + try { + Tagging.removeTags(proxyClient, arn, Tagging.translateTagsToSdk(tagsToRemove)); + Tagging.addTags(proxyClient, arn, Tagging.translateTagsToSdk(tagsToAdd)); + } catch (Exception exception) { + return Commons.handleException( + progress, + exception, + DEFAULT_DB_SHARD_GROUP_ERROR_RULE_SET.extendWith(Tagging.getUpdateTagsAccessDeniedRuleSet(rulesetTagsToAdd, rulesetTagsToRemove)), + requestLogger + ); + } + + return progress; } - final String arn = assembleArn(request.getAwsPartition(), request.getRegion(), request.getAwsAccountId(), dbShardGroup.dbShardGroupResourceId()); - - try { - Tagging.removeTags(proxyClient, arn, Tagging.translateTagsToSdk(tagsToRemove)); - Tagging.addTags(proxyClient, arn, Tagging.translateTagsToSdk(tagsToAdd)); - } catch (Exception exception) { - return Commons.handleException( - progress, - exception, - DEFAULT_DB_SHARD_GROUP_ERROR_RULE_SET.extendWith(Tagging.getUpdateTagsAccessDeniedRuleSet(rulesetTagsToAdd, rulesetTagsToRemove)), - requestLogger - ); + protected Set getTags( + final ProxyClient proxyClient, + final ResourceHandlerRequest request, + final DBShardGroup dbShardGroup + ) { + String arn = assembleArn(request.getAwsPartition(), request.getRegion(), request.getAwsAccountId(), dbShardGroup.dbShardGroupResourceId()); + return Tagging.listTagsForResource(proxyClient, arn); } - return progress; - } - - protected Set getTags( - final ProxyClient proxyClient, - final ResourceHandlerRequest request, - final DBShardGroup dbShardGroup - ){ - String arn = assembleArn(request.getAwsPartition(), request.getRegion(), request.getAwsAccountId(), dbShardGroup.dbShardGroupResourceId()); - return Tagging.listTagsForResource(proxyClient, arn); - } - - private String assembleArn(String partition, String region, String accountId, String dbShardGroupResourceId) { - return Arn.builder() - .withPartition(partition) - .withService("rds") - .withRegion(region) - .withAccountId(accountId) - .withResource(ArnResource.builder() - .withResourceType("shard-group") - .withResource(dbShardGroupResourceId) - .build().toString()) - .build().toString(); - } + private String assembleArn(String partition, String region, String accountId, String dbShardGroupResourceId) { + return Arn.builder() + .withPartition(partition) + .withService("rds") + .withRegion(region) + .withAccountId(accountId) + .withResource(ArnResource.builder() + .withResourceType("shard-group") + .withResource(dbShardGroupResourceId) + .build().toString()) + .build().toString(); + } } diff --git a/aws-rds-dbshardgroup/src/main/java/software/amazon/rds/dbshardgroup/CallbackContext.java b/aws-rds-dbshardgroup/src/main/java/software/amazon/rds/dbshardgroup/CallbackContext.java index 6bbd5971..7bbb267f 100644 --- a/aws-rds-dbshardgroup/src/main/java/software/amazon/rds/dbshardgroup/CallbackContext.java +++ b/aws-rds-dbshardgroup/src/main/java/software/amazon/rds/dbshardgroup/CallbackContext.java @@ -1,15 +1,35 @@ package software.amazon.rds.dbshardgroup; import software.amazon.cloudformation.proxy.StdCallbackContext; +import software.amazon.rds.common.handler.TaggingContext; @lombok.Getter @lombok.Setter @lombok.ToString @lombok.EqualsAndHashCode(callSuper = true) -public class CallbackContext extends StdCallbackContext { +public class CallbackContext extends StdCallbackContext implements TaggingContext.Provider { private boolean described; private boolean updated; - private boolean tagged; + + private TaggingContext taggingContext; + + public CallbackContext() { + super(); + this.taggingContext = new TaggingContext(); + } + + @Override + public TaggingContext getTaggingContext() { + return taggingContext; + } + + public boolean isAddTagsComplete() { + return taggingContext.isAddTagsComplete(); + } + + public void setAddTagsComplete(final boolean addTagsComplete) { + taggingContext.setAddTagsComplete(addTagsComplete); + } private String dbClusterIdentifier; diff --git a/aws-rds-dbshardgroup/src/main/java/software/amazon/rds/dbshardgroup/CreateHandler.java b/aws-rds-dbshardgroup/src/main/java/software/amazon/rds/dbshardgroup/CreateHandler.java index 19510b3e..6405dab5 100644 --- a/aws-rds-dbshardgroup/src/main/java/software/amazon/rds/dbshardgroup/CreateHandler.java +++ b/aws-rds-dbshardgroup/src/main/java/software/amazon/rds/dbshardgroup/CreateHandler.java @@ -9,8 +9,6 @@ import software.amazon.cloudformation.proxy.ProgressEvent; import software.amazon.cloudformation.proxy.ProxyClient; import software.amazon.cloudformation.proxy.ResourceHandlerRequest; -import software.amazon.rds.common.error.ErrorRuleSet; -import software.amazon.rds.common.error.ErrorStatus; import software.amazon.rds.common.handler.Commons; import software.amazon.rds.common.handler.HandlerConfig; import software.amazon.rds.common.handler.Tagging; @@ -50,58 +48,40 @@ protected ProgressEvent handleRequest( .stackTags(Tagging.translateTagsToSdk(request.getDesiredResourceTags())) .resourceTags(new HashSet<>(Translator.translateTagsToSdk(request.getDesiredResourceState().getTags()))) .build(); + ResourceModel model = request.getDesiredResourceState(); + if (StringUtils.isNullOrEmpty(model.getDBShardGroupIdentifier())) { + model.setDBShardGroupIdentifier( + dbShardGroupIdentifierFactory.newIdentifier() + .withStackId(request.getStackId()) + .withResourceId(request.getLogicalResourceIdentifier()) + .withRequestToken(request.getClientRequestToken()) + .toString()); + } return ProgressEvent.progress(request.getDesiredResourceState(), callbackContext) - .then(progress -> { - ResourceModel model = progress.getResourceModel(); - if (StringUtils.isNullOrEmpty(model.getDBShardGroupIdentifier())){ - model.setDBShardGroupIdentifier( - dbShardGroupIdentifierFactory.newIdentifier() - .withStackId(request.getStackId()) - .withResourceId(request.getLogicalResourceIdentifier()) - .withRequestToken(request.getClientRequestToken()) - .toString()); - } - return createDbShardGroup(proxy, proxyClient, progress); - }) - .then(progress -> Commons.execOnce(progress, () -> addTags(proxyClient, request, progress, allTags), CallbackContext::isTagged, CallbackContext::setTagged)) - .then(progress -> proxy.initiate("rds::create-db-shard-group-stabilize", proxyClient, request.getDesiredResourceState(), callbackContext) - .translateToServiceRequest(Function.identity()) - .backoffDelay(config.getBackoff()) - .makeServiceCall(NOOP_CALL) - .stabilize((noopRequest, noopResponse, proxyInvocation, model, context) -> isDBShardGroupStabilized(model, proxyInvocation)) - .handleError((deleteRequest, exception, client, resourceModel, ctx) -> Commons.handleException( - ProgressEvent.progress(resourceModel, ctx), - exception, - DEFAULT_DB_SHARD_GROUP_ERROR_RULE_SET, - requestLogger - )) - .progress()) - // Stabilize cluster state to ensure shard group operations are fully available - .then(progress -> proxy.initiate("rds::create-db-shard-group-stabilize-cluster", proxyClient, request.getDesiredResourceState(), callbackContext) - .translateToServiceRequest(Function.identity()) - .backoffDelay(config.getBackoff()) - .makeServiceCall(NOOP_CALL) - .stabilize((noopRequest, noopResponse, proxyInvocation, model, context) -> isDBClusterStabilized(model, proxyInvocation)) - .handleError((noopRequest, exception, client, model, context) -> Commons.handleException( - ProgressEvent.progress(model, context), - exception, - DEFAULT_DB_SHARD_GROUP_ERROR_RULE_SET, - requestLogger - )).progress()) + .then(progress -> Tagging.createWithTaggingFallback(proxy, proxyClient, this::createDbShardGroup, progress, allTags) + .then(p -> Commons.execOnce(p, () -> { + final Tagging.TagSet extraTags = Tagging.TagSet.builder() + .stackTags(allTags.getStackTags()) + .resourceTags(allTags.getResourceTags()) + .build(); + return addTags(proxyClient, request, p, extraTags); + }, CallbackContext::isAddTagsComplete, CallbackContext::setAddTagsComplete)) + ) .then(progress -> new ReadHandler().handleRequest(proxy, proxyClient, request, callbackContext)); } private ProgressEvent createDbShardGroup( final AmazonWebServicesClientProxy proxy, final ProxyClient proxyClient, - final ProgressEvent progress + final ProgressEvent progress, + final Tagging.TagSet tags ) { return proxy.initiate("rds::create-db-shard-group", proxyClient, progress.getResourceModel(), progress.getCallbackContext()) - .translateToServiceRequest(Translator::createDbShardGroupRequest) + .translateToServiceRequest((resourceModel) -> Translator.createDbShardGroupRequest(resourceModel, tags)) .backoffDelay(config.getBackoff()) .makeServiceCall((createDbShardGroupRequest, proxyInvocation) -> proxyInvocation.injectCredentialsAndInvokeV2(createDbShardGroupRequest, proxyInvocation.client()::createDBShardGroup)) - .stabilize((createRequest, createResponse, proxyInvocation, model, context) -> isDBShardGroupStabilized(model, proxyInvocation)) + .stabilize((createRequest, createResponse, proxyInvocation, model, context) -> isDBShardGroupStabilizedAfterMutate(model, proxyInvocation)) .handleError((deleteRequest, exception, client, resourceModel, ctx) -> Commons.handleException( ProgressEvent.progress(resourceModel, ctx), exception, diff --git a/aws-rds-dbshardgroup/src/main/java/software/amazon/rds/dbshardgroup/DeleteHandler.java b/aws-rds-dbshardgroup/src/main/java/software/amazon/rds/dbshardgroup/DeleteHandler.java index 0ba1f9e7..c10cc322 100644 --- a/aws-rds-dbshardgroup/src/main/java/software/amazon/rds/dbshardgroup/DeleteHandler.java +++ b/aws-rds-dbshardgroup/src/main/java/software/amazon/rds/dbshardgroup/DeleteHandler.java @@ -1,6 +1,8 @@ package software.amazon.rds.dbshardgroup; +import java.util.List; import java.util.function.Function; +import org.apache.commons.collections.CollectionUtils; import software.amazon.awssdk.services.rds.RdsClient; import software.amazon.awssdk.services.rds.model.DBCluster; import software.amazon.awssdk.services.rds.model.DBShardGroup; @@ -51,19 +53,7 @@ protected ProgressEvent handleRequest( .translateToServiceRequest(Function.identity()) .backoffDelay(config.getBackoff()) .makeServiceCall(NOOP_CALL) - .stabilize((noopRequest, noopResponse, proxyInvocation, model, context) -> isDbShardGroupDeleted(model, proxyInvocation)) - .handleError((noopRequest, exception, client, model, context) -> Commons.handleException( - ProgressEvent.progress(model, context), - exception, - DELETE_DB_SHARD_GROUP_ERROR_RULE_SET, - requestLogger - )).progress()) - // Stabilize cluster state to ensure cluster is available for new db shard group creation - .then(progress -> proxy.initiate("rds::delete-db-shard-group-stabilize-cluster", proxyClient, request.getDesiredResourceState(), callbackContext) - .translateToServiceRequest(Function.identity()) - .backoffDelay(config.getBackoff()) - .makeServiceCall(NOOP_CALL) - .stabilize((noopRequest, noopResponse, proxyInvocation, model, context) -> isDbClusterStabilizedOrDeleted(proxyInvocation, context)) + .stabilize((noopRequest, noopResponse, proxyInvocation, model, context) -> isDbShardGroupDeleted(model, proxyInvocation) && isDbClusterStabilizedOrDeleted(proxyInvocation, context)) .handleError((noopRequest, exception, client, model, context) -> Commons.handleException( ProgressEvent.progress(model, context), exception, @@ -73,22 +63,6 @@ protected ProgressEvent handleRequest( .then(progress -> ProgressEvent.defaultSuccessHandler(null)); } - private ProgressEvent checkIfDbShardGroupExists(final AmazonWebServicesClientProxy proxy, - final ResourceHandlerRequest request, - final CallbackContext callbackContext, - final ProxyClient proxyClient) { - return proxy.initiate("rds::delete-db-shard-group-check-exists", proxyClient, request.getDesiredResourceState(), callbackContext) - .translateToServiceRequest(Translator::describeDbShardGroupsRequest) - .makeServiceCall(((describeRequest, proxyInvocation) -> proxyInvocation.injectCredentialsAndInvokeV2(describeRequest, proxyInvocation.client()::describeDBShardGroups))) - .handleError((deleteRequest, exception, client, resourceModel, ctx) -> Commons.handleException( - ProgressEvent.progress(resourceModel, ctx), - exception, - DEFAULT_DB_SHARD_GROUP_ERROR_RULE_SET, - requestLogger - )) - .progress(); - } - private ProgressEvent deleteDbShardGroupIfNotInProgress(final ProgressEvent progress, final AmazonWebServicesClientProxy proxy, final ResourceHandlerRequest request, @@ -140,14 +114,21 @@ private boolean isDbShardGroupDeleted(final ResourceModel model, private boolean isDbClusterStabilizedOrDeleted(final ProxyClient proxyClient, final CallbackContext callbackContext) { try { - final DBCluster dbCluster = proxyClient.injectCredentialsAndInvokeV2( + final List dbClusters = proxyClient.injectCredentialsAndInvokeV2( DescribeDbClustersRequest.builder() .dbClusterIdentifier(callbackContext.getDbClusterIdentifier()) .build(), proxyClient.client()::describeDBClusters - ).dbClusters().get(0); - return isDBClusterAvailable(dbCluster); - } catch (DbClusterNotFoundException|IndexOutOfBoundsException e) { + ).dbClusters(); + + if (CollectionUtils.isEmpty(dbClusters)) { + // For an empty response, we assume the same behavior for a DbClusterNotFoundException + // https://jira.rds.a2z.com/browse/WS-6673 + return true; + } else { + return isDBClusterAvailable(dbClusters.get(0)); + } + } catch (DbClusterNotFoundException e) { return true; } } diff --git a/aws-rds-dbshardgroup/src/main/java/software/amazon/rds/dbshardgroup/ResourceStatus.java b/aws-rds-dbshardgroup/src/main/java/software/amazon/rds/dbshardgroup/ResourceStatus.java index 6122cc6a..2cc05cc3 100644 --- a/aws-rds-dbshardgroup/src/main/java/software/amazon/rds/dbshardgroup/ResourceStatus.java +++ b/aws-rds-dbshardgroup/src/main/java/software/amazon/rds/dbshardgroup/ResourceStatus.java @@ -14,15 +14,6 @@ public enum ResourceStatus { this.value = value; } - public static ResourceStatus fromString(final String status) { - for (final ResourceStatus ResourceStatus : ResourceStatus.values()) { - if (ResourceStatus.equalsString(status)) { - return ResourceStatus; - } - } - return null; - } - @Override public String toString() { return value; diff --git a/aws-rds-dbshardgroup/src/main/java/software/amazon/rds/dbshardgroup/Translator.java b/aws-rds-dbshardgroup/src/main/java/software/amazon/rds/dbshardgroup/Translator.java index d92d495b..924bff10 100644 --- a/aws-rds-dbshardgroup/src/main/java/software/amazon/rds/dbshardgroup/Translator.java +++ b/aws-rds-dbshardgroup/src/main/java/software/amazon/rds/dbshardgroup/Translator.java @@ -13,10 +13,11 @@ import software.amazon.awssdk.services.rds.model.DescribeDbClustersRequest; import software.amazon.awssdk.services.rds.model.DescribeDbShardGroupsRequest; import software.amazon.awssdk.services.rds.model.ModifyDbShardGroupRequest; +import software.amazon.rds.common.handler.Tagging; public class Translator { - static CreateDbShardGroupRequest createDbShardGroupRequest(final ResourceModel model) { + static CreateDbShardGroupRequest createDbShardGroupRequest(final ResourceModel model, final Tagging.TagSet tags) { return CreateDbShardGroupRequest.builder() .computeRedundancy(model.getComputeRedundancy()) .dbClusterIdentifier(model.getDBClusterIdentifier()) @@ -24,11 +25,13 @@ static CreateDbShardGroupRequest createDbShardGroupRequest(final ResourceModel m .maxACU(model.getMaxACU()) .minACU(model.getMinACU()) .publiclyAccessible(model.getPubliclyAccessible()) + .tags(Tagging.translateTagsToSdk(tags)) .build(); } static ModifyDbShardGroupRequest modifyDbShardGroupRequest(final ResourceModel desiredModel) { return ModifyDbShardGroupRequest.builder() + .computeRedundancy(desiredModel.getComputeRedundancy()) .dbShardGroupIdentifier(desiredModel.getDBShardGroupIdentifier()) .maxACU(desiredModel.getMaxACU()) .minACU(desiredModel.getMinACU()) diff --git a/aws-rds-dbshardgroup/src/main/java/software/amazon/rds/dbshardgroup/UpdateHandler.java b/aws-rds-dbshardgroup/src/main/java/software/amazon/rds/dbshardgroup/UpdateHandler.java index 31e12d18..798e96d3 100644 --- a/aws-rds-dbshardgroup/src/main/java/software/amazon/rds/dbshardgroup/UpdateHandler.java +++ b/aws-rds-dbshardgroup/src/main/java/software/amazon/rds/dbshardgroup/UpdateHandler.java @@ -47,7 +47,7 @@ protected ProgressEvent handleRequest(final Amaz .then(progress -> Commons.execOnce( progress, () -> proxy.initiate("rds::modify-db-shard-group", proxyClient, request.getDesiredResourceState(), callbackContext) - .translateToServiceRequest(model -> Translator.modifyDbShardGroupRequest(desiredModel)) + .translateToServiceRequest(Translator::modifyDbShardGroupRequest) .backoffDelay(config.getBackoff()) .makeServiceCall((modifyDbShardGroupRequest, proxyInvocation) -> proxyInvocation.injectCredentialsAndInvokeV2(modifyDbShardGroupRequest, proxyClient.client()::modifyDBShardGroup)) .handleError((describeRequest, exception, client, resourceModel, ctx) -> Commons.handleException( @@ -56,14 +56,14 @@ protected ProgressEvent handleRequest(final Amaz DEFAULT_DB_SHARD_GROUP_ERROR_RULE_SET, requestLogger)) .progress(), CallbackContext::isUpdated, CallbackContext::setUpdated)) - .then(progress -> Commons.execOnce(progress, () -> updateTags(proxyClient, request, progress, previousTags, desiredTags), CallbackContext::isTagged, CallbackContext::setTagged)) + .then(progress -> Commons.execOnce(progress, () -> updateTags(proxyClient, request, progress, previousTags, desiredTags), CallbackContext::isAddTagsComplete, CallbackContext::setAddTagsComplete)) // There is a lag between the modifyDbShardGroup request call and the shard group state moving to "modifying", so we introduce a fixed delay prior to stabilization .then((progress) -> delay(progress, POST_MODIFY_DELAY_SEC)) .then(progress -> proxy.initiate("rds::update-db-shard-group-stabilize", proxyClient, request.getDesiredResourceState(), callbackContext) .translateToServiceRequest(Function.identity()) .backoffDelay(config.getBackoff()) .makeServiceCall(NOOP_CALL) - .stabilize((noopRequest, noopResponse, proxyInvocation, model, context) -> isDBShardGroupStabilized(model, proxyInvocation)) + .stabilize((noopRequest, noopResponse, proxyInvocation, model, context) -> isDBShardGroupStabilizedAfterMutate(model, proxyInvocation)) .handleError((deleteRequest, exception, client, resourceModel, ctx) -> Commons.handleException( ProgressEvent.progress(resourceModel, ctx), exception, @@ -71,18 +71,6 @@ protected ProgressEvent handleRequest(final Amaz requestLogger )) .progress()) - // Stabilize cluster state to ensure shard group operations are fully available - .then(progress -> proxy.initiate("rds::update-db-shard-group-stabilize-cluster", proxyClient, request.getDesiredResourceState(), callbackContext) - .translateToServiceRequest(Function.identity()) - .backoffDelay(config.getBackoff()) - .makeServiceCall(NOOP_CALL) - .stabilize((noopRequest, noopResponse, proxyInvocation, model, context) -> isDBClusterStabilized(model, proxyInvocation)) - .handleError((noopRequest, exception, client, model, context) -> Commons.handleException( - ProgressEvent.progress(model, context), - exception, - DEFAULT_DB_SHARD_GROUP_ERROR_RULE_SET, - requestLogger - )).progress()) .then(progress -> new ReadHandler().handleRequest(proxy, proxyClient, request, callbackContext)); } diff --git a/aws-rds-dbshardgroup/src/test/java/software/amazon/rds/dbshardgroup/AbstractHandlerTest.java b/aws-rds-dbshardgroup/src/test/java/software/amazon/rds/dbshardgroup/AbstractHandlerTest.java index b01e04e9..c7fcd0dc 100644 --- a/aws-rds-dbshardgroup/src/test/java/software/amazon/rds/dbshardgroup/AbstractHandlerTest.java +++ b/aws-rds-dbshardgroup/src/test/java/software/amazon/rds/dbshardgroup/AbstractHandlerTest.java @@ -17,149 +17,151 @@ import software.amazon.cloudformation.proxy.delay.Constant; import software.amazon.rds.common.handler.HandlerConfig; import software.amazon.rds.common.handler.Tagging; +import software.amazon.rds.common.logging.RequestLogger; +import software.amazon.rds.common.printer.FilteredJsonPrinter; import software.amazon.rds.test.common.core.AbstractTestBase; public abstract class AbstractHandlerTest extends AbstractTestBase { - protected static final String LOGICAL_RESOURCE_IDENTIFIER = "dbshardgroup"; - protected static final String CLIENT_REQUEST_TOKEN = UUID.randomUUID().toString(); - protected static final String STACK_ID = UUID.randomUUID().toString(); - - protected static final Credentials MOCK_CREDENTIALS; - protected static final LoggerProxy logger; - static final Set TAG_LIST; - static final Set TAG_LIST_EMPTY; - static final Set TAG_LIST_ALTER; - static final Tagging.TagSet TAG_SET; - - static { - MOCK_CREDENTIALS = new Credentials("accessKey", "secretKey", "token"); - logger = new LoggerProxy(); - - TAG_LIST_EMPTY = ImmutableSet.of(); - - TAG_LIST = ImmutableSet.of( - Tag.builder().key("foo").value("bar").build() - ); - - TAG_LIST_ALTER = ImmutableSet.of( - Tag.builder().key("bar").value("baz").build(), - Tag.builder().key("fizz").value("buzz").build() - ); - - TAG_SET = Tagging.TagSet.builder() - .systemTags(ImmutableSet.of( - software.amazon.awssdk.services.rds.model.Tag.builder().key("system-tag-1").value("system-tag-value1").build(), - software.amazon.awssdk.services.rds.model.Tag.builder().key("system-tag-2").value("system-tag-value2").build(), - software.amazon.awssdk.services.rds.model.Tag.builder().key("system-tag-3").value("system-tag-value3").build() - )).stackTags(ImmutableSet.of( - software.amazon.awssdk.services.rds.model.Tag.builder().key("stack-tag-1").value("stack-tag-value1").build(), - software.amazon.awssdk.services.rds.model.Tag.builder().key("stack-tag-2").value("stack-tag-value2").build(), - software.amazon.awssdk.services.rds.model.Tag.builder().key("stack-tag-3").value("stack-tag-value3").build() - )).resourceTags(ImmutableSet.of( - software.amazon.awssdk.services.rds.model.Tag.builder().key("resource-tag-1").value("resource-tag-value1").build(), - software.amazon.awssdk.services.rds.model.Tag.builder().key("resource-tag-2").value("resource-tag-value2").build(), - software.amazon.awssdk.services.rds.model.Tag.builder().key("resource-tag-3").value("resource-tag-value3").build() - )).build(); - } - - static final String DB_SHARD_GROUP_IDENTIFIER = "testDbShardGroup"; - static final String DB_SHARD_GROUP_RESOURCE_ID = "testDbShardGroupId"; - static final String DB_CLUSTER_IDENTIFIER = "testDbCluster"; - - static final String ACCOUNT_ID = "123456789012"; - static final String REGION = "us-east-1"; - static final String PARTITION = "aws"; - static final String DB_SHARD_GROUP_ARN = "arn:aws:rds:us-east-1:123456789012:shard-group:testDbShardGroupId"; - static final int COMPUTE_REDUNDANCY = 1; - static final Double MAX_ACU = 3.5; - static final Double MAX_ACU_ALTER = 5d; - - static final ResourceModel RESOURCE_MODEL = ResourceModel.builder() - .dBShardGroupIdentifier(DB_SHARD_GROUP_IDENTIFIER) - .dBClusterIdentifier(DB_CLUSTER_IDENTIFIER) - .dBShardGroupResourceId(DB_SHARD_GROUP_RESOURCE_ID) - .computeRedundancy(COMPUTE_REDUNDANCY) - .maxACU(MAX_ACU) - .publiclyAccessible(false) - .build(); - - static final ResourceModel RESOURCE_MODEL_NO_IDENT = ResourceModel.builder() - .dBClusterIdentifier(DB_CLUSTER_IDENTIFIER) - .dBShardGroupResourceId(DB_SHARD_GROUP_RESOURCE_ID) - .computeRedundancy(COMPUTE_REDUNDANCY) - .maxACU(MAX_ACU) - .publiclyAccessible(false) - .build(); - - static final DBShardGroup DB_SHARD_GROUP = DBShardGroup.builder() - .dbShardGroupIdentifier(DB_SHARD_GROUP_IDENTIFIER) - .dbClusterIdentifier(DB_CLUSTER_IDENTIFIER) - .dbShardGroupResourceId(DB_SHARD_GROUP_RESOURCE_ID) - .computeRedundancy(COMPUTE_REDUNDANCY) - .maxACU(MAX_ACU) - .publiclyAccessible(false) - .build(); - - protected static final DBShardGroup DB_SHARD_GROUP_AVAILABLE = DB_SHARD_GROUP.toBuilder() - .status(ResourceStatus.AVAILABLE.toString()) - .build(); - - protected static final DBShardGroup DB_SHARD_GROUP_CREATING = DB_SHARD_GROUP.toBuilder() - .status(ResourceStatus.CREATING.toString()) - .build(); - - protected static final DBShardGroup DB_SHARD_GROUP_MODIFYING = DB_SHARD_GROUP.toBuilder() - .status(ResourceStatus.MODIFYING.toString()) - .build(); - - protected static final DBShardGroup DB_SHARD_GROUP_DELETING = DB_SHARD_GROUP.toBuilder() - .status(ResourceStatus.DELETING.toString()) - .build(); - - // use an accelerated backoff for faster unit testing - protected final HandlerConfig TEST_HANDLER_CONFIG = HandlerConfig.builder() - .probingEnabled(false) - .backoff(Constant.of().delay(Duration.ofMillis(1)) - .timeout(Duration.ofSeconds(120)) - .build()) - .build(); - - static ProxyClient MOCK_PROXY(final AmazonWebServicesClientProxy proxy, final ClientT client) { - return new BaseProxyClient<>(proxy, client); - } - - protected abstract BaseHandlerStd getHandler(); - - protected abstract AmazonWebServicesClientProxy getProxy(); - - protected abstract ProxyClient getProxyClient(); - - @Override - protected String getLogicalResourceIdentifier() { - return LOGICAL_RESOURCE_IDENTIFIER; - } - - @Override - protected String newClientRequestToken() { - return CLIENT_REQUEST_TOKEN; - } - - protected String newStackId() { - return STACK_ID; - } - - @Override - protected void expectResourceSupply(Supplier supplier) { - when(getProxyClient().client().describeDBShardGroups(any(DescribeDbShardGroupsRequest.class))) - .then((req) -> - DescribeDbShardGroupsResponse.builder() - .dbShardGroups(supplier.get()) - .build()); - } - - @Override - protected ProgressEvent invokeHandleRequest(ResourceHandlerRequest request, CallbackContext context) { - return getHandler().handleRequest(getProxy(), getProxyClient(), request, context); - } + protected static final String LOGICAL_RESOURCE_IDENTIFIER = "dbshardgroup"; + protected static final String CLIENT_REQUEST_TOKEN = UUID.randomUUID().toString(); + protected static final String STACK_ID = UUID.randomUUID().toString(); + + protected static final Credentials MOCK_CREDENTIALS; + protected static final LoggerProxy logger; + static final Set TAG_LIST; + static final Set TAG_LIST_EMPTY; + static final Set TAG_LIST_ALTER; + static final Tagging.TagSet TAG_SET; + + static { + MOCK_CREDENTIALS = new Credentials("accessKey", "secretKey", "token"); + logger = new LoggerProxy(); + + TAG_LIST_EMPTY = ImmutableSet.of(); + + TAG_LIST = ImmutableSet.of( + Tag.builder().key("foo").value("bar").build() + ); + + TAG_LIST_ALTER = ImmutableSet.of( + Tag.builder().key("bar").value("baz").build(), + Tag.builder().key("fizz").value("buzz").build() + ); + + TAG_SET = Tagging.TagSet.builder() + .systemTags(ImmutableSet.of( + software.amazon.awssdk.services.rds.model.Tag.builder().key("system-tag-1").value("system-tag-value1").build(), + software.amazon.awssdk.services.rds.model.Tag.builder().key("system-tag-2").value("system-tag-value2").build(), + software.amazon.awssdk.services.rds.model.Tag.builder().key("system-tag-3").value("system-tag-value3").build() + )).stackTags(ImmutableSet.of( + software.amazon.awssdk.services.rds.model.Tag.builder().key("stack-tag-1").value("stack-tag-value1").build(), + software.amazon.awssdk.services.rds.model.Tag.builder().key("stack-tag-2").value("stack-tag-value2").build(), + software.amazon.awssdk.services.rds.model.Tag.builder().key("stack-tag-3").value("stack-tag-value3").build() + )).resourceTags(ImmutableSet.of( + software.amazon.awssdk.services.rds.model.Tag.builder().key("resource-tag-1").value("resource-tag-value1").build(), + software.amazon.awssdk.services.rds.model.Tag.builder().key("resource-tag-2").value("resource-tag-value2").build(), + software.amazon.awssdk.services.rds.model.Tag.builder().key("resource-tag-3").value("resource-tag-value3").build() + )).build(); + } + + static final String DB_SHARD_GROUP_IDENTIFIER = "testDbShardGroup"; + static final String DB_SHARD_GROUP_RESOURCE_ID = "testDbShardGroupId"; + static final String DB_CLUSTER_IDENTIFIER = "testDbCluster"; + + static final String ACCOUNT_ID = "123456789012"; + static final String REGION = "us-east-1"; + static final String PARTITION = "aws"; + static final String DB_SHARD_GROUP_ARN = "arn:aws:rds:us-east-1:123456789012:shard-group:testDbShardGroupId"; + static final int COMPUTE_REDUNDANCY = 1; + static final Double MAX_ACU = 3.5; + static final Double MAX_ACU_ALTER = 5d; + + static final ResourceModel RESOURCE_MODEL = ResourceModel.builder() + .dBShardGroupIdentifier(DB_SHARD_GROUP_IDENTIFIER) + .dBClusterIdentifier(DB_CLUSTER_IDENTIFIER) + .dBShardGroupResourceId(DB_SHARD_GROUP_RESOURCE_ID) + .computeRedundancy(COMPUTE_REDUNDANCY) + .maxACU(MAX_ACU) + .publiclyAccessible(false) + .build(); + + static final ResourceModel RESOURCE_MODEL_NO_IDENT = ResourceModel.builder() + .dBClusterIdentifier(DB_CLUSTER_IDENTIFIER) + .dBShardGroupResourceId(DB_SHARD_GROUP_RESOURCE_ID) + .computeRedundancy(COMPUTE_REDUNDANCY) + .maxACU(MAX_ACU) + .publiclyAccessible(false) + .build(); + + static final DBShardGroup DB_SHARD_GROUP = DBShardGroup.builder() + .dbShardGroupIdentifier(DB_SHARD_GROUP_IDENTIFIER) + .dbClusterIdentifier(DB_CLUSTER_IDENTIFIER) + .dbShardGroupResourceId(DB_SHARD_GROUP_RESOURCE_ID) + .computeRedundancy(COMPUTE_REDUNDANCY) + .maxACU(MAX_ACU) + .publiclyAccessible(false) + .build(); + + protected static final DBShardGroup DB_SHARD_GROUP_AVAILABLE = DB_SHARD_GROUP.toBuilder() + .status(ResourceStatus.AVAILABLE.toString()) + .build(); + + protected static final DBShardGroup DB_SHARD_GROUP_CREATING = DB_SHARD_GROUP.toBuilder() + .status(ResourceStatus.CREATING.toString()) + .build(); + + protected static final DBShardGroup DB_SHARD_GROUP_MODIFYING = DB_SHARD_GROUP.toBuilder() + .status(ResourceStatus.MODIFYING.toString()) + .build(); + + protected static final DBShardGroup DB_SHARD_GROUP_DELETING = DB_SHARD_GROUP.toBuilder() + .status(ResourceStatus.DELETING.toString()) + .build(); + + // use an accelerated backoff for faster unit testing + protected final HandlerConfig TEST_HANDLER_CONFIG = HandlerConfig.builder() + .probingEnabled(false) + .backoff(Constant.of().delay(Duration.ofMillis(1)) + .timeout(Duration.ofSeconds(120)) + .build()) + .build(); + + static ProxyClient MOCK_PROXY(final AmazonWebServicesClientProxy proxy, final ClientT client) { + return new BaseProxyClient<>(proxy, client); + } + + protected abstract BaseHandlerStd getHandler(); + + protected abstract AmazonWebServicesClientProxy getProxy(); + + protected abstract ProxyClient getProxyClient(); + + @Override + protected String getLogicalResourceIdentifier() { + return LOGICAL_RESOURCE_IDENTIFIER; + } + + @Override + protected String newClientRequestToken() { + return CLIENT_REQUEST_TOKEN; + } + + protected String newStackId() { + return STACK_ID; + } + + @Override + protected void expectResourceSupply(Supplier supplier) { + when(getProxyClient().client().describeDBShardGroups(any(DescribeDbShardGroupsRequest.class))) + .then((req) -> + DescribeDbShardGroupsResponse.builder() + .dbShardGroups(supplier.get()) + .build()); + } + + @Override + protected ProgressEvent invokeHandleRequest(ResourceHandlerRequest request, CallbackContext context) { + return getHandler().handleRequest(getProxy(), getProxyClient(), request, context, new RequestLogger(logger, request, new FilteredJsonPrinter())); + } } diff --git a/aws-rds-dbshardgroup/src/test/java/software/amazon/rds/dbshardgroup/CreateHandlerTest.java b/aws-rds-dbshardgroup/src/test/java/software/amazon/rds/dbshardgroup/CreateHandlerTest.java index b794e860..6fc3cd19 100644 --- a/aws-rds-dbshardgroup/src/test/java/software/amazon/rds/dbshardgroup/CreateHandlerTest.java +++ b/aws-rds-dbshardgroup/src/test/java/software/amazon/rds/dbshardgroup/CreateHandlerTest.java @@ -1,6 +1,7 @@ package software.amazon.rds.dbshardgroup; import java.time.Duration; +import java.util.Collections; import java.util.Objects; import java.util.Queue; @@ -114,7 +115,7 @@ public void handleRequest_SimpleSuccess() { Objects.equals(DB_SHARD_GROUP_CREATING.dbShardGroupIdentifier(), req.dbShardGroupIdentifier()) ) ); - verify(proxyClient.client(), times(4)).describeDBShardGroups( + verify(proxyClient.client(), times(2)).describeDBShardGroups( ArgumentMatchers.argThat(req -> Objects.equals(DB_SHARD_GROUP_CREATING.dbShardGroupIdentifier(), req.dbShardGroupIdentifier()) ) @@ -179,7 +180,7 @@ public void handleRequest_NoDbShardGroupIdentifier() { Objects.equals(dbShardGroupIdentifier, req.dbShardGroupIdentifier()) ) ); - verify(proxyClient.client(), times(4)).describeDBShardGroups( + verify(proxyClient.client(), times(2)).describeDBShardGroups( ArgumentMatchers.argThat(req -> Objects.equals(dbShardGroupIdentifier, req.dbShardGroupIdentifier()) ) @@ -254,7 +255,7 @@ public void handleRequest_Stabilize() { Objects.equals(DB_SHARD_GROUP_CREATING.dbShardGroupIdentifier(), req.dbShardGroupIdentifier()) ) ); - verify(proxyClient.client(), times(6)).describeDBShardGroups( + verify(proxyClient.client(), times(3)).describeDBShardGroups( ArgumentMatchers.argThat(req -> Objects.equals(DB_SHARD_GROUP_CREATING.dbShardGroupIdentifier(), req.dbShardGroupIdentifier()) ) diff --git a/aws-rds-dbshardgroup/src/test/java/software/amazon/rds/dbshardgroup/UpdateHandlerTest.java b/aws-rds-dbshardgroup/src/test/java/software/amazon/rds/dbshardgroup/UpdateHandlerTest.java index face11ea..801979af 100644 --- a/aws-rds-dbshardgroup/src/test/java/software/amazon/rds/dbshardgroup/UpdateHandlerTest.java +++ b/aws-rds-dbshardgroup/src/test/java/software/amazon/rds/dbshardgroup/UpdateHandlerTest.java @@ -191,7 +191,7 @@ public void handleRequest_Stabilize() { verify(proxyClient.client(), times(5)).describeDBShardGroups(any(DescribeDbShardGroupsRequest.class)); verify(proxyClient.client(), times(1)).removeTagsFromResource(any(RemoveTagsFromResourceRequest.class)); verify(proxyClient.client(), times(1)).addTagsToResource(any(AddTagsToResourceRequest.class)); - verify(proxyClient.client(), times(2)).describeDBClusters( + verify(proxyClient.client(), times(3)).describeDBClusters( ArgumentMatchers.argThat(req -> Objects.equals(DB_SHARD_GROUP_CREATING.dbClusterIdentifier(), req.dbClusterIdentifier()) )