From 88c57ea2386912378901b5f49ffebfee50cbd6d3 Mon Sep 17 00:00:00 2001 From: Vikas Ramaneti Date: Mon, 21 Oct 2024 07:58:24 +0000 Subject: [PATCH 1/3] Update Tags Support for Namespace --- .../aws-redshiftserverless-namespace.json | 26 ++++- .../resource-role.yaml | 4 + .../namespace/BaseHandlerStd.java | 22 +++- .../namespace/CreateHandler.java | 2 +- .../namespace/ReadHandler.java | 10 ++ .../namespace/Translator.java | 102 ++++++++++++++++-- .../namespace/UpdateHandler.java | 82 +++++++++++--- .../namespace/UpdateTagsRequest.java | 17 +++ .../namespace/AbstractTestBase.java | 2 + .../namespace/CreateHandlerTest.java | 10 ++ .../namespace/ReadHandlerTest.java | 7 ++ .../namespace/UpdateHandlerTest.java | 10 ++ 12 files changed, 261 insertions(+), 33 deletions(-) create mode 100644 aws-redshiftserverless-namespace/src/main/java/software/amazon/redshiftserverless/namespace/UpdateTagsRequest.java diff --git a/aws-redshiftserverless-namespace/aws-redshiftserverless-namespace.json b/aws-redshiftserverless-namespace/aws-redshiftserverless-namespace.json index c7d11ad..29fb06b 100644 --- a/aws-redshiftserverless-namespace/aws-redshiftserverless-namespace.json +++ b/aws-redshiftserverless-namespace/aws-redshiftserverless-namespace.json @@ -225,7 +225,16 @@ } }, "tagging": { - "taggable": false + "taggable": true, + "tagOnCreate": true, + "tagUpdatable": true, + "cloudFormationSystemTags": false, + "tagProperty": "/properties/Tags", + "permissions": [ + "redshift-serverless:ListTagsForResource", + "redshift-serverless:TagResource", + "redshift-serverless:UntagResource" + ] }, "required": [ "NamespaceName" @@ -248,9 +257,6 @@ "/properties/AdminUserPassword", "/properties/FinalSnapshotName", "/properties/FinalSnapshotRetentionPeriod", - "/properties/Tags", - "/properties/Tags/*/Key", - "/properties/Tags/*/Value", "/properties/ManageAdminPassword", "/properties/RedshiftIdcApplicationArn" ], @@ -264,6 +270,7 @@ "handlers": { "create": { "permissions": [ + "iam:CreateServiceLinkedRole", "iam:PassRole", "kms:TagResource", "kms:UntagResource", @@ -282,6 +289,8 @@ "redshift-serverless:GetNamespace", "redshift-serverless:ListSnapshotCopyConfigurations", "redshift-serverless:CreateSnapshotCopyConfiguration", + "redshift-serverless:ListTagsForResource", + "redshift-serverless:TagResource", "redshift:GetResourcePolicy", "redshift:PutResourcePolicy", "secretsmanager:CreateSecret", @@ -294,6 +303,7 @@ "permissions": [ "iam:PassRole", "redshift-serverless:GetNamespace", + "redshift-serverless:ListTagsForResource", "redshift:GetResourcePolicy", "redshift-serverless:ListSnapshotCopyConfigurations" ] @@ -320,6 +330,9 @@ "redshift-serverless:CreateSnapshotCopyConfiguration", "redshift-serverless:UpdateSnapshotCopyConfiguration", "redshift-serverless:DeleteSnapshotCopyConfiguration", + "redshift-serverless:ListTagsForResource", + "redshift-serverless:TagResource", + "redshift-serverless:UntagResource", "redshift:GetResourcePolicy", "redshift:PutResourcePolicy", "redshift:DeleteResourcePolicy", @@ -336,6 +349,8 @@ "iam:PassRole", "redshift-serverless:DeleteNamespace", "redshift-serverless:GetNamespace", + "redshift-serverless:ListTagsForResource", + "redshift-serverless:UntagResource", "kms:RetireGrant", "secretsmanager:DescribeSecret", "secretsmanager:DeleteSecret", @@ -345,7 +360,8 @@ "list": { "permissions": [ "iam:PassRole", - "redshift-serverless:ListNamespaces" + "redshift-serverless:ListNamespaces", + "redshift-serverless:ListTagsForResource" ] } }, diff --git a/aws-redshiftserverless-namespace/resource-role.yaml b/aws-redshiftserverless-namespace/resource-role.yaml index 8f233a6..3d4b9d0 100644 --- a/aws-redshiftserverless-namespace/resource-role.yaml +++ b/aws-redshiftserverless-namespace/resource-role.yaml @@ -30,6 +30,7 @@ Resources: Statement: - Effect: Allow Action: + - "iam:CreateServiceLinkedRole" - "iam:PassRole" - "kms:CancelKeyDeletion" - "kms:CreateGrant" @@ -51,6 +52,9 @@ Resources: - "redshift-serverless:GetNamespace" - "redshift-serverless:ListNamespaces" - "redshift-serverless:ListSnapshotCopyConfigurations" + - "redshift-serverless:ListTagsForResource" + - "redshift-serverless:TagResource" + - "redshift-serverless:UntagResource" - "redshift-serverless:UpdateNamespace" - "redshift-serverless:UpdateSnapshotCopyConfiguration" - "redshift:DeleteResourcePolicy" diff --git a/aws-redshiftserverless-namespace/src/main/java/software/amazon/redshiftserverless/namespace/BaseHandlerStd.java b/aws-redshiftserverless-namespace/src/main/java/software/amazon/redshiftserverless/namespace/BaseHandlerStd.java index 96bdc60..899a993 100644 --- a/aws-redshiftserverless-namespace/src/main/java/software/amazon/redshiftserverless/namespace/BaseHandlerStd.java +++ b/aws-redshiftserverless-namespace/src/main/java/software/amazon/redshiftserverless/namespace/BaseHandlerStd.java @@ -10,9 +10,12 @@ import software.amazon.awssdk.services.redshiftserverless.model.InternalServerException; import software.amazon.awssdk.services.redshiftserverless.model.ListSnapshotCopyConfigurationsRequest; import software.amazon.awssdk.services.redshiftserverless.model.ListSnapshotCopyConfigurationsResponse; +import software.amazon.awssdk.services.redshiftserverless.model.ListTagsForResourceRequest; +import software.amazon.awssdk.services.redshiftserverless.model.ListTagsForResourceResponse; import software.amazon.awssdk.services.redshiftserverless.model.ServiceQuotaExceededException; import software.amazon.awssdk.services.redshiftserverless.model.Namespace; import software.amazon.awssdk.services.redshiftserverless.model.ResourceNotFoundException; +import software.amazon.awssdk.services.redshiftserverless.model.RedshiftServerlessResponse; import software.amazon.awssdk.services.redshiftserverless.model.TooManyTagsException; import software.amazon.awssdk.services.redshiftserverless.model.ValidationException; import software.amazon.awssdk.services.redshift.RedshiftClient; @@ -35,6 +38,10 @@ public abstract class BaseHandlerStd extends BaseHandler { protected final String NAMESPACE_STATUS_AVAILABLE = "available"; protected static final Constant BACKOFF_STRATEGY = Constant.of(). timeout(Duration.ofMinutes(30L)).delay(Duration.ofSeconds(10L)).build(); + protected static final Constant PREOPERATION_BACKOFF_STRATEGY = Constant.of() + .timeout(Duration.ofMinutes(5L)) + .delay(Duration.ofSeconds(5L)) + .build(); @Override public final ProgressEvent handleRequest( @@ -62,7 +69,11 @@ protected abstract ProgressEvent handleRequest( final ProxyClient secretsManagerProxyClient, final Logger logger); - protected boolean isNamespaceActive (final ProxyClient proxyClient, ResourceModel resourceModel, CallbackContext context) { + protected boolean isNamespaceActive (final Object awsRequest, + final RedshiftServerlessResponse awsResponse, + final ProxyClient proxyClient, + final ResourceModel resourceModel, + final CallbackContext context) { GetNamespaceRequest getNamespaceRequest = GetNamespaceRequest.builder().namespaceName(resourceModel.getNamespaceName()).build(); GetNamespaceResponse getNamespaceResponse = proxyClient.injectCredentialsAndInvokeV2(getNamespaceRequest, proxyClient.client()::getNamespace); Namespace namespace = getNamespaceResponse.namespace(); @@ -73,6 +84,15 @@ protected boolean isNamespaceActive (final ProxyClient return NAMESPACE_STATUS_AVAILABLE.equalsIgnoreCase(getNamespaceResponse.namespace().statusAsString()); } + protected ListTagsForResourceResponse readTags(final ListTagsForResourceRequest awsRequest, + final ProxyClient proxyClient) { + + ListTagsForResourceResponse awsResponse = proxyClient.injectCredentialsAndInvokeV2(awsRequest, proxyClient.client()::listTagsForResource); + + logger.log(String.format("%s's tags have successfully been read.", ResourceModel.TYPE_NAME)); + return awsResponse; + } + protected boolean isNamespaceActiveAfterDelete (final ProxyClient proxyClient, ResourceModel resourceModel, CallbackContext context) { GetNamespaceRequest getNamespaceRequest = GetNamespaceRequest.builder().namespaceName(resourceModel.getNamespaceName()).build(); try { diff --git a/aws-redshiftserverless-namespace/src/main/java/software/amazon/redshiftserverless/namespace/CreateHandler.java b/aws-redshiftserverless-namespace/src/main/java/software/amazon/redshiftserverless/namespace/CreateHandler.java index 103823f..91d9dfb 100644 --- a/aws-redshiftserverless-namespace/src/main/java/software/amazon/redshiftserverless/namespace/CreateHandler.java +++ b/aws-redshiftserverless-namespace/src/main/java/software/amazon/redshiftserverless/namespace/CreateHandler.java @@ -43,7 +43,7 @@ protected ProgressEvent handleRequest( return proxy.initiate("AWS-RedshiftServerless-Namespace::Create", proxyClient, progress.getResourceModel(), callbackContext) .translateToServiceRequest(Translator::translateToCreateRequest) .makeServiceCall(this::createNamespace) - .stabilize((_awsRequest, _awsResponse, _client, _model, _context) -> isNamespaceActive(_client, _model, _context)) + .stabilize(this::isNamespaceActive) .handleError(this::defaultErrorHandler) .done((_request, _response, _client, _model, _context) -> { callbackContext.setNamespaceArn(_response.namespace().namespaceArn()); diff --git a/aws-redshiftserverless-namespace/src/main/java/software/amazon/redshiftserverless/namespace/ReadHandler.java b/aws-redshiftserverless-namespace/src/main/java/software/amazon/redshiftserverless/namespace/ReadHandler.java index a3486c2..b913db3 100644 --- a/aws-redshiftserverless-namespace/src/main/java/software/amazon/redshiftserverless/namespace/ReadHandler.java +++ b/aws-redshiftserverless-namespace/src/main/java/software/amazon/redshiftserverless/namespace/ReadHandler.java @@ -59,6 +59,16 @@ protected ProgressEvent handleRequest( }); return progress; }) + .then(progress -> { + progress = proxy.initiate("AWS-RedshiftServerless-Namespace::Read::ReadTags", proxyClient, progress.getResourceModel(), progress.getCallbackContext()) + .translateToServiceRequest(Translator::translateToReadTagsRequest) + .makeServiceCall(this::readTags) + .handleError(this::defaultErrorHandler) + .done((_request, _response, _client, _model, _context) -> { + return ProgressEvent.progress(Translator.translateFromReadTagsResponse(_response, _model), _context); + }); + return progress; + }) .then(progress -> { return proxy.initiate("AWS-Redshift-ResourcePolicy::Get", redshiftProxyClient, progress.getResourceModel(), callbackContext) .translateToServiceRequest(resourceModelRequest -> Translator.translateToGetResourcePolicy(resourceModelRequest, callbackContext.getNamespaceArn())) diff --git a/aws-redshiftserverless-namespace/src/main/java/software/amazon/redshiftserverless/namespace/Translator.java b/aws-redshiftserverless-namespace/src/main/java/software/amazon/redshiftserverless/namespace/Translator.java index bc6b2f3..254556e 100644 --- a/aws-redshiftserverless-namespace/src/main/java/software/amazon/redshiftserverless/namespace/Translator.java +++ b/aws-redshiftserverless-namespace/src/main/java/software/amazon/redshiftserverless/namespace/Translator.java @@ -4,6 +4,8 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import software.amazon.awssdk.awscore.AwsRequest; import software.amazon.awssdk.services.redshift.model.DeleteResourcePolicyRequest; import software.amazon.awssdk.services.redshift.model.GetResourcePolicyRequest; @@ -19,6 +21,10 @@ import software.amazon.awssdk.services.redshiftserverless.model.CreateSnapshotCopyConfigurationRequest; import software.amazon.awssdk.services.redshiftserverless.model.UpdateSnapshotCopyConfigurationRequest; import software.amazon.awssdk.services.redshiftserverless.model.DeleteSnapshotCopyConfigurationRequest; +import software.amazon.awssdk.services.redshiftserverless.model.ListTagsForResourceRequest; +import software.amazon.awssdk.services.redshiftserverless.model.ListTagsForResourceResponse; +import software.amazon.awssdk.services.redshiftserverless.model.TagResourceRequest; +import software.amazon.awssdk.services.redshiftserverless.model.UntagResourceRequest; import software.amazon.cloudformation.proxy.Logger; import java.io.IOException; @@ -38,6 +44,7 @@ */ public class Translator { + private static final Gson GSON = new GsonBuilder().create(); /** * Request to create a resource @@ -54,22 +61,13 @@ static CreateNamespaceRequest translateToCreateRequest(final ResourceModel model .defaultIamRoleArn(model.getDefaultIamRoleArn()) .iamRoles(model.getIamRoles()) .logExportsWithStrings(model.getLogExports()) - .tags(translateTagsToSdk(model.getTags())) + .tags(translateToSdkTags(model.getTags())) .manageAdminPassword(model.getManageAdminPassword()) .adminPasswordSecretKmsKeyId(model.getAdminPasswordSecretKmsKeyId()) .redshiftIdcApplicationArn(model.getRedshiftIdcApplicationArn()) .build(); } - static List translateTagsToSdk(final List tags) { - return Optional.ofNullable(tags).orElse(Collections.emptyList()) - .stream() - .map(tag -> software.amazon.awssdk.services.redshiftserverless.model.Tag.builder() - .key(tag.getKey()) - .value(tag.getValue()).build()) - .collect(Collectors.toList()); - } - /* This function is to return the iam role in the same format as input iam roles. Instead of modifying the schema for backward compatibitlity we use regex to extract the iam role. @@ -264,6 +262,90 @@ static AwsRequest untagResourceRequest(final ResourceModel model, final Set toBeCreatedTags = desiredResourceState.getTags() == null ? Collections.emptyList() : desiredResourceState.getTags() + .stream() + .filter(tag -> currentResourceState.getTags() == null || !currentResourceState.getTags().contains(tag)) + .collect(Collectors.toList()); + + List toBeDeletedTags = currentResourceState.getTags() == null ? Collections.emptyList() : currentResourceState.getTags() + .stream() + .filter(tag -> desiredResourceState.getTags() == null || !desiredResourceState.getTags().contains(tag)) + .collect(Collectors.toList()); + + return UpdateTagsRequest.builder() + .createNewTagsRequest(TagResourceRequest.builder() + .tags(translateToSdkTags(toBeCreatedTags)) + .resourceArn(resourceArn) + .build()) + .deleteOldTagsRequest(UntagResourceRequest.builder() + .tagKeys(toBeDeletedTags + .stream() + .map(Tag::getKey) + .collect(Collectors.toList())) + .resourceArn(resourceArn) + .build()) + .build(); + } + + private static software.amazon.awssdk.services.redshiftserverless.model.Tag translateToSdkTag(Tag tag) { + return GSON.fromJson(GSON.toJson(tag), software.amazon.awssdk.services.redshiftserverless.model.Tag.class); + } + + private static List translateToSdkTags(final List tags) { + return tags == null ? null : tags + .stream() + .map(Translator::translateToSdkTag) + .collect(Collectors.toList()); + } + + private static Tag translateToModelTag(software.amazon.awssdk.services.redshiftserverless.model.Tag tag) { + return GSON.fromJson(GSON.toJson(tag), Tag.class); + } + + private static List translateToModelTags(Collection tags) { + return tags == null ? null : tags + .stream() + .map(Translator::translateToModelTag) + .collect(Collectors.toList()); + } + private static Namespace translateToModelNamespace( software.amazon.awssdk.services.redshiftserverless.model.Namespace namespace) { diff --git a/aws-redshiftserverless-namespace/src/main/java/software/amazon/redshiftserverless/namespace/UpdateHandler.java b/aws-redshiftserverless-namespace/src/main/java/software/amazon/redshiftserverless/namespace/UpdateHandler.java index f3164aa..ea5094a 100644 --- a/aws-redshiftserverless-namespace/src/main/java/software/amazon/redshiftserverless/namespace/UpdateHandler.java +++ b/aws-redshiftserverless-namespace/src/main/java/software/amazon/redshiftserverless/namespace/UpdateHandler.java @@ -15,8 +15,11 @@ import software.amazon.awssdk.services.redshiftserverless.model.DeleteSnapshotCopyConfigurationResponse; import software.amazon.awssdk.services.redshiftserverless.model.GetNamespaceRequest; import software.amazon.awssdk.services.redshiftserverless.model.GetNamespaceResponse; +import software.amazon.awssdk.services.redshiftserverless.model.ListTagsForResourceRequest; +import software.amazon.awssdk.services.redshiftserverless.model.ListTagsForResourceResponse; import software.amazon.awssdk.services.redshiftserverless.model.ListSnapshotCopyConfigurationsResponse; import software.amazon.awssdk.services.redshiftserverless.model.ResourceNotFoundException; +import software.amazon.awssdk.services.redshiftserverless.model.TagResourceResponse; import software.amazon.awssdk.services.redshiftserverless.model.UpdateNamespaceRequest; import software.amazon.awssdk.services.redshiftserverless.model.UpdateNamespaceResponse; import software.amazon.awssdk.services.redshiftserverless.model.UpdateSnapshotCopyConfigurationRequest; @@ -28,6 +31,7 @@ import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy; import software.amazon.cloudformation.proxy.HandlerErrorCode; import software.amazon.cloudformation.proxy.Logger; +import software.amazon.cloudformation.proxy.OperationStatus; import software.amazon.cloudformation.proxy.ProgressEvent; import software.amazon.cloudformation.proxy.ProxyClient; import software.amazon.cloudformation.proxy.ResourceHandlerRequest; @@ -61,8 +65,7 @@ protected ProgressEvent handleRequest( ResourceModel tempUpdateRequestModel = currentModel.toBuilder() .adminUserPassword(StringUtils.equals(prevModel.getAdminUserPassword(), currentModel.getAdminUserPassword()) ? null : currentModel.getAdminUserPassword()) .adminUsername(StringUtils.equals(prevModel.getAdminUsername(), currentModel.getAdminUsername()) ? null : currentModel.getAdminUsername()) - //TODO: we only support updating db-name after GA -// .dbName(StringUtils.equals(prevModel.getDbName(), currentModel.getDbName()) ? null : currentModel.getDbName()) + .dbName(StringUtils.equals(prevModel.getDbName(), currentModel.getDbName()) ? null : currentModel.getDbName()) .kmsKeyId(StringUtils.equals(prevModel.getKmsKeyId(), currentModel.getKmsKeyId()) ? null : currentModel.getKmsKeyId()) .defaultIamRoleArn(StringUtils.equals(prevModel.getDefaultIamRoleArn(), currentModel.getDefaultIamRoleArn()) ? null : currentModel.getDefaultIamRoleArn()) .iamRoles(compareListParamsEqualOrNot(prevModel.getIamRoles(), currentModel.getIamRoles()) ? null : currentModel.getIamRoles()) @@ -97,25 +100,49 @@ protected ProgressEvent handleRequest( final ResourceModel updateRequestModel = tempUpdateRequestModel; return ProgressEvent.progress(currentModel, callbackContext) - .then(progress -> - proxy.initiate("AWS-RedshiftServerless-Namespace::Update::first", proxyClient, updateRequestModel, progress.getCallbackContext()) - .translateToServiceRequest(Translator::translateToUpdateRequest) - .backoffDelay(BACKOFF_STRATEGY) - .makeServiceCall(this::updateNamespace) - .stabilize((_awsRequest, _awsResponse, _client, _model, _context) -> isNamespaceActive(_client, _model, _context)) - .handleError(this::defaultErrorHandler) - .progress()) .then(progress -> { - progress = proxy.initiate("AWS-RedshiftServerless-Namespace::ReadOnly", proxyClient, updateRequestModel, callbackContext) - .translateToServiceRequest(Translator::translateToReadRequest) - .makeServiceCall(this::getNamespace) + progress = proxy.initiate("AWS-RedshiftServerless-Namespace::Update::ReadInstanceBeforeUpdate", proxyClient, updateRequestModel, callbackContext) + .translateToServiceRequest(Translator::translateToReadRequest) + .backoffDelay(PREOPERATION_BACKOFF_STRATEGY)// We wait for max of 5mins here + .makeServiceCall(this::getNamespace) + .stabilize(this::isNamespaceActive) // This basically checks for namespace to be stabilized before we perform the update operation + .handleError(this::defaultErrorHandler) + .done(awsResponse -> { + callbackContext.setNamespaceArn(awsResponse.namespace().namespaceArn()); + return ProgressEvent.progress(Translator.translateFromReadResponse(awsResponse), callbackContext); + }); + return progress; + }) + .then(progress -> { + progress = proxy.initiate("AWS-RedshiftServerless-Namespace::Update::ReadTags", proxyClient, progress.getResourceModel(), progress.getCallbackContext()) + .translateToServiceRequest(Translator::translateToReadTagsRequest) + .makeServiceCall(this::readTags) .handleError(this::defaultErrorHandler) - .done(awsResponse -> { - callbackContext.setNamespaceArn(awsResponse.namespace().namespaceArn()); - return ProgressEvent.progress(Translator.translateFromReadResponse(awsResponse), callbackContext); + .done((tagsRequest, tagsResponse, client, model, context) -> { + return ProgressEvent.progress(Translator.translateFromReadTagsResponse(tagsResponse, model), callbackContext); }); return progress; }) + .then(progress -> { + progress = proxy.initiate("AWS-RedshiftServerless-Workgroup::Update::UpdateTags", proxyClient, progress.getResourceModel(), progress.getCallbackContext()) + .translateToServiceRequest(resourceModel -> Translator.translateToUpdateTagsRequest(request.getDesiredResourceState(), resourceModel)) + .backoffDelay(BACKOFF_STRATEGY) + .makeServiceCall(this::updateTags) + .stabilize(this::isNamespaceActive) + .handleError(this::defaultErrorHandler) + .progress(); + return progress; + }) + .then(progress -> { + progress = proxy.initiate("AWS-RedshiftServerless-Namespace::UpdateInstance", proxyClient, updateRequestModel, progress.getCallbackContext()) + .translateToServiceRequest(Translator::translateToUpdateRequest) + .backoffDelay(BACKOFF_STRATEGY) + .makeServiceCall(this::updateNamespace) + .stabilize(this::isNamespaceActive) + .handleError(this::defaultErrorHandler) + .progress(); + return progress; + }) .then(progress -> { if (callbackContext.getNamespaceArn() != null && currentModel.getNamespaceResourcePolicy() != null) { if (currentModel.getNamespaceResourcePolicy().isEmpty()) { @@ -185,6 +212,29 @@ protected ProgressEvent handleRequest( .then(progress -> new ReadHandler().handleRequest(proxy, request, callbackContext, proxyClient, redshiftProxyClient, secretsManagerProxyClient, logger)); } + private TagResourceResponse updateTags(final UpdateTagsRequest awsRequest, + final ProxyClient proxyClient) { + TagResourceResponse awsResponse = null; + + if (awsRequest.getDeleteOldTagsRequest().tagKeys().isEmpty()) { + logger.log(String.format("No tags would be deleted for the resource: %s.", ResourceModel.TYPE_NAME)); + + } else { + proxyClient.injectCredentialsAndInvokeV2(awsRequest.getDeleteOldTagsRequest(), proxyClient.client()::untagResource); + logger.log(String.format("Delete tags for the resource: %s.", ResourceModel.TYPE_NAME)); + } + + if (awsRequest.getCreateNewTagsRequest().tags().isEmpty()) { + logger.log(String.format("No tags would be created for the resource: %s.", ResourceModel.TYPE_NAME)); + + } else { + awsResponse = proxyClient.injectCredentialsAndInvokeV2(awsRequest.getCreateNewTagsRequest(), proxyClient.client()::tagResource); + logger.log(String.format("Create tags for the resource: %s.", ResourceModel.TYPE_NAME)); + } + + return awsResponse; + } + private UpdateNamespaceResponse updateNamespace(final UpdateNamespaceRequest updateNamespaceRequest, final ProxyClient proxyClient) { UpdateNamespaceResponse updateNamespaceResponse = null; diff --git a/aws-redshiftserverless-namespace/src/main/java/software/amazon/redshiftserverless/namespace/UpdateTagsRequest.java b/aws-redshiftserverless-namespace/src/main/java/software/amazon/redshiftserverless/namespace/UpdateTagsRequest.java new file mode 100644 index 0000000..166d140 --- /dev/null +++ b/aws-redshiftserverless-namespace/src/main/java/software/amazon/redshiftserverless/namespace/UpdateTagsRequest.java @@ -0,0 +1,17 @@ +package software.amazon.redshiftserverless.namespace; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import software.amazon.awssdk.services.redshiftserverless.model.TagResourceRequest; +import software.amazon.awssdk.services.redshiftserverless.model.UntagResourceRequest; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class UpdateTagsRequest { + private TagResourceRequest createNewTagsRequest; + private UntagResourceRequest deleteOldTagsRequest; +} \ No newline at end of file diff --git a/aws-redshiftserverless-namespace/src/test/java/software/amazon/redshiftserverless/namespace/AbstractTestBase.java b/aws-redshiftserverless-namespace/src/test/java/software/amazon/redshiftserverless/namespace/AbstractTestBase.java index 1709c70..e37f398 100644 --- a/aws-redshiftserverless-namespace/src/test/java/software/amazon/redshiftserverless/namespace/AbstractTestBase.java +++ b/aws-redshiftserverless-namespace/src/test/java/software/amazon/redshiftserverless/namespace/AbstractTestBase.java @@ -185,6 +185,7 @@ public static ResourceModel getCreateResponseResourceModel() { .logExports(LOG_EXPORTS) .namespaceName(NAMESPACE_NAME) .namespace(translateToModelNamespace(NAMESPACE)) + .tags(Collections.emptyList()) .snapshotCopyConfigurations(Collections.emptyList()) .build(); } @@ -283,6 +284,7 @@ public static ResourceModel getNamespaceResponseResourceModel() { .iamRoles(IAM_ROLES) .kmsKeyId(KMS_KEY_ID) .logExports(LOG_EXPORTS) + .tags(Collections.emptyList()) .namespaceName(NAMESPACE_NAME) .namespace(translateToModelNamespace(NAMESPACE)) .snapshotCopyConfigurations(Collections.emptyList()) diff --git a/aws-redshiftserverless-namespace/src/test/java/software/amazon/redshiftserverless/namespace/CreateHandlerTest.java b/aws-redshiftserverless-namespace/src/test/java/software/amazon/redshiftserverless/namespace/CreateHandlerTest.java index eb23b99..4706a21 100644 --- a/aws-redshiftserverless-namespace/src/test/java/software/amazon/redshiftserverless/namespace/CreateHandlerTest.java +++ b/aws-redshiftserverless-namespace/src/test/java/software/amazon/redshiftserverless/namespace/CreateHandlerTest.java @@ -11,6 +11,8 @@ import software.amazon.awssdk.services.redshiftserverless.model.CreateSnapshotCopyConfigurationRequest; import software.amazon.awssdk.services.redshiftserverless.model.CreateSnapshotCopyConfigurationResponse; import software.amazon.awssdk.services.redshiftserverless.model.GetNamespaceRequest; +import software.amazon.awssdk.services.redshiftserverless.model.ListTagsForResourceRequest; +import software.amazon.awssdk.services.redshiftserverless.model.ListTagsForResourceResponse; import software.amazon.awssdk.services.redshiftserverless.model.ListSnapshotCopyConfigurationsRequest; import software.amazon.awssdk.services.redshiftserverless.model.ListSnapshotCopyConfigurationsResponse; import software.amazon.awssdk.services.redshiftserverless.model.SnapshotCopyConfiguration; @@ -78,6 +80,8 @@ public void handleRequest_SimpleSuccess() { final ResourceHandlerRequest request = ResourceHandlerRequest.builder() .desiredResourceState(requestResourceModel) .build(); + + when(proxyClient.client().listTagsForResource(any(ListTagsForResourceRequest.class))).thenReturn(ListTagsForResourceResponse.builder().build()); when(proxyClient.client().createNamespace(any(CreateNamespaceRequest.class))).thenReturn(getCreateResponseSdk()); when(proxyClient.client().getNamespace(any(GetNamespaceRequest.class))).thenReturn(getNamespaceResponseSdk()); when(proxyClient.client().listSnapshotCopyConfigurations(any(ListSnapshotCopyConfigurationsRequest.class))).thenReturn(getSnapshotCopyConfigurationsResponseSdk()); @@ -107,6 +111,8 @@ public void testPutNamespaceResourcePolicy() { final ResourceHandlerRequest request = ResourceHandlerRequest.builder() .desiredResourceState(requestResourceModel) .build(); + + when(proxyClient.client().listTagsForResource(any(ListTagsForResourceRequest.class))).thenReturn(ListTagsForResourceResponse.builder().build()); when(proxyClient.client().createNamespace(any(CreateNamespaceRequest.class))).thenReturn(getCreateResponseSdk()); when(proxyClient.client().getNamespace(any(GetNamespaceRequest.class))).thenReturn(getNamespaceResponseSdk()); when(proxyClient.client().listSnapshotCopyConfigurations(any(ListSnapshotCopyConfigurationsRequest.class))).thenReturn(getSnapshotCopyConfigurationsResponseSdk()); @@ -135,6 +141,8 @@ public void testCreateNamespace_ManagedAdminPassword() { final ResourceHandlerRequest request = ResourceHandlerRequest.builder() .desiredResourceState(requestResourceModel) .build(); + + when(proxyClient.client().listTagsForResource(any(ListTagsForResourceRequest.class))).thenReturn(ListTagsForResourceResponse.builder().build()); when(proxyClient.client().createNamespace(any(CreateNamespaceRequest.class))).thenReturn(getCreateResponseSdkForManagedAdminPasswords()); when(proxyClient.client().getNamespace(any(GetNamespaceRequest.class))).thenReturn(getNamespaceResponseSdkForManagedAdminPasswords()); when(proxyClient.client().listSnapshotCopyConfigurations(any(ListSnapshotCopyConfigurationsRequest.class))).thenReturn(getSnapshotCopyConfigurationsResponseSdk()); @@ -181,6 +189,8 @@ public void testCreateNamespace_SnapshotCopyConfiguration() { final ResourceHandlerRequest request = ResourceHandlerRequest.builder() .desiredResourceState(requestResourceModel) .build(); + + when(proxyClient.client().listTagsForResource(any(ListTagsForResourceRequest.class))).thenReturn(ListTagsForResourceResponse.builder().build()); when(proxyClient.client().createNamespace(any(CreateNamespaceRequest.class))).thenReturn(getCreateResponseSdk()); when(proxyClient.client().getNamespace(any(GetNamespaceRequest.class))).thenReturn(getNamespaceResponseSdk()); when(proxyClient.client().createSnapshotCopyConfiguration(any(CreateSnapshotCopyConfigurationRequest.class))) diff --git a/aws-redshiftserverless-namespace/src/test/java/software/amazon/redshiftserverless/namespace/ReadHandlerTest.java b/aws-redshiftserverless-namespace/src/test/java/software/amazon/redshiftserverless/namespace/ReadHandlerTest.java index 1b9117e..fbb8eb5 100644 --- a/aws-redshiftserverless-namespace/src/test/java/software/amazon/redshiftserverless/namespace/ReadHandlerTest.java +++ b/aws-redshiftserverless-namespace/src/test/java/software/amazon/redshiftserverless/namespace/ReadHandlerTest.java @@ -19,6 +19,8 @@ import software.amazon.cloudformation.exceptions.CfnInvalidRequestException; import software.amazon.awssdk.services.redshiftserverless.model.ListSnapshotCopyConfigurationsRequest; import software.amazon.awssdk.services.redshiftserverless.model.ListSnapshotCopyConfigurationsResponse; +import software.amazon.awssdk.services.redshiftserverless.model.ListTagsForResourceRequest; +import software.amazon.awssdk.services.redshiftserverless.model.ListTagsForResourceResponse; import software.amazon.awssdk.services.redshiftserverless.model.RedshiftServerlessException; import software.amazon.awssdk.services.redshiftserverless.model.ResourceNotFoundException; import software.amazon.awssdk.services.redshiftserverless.model.SnapshotCopyConfiguration; @@ -96,6 +98,7 @@ public void handleRequest_SimpleSuccess() { .desiredResourceState(requestResourceModel) .build(); + when(proxyClient.client().listTagsForResource(any(ListTagsForResourceRequest.class))).thenReturn(ListTagsForResourceResponse.builder().build()); when(proxyClient.client().listSnapshotCopyConfigurations(any(ListSnapshotCopyConfigurationsRequest.class))).thenReturn(getSnapshotCopyConfigurationsResponseSdk()); when(proxyClient.client().getNamespace(any(GetNamespaceRequest.class))).thenReturn(getNamespaceResponseSdk()); when(redshiftProxyClient.client().getResourcePolicy(any(GetResourcePolicyRequest.class))).thenReturn(getEmptyResourcePolicyResponseSdk()); @@ -151,6 +154,7 @@ public void catchResourcePolicyExceptionAsExpected( .desiredResourceState(requestResourceModel) .build(); + when(proxyClient.client().listTagsForResource(any(ListTagsForResourceRequest.class))).thenReturn(ListTagsForResourceResponse.builder().build()); when(proxyClient.client().getNamespace(any(GetNamespaceRequest.class))).thenReturn(getNamespaceResponseSdk()); when(redshiftProxyClient.client().getResourcePolicy(any(GetResourcePolicyRequest.class))) .thenThrow((Throwable) createExceptionWithBuilder(exceptionClass)); @@ -184,6 +188,7 @@ public void testGetNamespaceResourcePolicy() { .desiredResourceState(requestResourceModel) .build(); + when(proxyClient.client().listTagsForResource(any(ListTagsForResourceRequest.class))).thenReturn(ListTagsForResourceResponse.builder().build()); when(proxyClient.client().listSnapshotCopyConfigurations(any(ListSnapshotCopyConfigurationsRequest.class))).thenReturn(getSnapshotCopyConfigurationsResponseSdk()); when(proxyClient.client().getNamespace(any(GetNamespaceRequest.class))).thenReturn(getNamespaceResponseSdk()); when(redshiftProxyClient.client().getResourcePolicy(any(GetResourcePolicyRequest.class))).thenReturn(getResourcePolicyResponseSdk()); @@ -217,6 +222,7 @@ public void handleRequest_ListSnapshotCopyConfigurations() { .desiredResourceState(requestResourceModel) .build(); + when(proxyClient.client().listTagsForResource(any(ListTagsForResourceRequest.class))).thenReturn(ListTagsForResourceResponse.builder().build()); when(proxyClient.client().listSnapshotCopyConfigurations(any(ListSnapshotCopyConfigurationsRequest.class))) .thenReturn(ListSnapshotCopyConfigurationsResponse.builder() .snapshotCopyConfigurations(Collections.singletonList(SnapshotCopyConfiguration.builder() @@ -275,6 +281,7 @@ public void catchListSnapshotCopyConfigurationsExceptionAsExpected( .desiredResourceState(requestResourceModel) .build(); + when(proxyClient.client().listTagsForResource(any(ListTagsForResourceRequest.class))).thenReturn(ListTagsForResourceResponse.builder().build()); when(proxyClient.client().listSnapshotCopyConfigurations(any(ListSnapshotCopyConfigurationsRequest.class))) .thenThrow((Throwable) createExceptionWithBuilder(exceptionClass)); when(proxyClient.client().getNamespace(any(GetNamespaceRequest.class))).thenReturn(getNamespaceResponseSdk()); diff --git a/aws-redshiftserverless-namespace/src/test/java/software/amazon/redshiftserverless/namespace/UpdateHandlerTest.java b/aws-redshiftserverless-namespace/src/test/java/software/amazon/redshiftserverless/namespace/UpdateHandlerTest.java index 4ed0dbb..1b4ff67 100644 --- a/aws-redshiftserverless-namespace/src/test/java/software/amazon/redshiftserverless/namespace/UpdateHandlerTest.java +++ b/aws-redshiftserverless-namespace/src/test/java/software/amazon/redshiftserverless/namespace/UpdateHandlerTest.java @@ -13,6 +13,8 @@ import software.amazon.awssdk.services.redshiftserverless.model.GetNamespaceRequest; import software.amazon.awssdk.services.redshiftserverless.model.ListSnapshotCopyConfigurationsRequest; import software.amazon.awssdk.services.redshiftserverless.model.ListSnapshotCopyConfigurationsResponse; +import software.amazon.awssdk.services.redshiftserverless.model.ListTagsForResourceRequest; +import software.amazon.awssdk.services.redshiftserverless.model.ListTagsForResourceResponse; import software.amazon.awssdk.services.redshiftserverless.model.UpdateNamespaceRequest; import software.amazon.awssdk.services.redshiftserverless.model.SnapshotCopyConfiguration; import software.amazon.awssdk.services.redshiftserverless.model.UpdateSnapshotCopyConfigurationRequest; @@ -92,6 +94,7 @@ public void handleRequest_SimpleSuccess() { .desiredResourceState(requestResourceModel) .build(); + when(proxyClient.client().listTagsForResource(any(ListTagsForResourceRequest.class))).thenReturn(ListTagsForResourceResponse.builder().build()); when(proxyClient.client().listSnapshotCopyConfigurations(any(ListSnapshotCopyConfigurationsRequest.class))).thenReturn(getSnapshotCopyConfigurationsResponseSdk()); when(proxyClient.client().updateNamespace(any(UpdateNamespaceRequest.class))).thenReturn(getUpdateResponseSdk()); when(proxyClient.client().getNamespace(any(GetNamespaceRequest.class))).thenReturn(getNamespaceResponseSdk()); @@ -126,6 +129,7 @@ public void testDeleteNamespaceResourcePolicy() { .previousResourceState(prevModel) .build(); + when(proxyClient.client().listTagsForResource(any(ListTagsForResourceRequest.class))).thenReturn(ListTagsForResourceResponse.builder().build()); when(proxyClient.client().listSnapshotCopyConfigurations(any(ListSnapshotCopyConfigurationsRequest.class))).thenReturn(getSnapshotCopyConfigurationsResponseSdk()); when(proxyClient.client().updateNamespace(any(UpdateNamespaceRequest.class))).thenReturn(getUpdateResponseSdk()); when(proxyClient.client().getNamespace(any(GetNamespaceRequest.class))).thenReturn(getNamespaceResponseSdk()); @@ -168,6 +172,7 @@ public void testModifyNamespaceResourcePolicy() { .policy(NEW_NAMESPACE_RESOURCE_POLICY) .build(); + when(proxyClient.client().listTagsForResource(any(ListTagsForResourceRequest.class))).thenReturn(ListTagsForResourceResponse.builder().build()); when(proxyClient.client().listSnapshotCopyConfigurations(any(ListSnapshotCopyConfigurationsRequest.class))).thenReturn(getSnapshotCopyConfigurationsResponseSdk()); when(proxyClient.client().updateNamespace(any(UpdateNamespaceRequest.class))).thenReturn(getUpdateResponseSdk()); when(proxyClient.client().getNamespace(any(GetNamespaceRequest.class))).thenReturn(getNamespaceResponseSdk()); @@ -204,6 +209,7 @@ public void testUpdate_OptInManagedAdminPassword() { .desiredResourceState(requestResourceModel) .build(); + when(proxyClient.client().listTagsForResource(any(ListTagsForResourceRequest.class))).thenReturn(ListTagsForResourceResponse.builder().build()); when(proxyClient.client().listSnapshotCopyConfigurations(any(ListSnapshotCopyConfigurationsRequest.class))).thenReturn(getSnapshotCopyConfigurationsResponseSdk()); when(proxyClient.client().updateNamespace(any(UpdateNamespaceRequest.class))).thenReturn(getUpdateResponseSdkForManagedAdminPasswords()); when(proxyClient.client().getNamespace(any(GetNamespaceRequest.class))).thenReturn(getNamespaceResponseSdkForManagedAdminPasswords()); @@ -241,6 +247,7 @@ public void testUpdate_OptOutManagedAdminPassword() { .desiredResourceState(requestResourceModel) .build(); + when(proxyClient.client().listTagsForResource(any(ListTagsForResourceRequest.class))).thenReturn(ListTagsForResourceResponse.builder().build()); when(proxyClient.client().listSnapshotCopyConfigurations(any(ListSnapshotCopyConfigurationsRequest.class))).thenReturn(getSnapshotCopyConfigurationsResponseSdk()); when(proxyClient.client().updateNamespace(any(UpdateNamespaceRequest.class))).thenReturn(getUpdateResponseSdk()); when(proxyClient.client().getNamespace(any(GetNamespaceRequest.class))).thenReturn(getNamespaceResponseSdk()); @@ -280,6 +287,7 @@ public void testUpdate_UpdateAdminPasswordSecretKmsKeyId() { .desiredResourceState(requestResourceModel) .build(); + when(proxyClient.client().listTagsForResource(any(ListTagsForResourceRequest.class))).thenReturn(ListTagsForResourceResponse.builder().build()); when(proxyClient.client().listSnapshotCopyConfigurations(any(ListSnapshotCopyConfigurationsRequest.class))).thenReturn(getSnapshotCopyConfigurationsResponseSdk()); when(proxyClient.client().updateNamespace(any(UpdateNamespaceRequest.class))).thenReturn(getUpdateResponseSdkForManagedAdminPasswords()); when(proxyClient.client().getNamespace(any(GetNamespaceRequest.class))).thenReturn(getNamespaceResponseSdkForManagedAdminPasswords()); @@ -344,6 +352,7 @@ public void handleRequest_SnapshotCopyConfiguration_DeleteAndReCreate() { .snapshotRetentionPeriod(-1) .build(); + when(proxyClient.client().listTagsForResource(any(ListTagsForResourceRequest.class))).thenReturn(ListTagsForResourceResponse.builder().build()); when(proxyClient.client().createSnapshotCopyConfiguration(any(CreateSnapshotCopyConfigurationRequest.class))) .thenReturn(CreateSnapshotCopyConfigurationResponse.builder() .snapshotCopyConfiguration(newSnapshotCopyConfiguration) @@ -415,6 +424,7 @@ public void handleRequest_SnapshotCopyConfiguration_Update() { .snapshotRetentionPeriod(-1) .build(); + when(proxyClient.client().listTagsForResource(any(ListTagsForResourceRequest.class))).thenReturn(ListTagsForResourceResponse.builder().build()); when(proxyClient.client().updateSnapshotCopyConfiguration(any(UpdateSnapshotCopyConfigurationRequest.class))) .thenReturn(UpdateSnapshotCopyConfigurationResponse.builder() .snapshotCopyConfiguration(snapshotCopyConfiguration.toBuilder().snapshotRetentionPeriod(2).build()) From 53ac0b5bcfad98eb504c01e74a7202b113a2e292 Mon Sep 17 00:00:00 2001 From: Vikas Ramaneti Date: Fri, 8 Nov 2024 23:50:22 +0000 Subject: [PATCH 2/3] Workgroup stabilization fixes and source to target operation failure fixes --- aws-redshiftserverless-workgroup/.rpdk-config | 5 ++- .../aws-redshiftserverless-workgroup.json | 30 ++++++++++--- .../docs/README.md | 1 + .../docs/configparameter.md | 1 + aws-redshiftserverless-workgroup/docs/tag.md | 1 + .../docs/workgroup.md | 1 + aws-redshiftserverless-workgroup/pom.xml | 6 +++ .../workgroup/BaseHandlerStd.java | 44 +++++++++++++++---- .../workgroup/CreateHandler.java | 13 +++++- .../workgroup/DeleteHandler.java | 22 ++++++++++ .../workgroup/DeleteHandlerTest.java | 2 +- 11 files changed, 108 insertions(+), 18 deletions(-) diff --git a/aws-redshiftserverless-workgroup/.rpdk-config b/aws-redshiftserverless-workgroup/.rpdk-config index ff023bb..0186150 100644 --- a/aws-redshiftserverless-workgroup/.rpdk-config +++ b/aws-redshiftserverless-workgroup/.rpdk-config @@ -24,5 +24,8 @@ "codegen_template_path": "guided_aws", "protocolVersion": "2.0.0" }, - "executableEntrypoint": "software.amazon.redshiftserverless.workgroup.HandlerWrapperExecutable" + "logProcessorEnabled": "true", + "executableEntrypoint": "software.amazon.redshiftserverless.workgroup.HandlerWrapperExecutable", + "contractSettings": {}, + "canarySettings": {} } diff --git a/aws-redshiftserverless-workgroup/aws-redshiftserverless-workgroup.json b/aws-redshiftserverless-workgroup/aws-redshiftserverless-workgroup.json index bfff347..0be1e15 100644 --- a/aws-redshiftserverless-workgroup/aws-redshiftserverless-workgroup.json +++ b/aws-redshiftserverless-workgroup/aws-redshiftserverless-workgroup.json @@ -267,7 +267,16 @@ } }, "tagging": { - "taggable": true + "taggable": true, + "tagOnCreate": true, + "tagUpdatable": true, + "cloudFormationSystemTags": false, + "tagProperty": "/properties/Tags", + "permissions": [ + "redshift-serverless:ListTagsForResource", + "redshift-serverless:TagResource", + "redshift-serverless:UntagResource" + ] }, "additionalProperties": false, "required": [ @@ -328,7 +337,9 @@ "redshift-serverless:CreateNamespace", "redshift-serverless:CreateWorkgroup", "redshift-serverless:GetWorkgroup", - "redshift-serverless:GetNamespace" + "redshift-serverless:GetNamespace", + "redshift-serverless:ListTagsForResource", + "redshift-serverless:TagResource" ] }, "read": { @@ -340,7 +351,8 @@ "ec2:DescribeSubnets", "ec2:DescribeAccountAttributes", "ec2:DescribeAvailabilityZones", - "redshift-serverless:GetWorkgroup" + "redshift-serverless:GetWorkgroup", + "redshift-serverless:ListTagsForResource" ] }, "update": { @@ -356,7 +368,10 @@ "redshift-serverless:TagResource", "redshift-serverless:UntagResource", "redshift-serverless:GetWorkgroup", - "redshift-serverless:UpdateWorkgroup" + "redshift-serverless:UpdateWorkgroup", + "redshift-serverless:ListTagsForResource", + "redshift-serverless:TagResource", + "redshift-serverless:UntagResource" ] }, "delete": { @@ -370,7 +385,9 @@ "ec2:DescribeAvailabilityZones", "redshift-serverless:GetWorkgroup", "redshift-serverless:GetNamespace", - "redshift-serverless:DeleteWorkgroup" + "redshift-serverless:DeleteWorkgroup", + "redshift-serverless:ListTagsForResource", + "redshift-serverless:UntagResource" ] }, "list": { @@ -382,7 +399,8 @@ "ec2:DescribeSubnets", "ec2:DescribeAccountAttributes", "ec2:DescribeAvailabilityZones", - "redshift-serverless:ListWorkgroups" + "redshift-serverless:ListWorkgroups", + "redshift-serverless:ListTagsForResource" ] } } diff --git a/aws-redshiftserverless-workgroup/docs/README.md b/aws-redshiftserverless-workgroup/docs/README.md index 44c55a5..1490b8f 100644 --- a/aws-redshiftserverless-workgroup/docs/README.md +++ b/aws-redshiftserverless-workgroup/docs/README.md @@ -276,3 +276,4 @@ Returns the PubliclyAccessible value. #### CreationDate Returns the CreationDate value. + diff --git a/aws-redshiftserverless-workgroup/docs/configparameter.md b/aws-redshiftserverless-workgroup/docs/configparameter.md index 6772991..0d2771b 100644 --- a/aws-redshiftserverless-workgroup/docs/configparameter.md +++ b/aws-redshiftserverless-workgroup/docs/configparameter.md @@ -41,3 +41,4 @@ _Type_: String _Maximum Length_: 15000 _Update requires_: [No interruption](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-no-interrupt) + diff --git a/aws-redshiftserverless-workgroup/docs/tag.md b/aws-redshiftserverless-workgroup/docs/tag.md index 38b523f..62f127e 100644 --- a/aws-redshiftserverless-workgroup/docs/tag.md +++ b/aws-redshiftserverless-workgroup/docs/tag.md @@ -43,3 +43,4 @@ _Type_: String _Maximum Length_: 256 _Update requires_: [No interruption](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-no-interrupt) + diff --git a/aws-redshiftserverless-workgroup/docs/workgroup.md b/aws-redshiftserverless-workgroup/docs/workgroup.md index b9319c5..acdf447 100644 --- a/aws-redshiftserverless-workgroup/docs/workgroup.md +++ b/aws-redshiftserverless-workgroup/docs/workgroup.md @@ -38,3 +38,4 @@ _Required_: No _Type_: Endpoint _Update requires_: [No interruption](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-no-interrupt) + diff --git a/aws-redshiftserverless-workgroup/pom.xml b/aws-redshiftserverless-workgroup/pom.xml index 4aeeef3..7ce3568 100644 --- a/aws-redshiftserverless-workgroup/pom.xml +++ b/aws-redshiftserverless-workgroup/pom.xml @@ -32,6 +32,12 @@ redshiftserverless 2.22.5 + + + software.amazon.awssdk + secretsmanager + 2.22.5 + org.projectlombok diff --git a/aws-redshiftserverless-workgroup/src/main/java/software/amazon/redshiftserverless/workgroup/BaseHandlerStd.java b/aws-redshiftserverless-workgroup/src/main/java/software/amazon/redshiftserverless/workgroup/BaseHandlerStd.java index 5fbdc19..98abce4 100644 --- a/aws-redshiftserverless-workgroup/src/main/java/software/amazon/redshiftserverless/workgroup/BaseHandlerStd.java +++ b/aws-redshiftserverless-workgroup/src/main/java/software/amazon/redshiftserverless/workgroup/BaseHandlerStd.java @@ -38,7 +38,7 @@ public abstract class BaseHandlerStd extends BaseHandler { protected Logger logger; public static final String BUSY_WORKGROUP_RETRY_EXCEPTION_MESSAGE = - "There is an operation running on the existing workgroup"; + "There is an operation in progress"; // This is for delete workgroup operation. We need AdminWF to finish the operation completely // This is needed for CTV2 to work @@ -54,7 +54,7 @@ protected static boolean isRetriableWorkgroupException(ConflictException excepti .build(); protected static final Constant PREOPERATION_BACKOFF_STRATEGY = Constant.of() - .timeout(Duration.ofMinutes(5L)) + .timeout(Duration.ofMinutes(60L)) .delay(Duration.ofSeconds(5L)) .build(); @@ -130,14 +130,40 @@ protected UpdateWorkgroupResponse updateWorkgroup(final UpdateWorkgroupRequest a } + // Since there are source to target operations on the cluster + // We will receive operation in progress in such cases. + // This needs to be handled on CFN side as this will break contract tests protected DeleteWorkgroupResponse deleteWorkgroup(final DeleteWorkgroupRequest awsRequest, - final ProxyClient proxyClient) { - - DeleteWorkgroupResponse awsResponse = proxyClient.injectCredentialsAndInvokeV2( - awsRequest, proxyClient.client()::deleteWorkgroup); - - logger.log(String.format("Workgroup : %s has successfully been deleted.", awsResponse.workgroup().workgroupName())); - logger.log(awsResponse.toString()); + final ProxyClient proxyClient) throws ConflictException { + boolean operationInProgress = false; + int max_retries = 5; + int current_retry = 0; + DeleteWorkgroupResponse awsResponse = null; + + do { + try { + awsResponse = proxyClient.injectCredentialsAndInvokeV2(awsRequest, proxyClient.client()::deleteWorkgroup); + logger.log(String.format("Workgroup : %s has successfully been deleted.", awsResponse.workgroup().workgroupName())); + logger.log(awsResponse.toString()); + operationInProgress = false; + } catch (ConflictException e) { + Pattern pattern = Pattern.compile(".*There is an operation in progress.*", Pattern.CASE_INSENSITIVE); + if(pattern.matcher(e.getMessage()).matches()) { + logger.log("There is an operation in progress during delete. We will wait and retry in 60 secs"); + operationInProgress = true; + current_retry = current_retry + 1; + // Since there are source to target operations on the cluster + try { + Thread.sleep(60000); + } catch(InterruptedException ex) { + Thread.currentThread().interrupt(); + } + } else { + // We need to explicitly catch operation in progress or reraise other conflict exceptions + throw e; + } + } + } while((current_retry < max_retries) && operationInProgress); return awsResponse; } diff --git a/aws-redshiftserverless-workgroup/src/main/java/software/amazon/redshiftserverless/workgroup/CreateHandler.java b/aws-redshiftserverless-workgroup/src/main/java/software/amazon/redshiftserverless/workgroup/CreateHandler.java index b49677a..2795646 100644 --- a/aws-redshiftserverless-workgroup/src/main/java/software/amazon/redshiftserverless/workgroup/CreateHandler.java +++ b/aws-redshiftserverless-workgroup/src/main/java/software/amazon/redshiftserverless/workgroup/CreateHandler.java @@ -35,6 +35,17 @@ protected ProgressEvent handleRequest( this.logger = logger; return ProgressEvent.progress(request.getDesiredResourceState(), callbackContext) + .then(progress -> + proxy.initiate("AWS-RedshiftServerless-Workgroup::Create::ReadNamespaceBeforeCreate", proxyClient, progress.getResourceModel(), progress.getCallbackContext()) + .translateToServiceRequest(Translator::translateToReadNamespaceRequest) + .backoffDelay(PREOPERATION_BACKOFF_STRATEGY)// We wait for max of 5mins here + .makeServiceCall(this::readNamespace) + .stabilize(this::isNamespaceStable) // This basically checks for namespace to be in stable state before we create workgroup + .handleError(this::createWorkgroupErrorHandler) + .done(awsResponse -> { + return ProgressEvent.progress(request.getDesiredResourceState(), callbackContext); + }) + ) .then(progress -> proxy.initiate("AWS-RedshiftServerless-Workgroup::Create", proxyClient, progress.getResourceModel(), progress.getCallbackContext()) .translateToServiceRequest(Translator::translateToCreateRequest) @@ -47,7 +58,7 @@ protected ProgressEvent handleRequest( }) ) .then(progress -> - proxy.initiate("AWS-RedshiftServerless-Workgroup::ReadNameSpace", proxyClient, progress.getResourceModel(), progress.getCallbackContext()) + proxy.initiate("AWS-RedshiftServerless-Workgroup::ReadNameSpaceAfterCreate", proxyClient, progress.getResourceModel(), progress.getCallbackContext()) .translateToServiceRequest(Translator::translateToReadNamespaceRequest) .backoffDelay(BACKOFF_STRATEGY) .makeServiceCall(this::readNamespace) diff --git a/aws-redshiftserverless-workgroup/src/main/java/software/amazon/redshiftserverless/workgroup/DeleteHandler.java b/aws-redshiftserverless-workgroup/src/main/java/software/amazon/redshiftserverless/workgroup/DeleteHandler.java index 315e650..9da8768 100644 --- a/aws-redshiftserverless-workgroup/src/main/java/software/amazon/redshiftserverless/workgroup/DeleteHandler.java +++ b/aws-redshiftserverless-workgroup/src/main/java/software/amazon/redshiftserverless/workgroup/DeleteHandler.java @@ -31,6 +31,28 @@ protected ProgressEvent handleRequest(final Amaz this.logger = logger; return ProgressEvent.progress(request.getDesiredResourceState(), callbackContext) + .then(progress -> + proxy.initiate("AWS-RedshiftServerless-Workgroup::Delete::ReadWorkgroup", proxyClient, progress.getResourceModel(), progress.getCallbackContext()) + .translateToServiceRequest(Translator::translateToReadRequest) + .backoffDelay(PREOPERATION_BACKOFF_STRATEGY)// We wait for max of 5mins here + .makeServiceCall(this::readWorkgroup) + .stabilize(this::isWorkgroupStable) // This basically checks for workgroup to be in stable state before we delete workgroup + .handleError(this::deleteWorkgroupErrorHandler) + .done( awsResponse -> { + return ProgressEvent.progress(Translator.translateFromReadResponse(awsResponse), callbackContext); + }) + ) + .then(progress -> + proxy.initiate("AWS-RedshiftServerless-Workgroup::Delete::ReadNamespaceBeforeDelete", proxyClient, progress.getResourceModel(), progress.getCallbackContext()) + .translateToServiceRequest(Translator::translateToReadNamespaceRequest) + .backoffDelay(PREOPERATION_BACKOFF_STRATEGY)// We wait for max of 5mins here + .makeServiceCall(this::readNamespace) + .stabilize(this::isNamespaceStable) // This basically checks for namespace to be in stable state before we delete workgroup + .handleError(this::deleteWorkgroupErrorHandler) + .done( awsResponse -> { + return ProgressEvent.progress(request.getDesiredResourceState(), callbackContext); + }) + ) .then(progress -> proxy.initiate("AWS-RedshiftServerless-Workgroup::Delete", proxyClient, progress.getResourceModel(), progress.getCallbackContext()) .translateToServiceRequest(Translator::translateToDeleteRequest) diff --git a/aws-redshiftserverless-workgroup/src/test/java/software/amazon/redshiftserverless/workgroup/DeleteHandlerTest.java b/aws-redshiftserverless-workgroup/src/test/java/software/amazon/redshiftserverless/workgroup/DeleteHandlerTest.java index 216cbc1..d42d4bf 100644 --- a/aws-redshiftserverless-workgroup/src/test/java/software/amazon/redshiftserverless/workgroup/DeleteHandlerTest.java +++ b/aws-redshiftserverless-workgroup/src/test/java/software/amazon/redshiftserverless/workgroup/DeleteHandlerTest.java @@ -63,7 +63,7 @@ public void handleRequest_SimpleSuccess() { when(proxyClient.client().deleteWorkgroup(any(DeleteWorkgroupRequest.class))).thenReturn(deleteResponseSdk()); // We first retreive workgroup, then wait for workgroup to be in available state and then get workgroup after delete operation - when(proxyClient.client().getWorkgroup(any(GetWorkgroupRequest.class))) + when(proxyClient.client().getWorkgroup(any(GetWorkgroupRequest.class))).thenReturn(getReadResponseSdk()).thenReturn(getReadResponseSdk()) .thenThrow(ResourceNotFoundException.builder().build()); when(proxyClient.client().getNamespace(any(GetNamespaceRequest.class))).thenReturn(getNamespaceResponseSdk()); From ae09d0756f1c34d72050b59610e613458dd37bf3 Mon Sep 17 00:00:00 2001 From: Vikas Ramaneti Date: Mon, 11 Nov 2024 17:41:20 +0000 Subject: [PATCH 3/3] Changes to accommodate for Uluru Tags contract tests --- .../aws-redshiftserverless-workgroup.json | 5 +-- .../workgroup/BaseHandlerStd.java | 11 ++++++ .../workgroup/CreateHandler.java | 4 +++ .../workgroup/DeleteHandler.java | 10 ------ .../workgroup/ReadHandler.java | 35 ++++++++++++++----- .../workgroup/UpdateHandler.java | 17 ++++----- .../workgroup/AbstractTestBase.java | 1 + .../workgroup/CreateHandlerTest.java | 3 ++ .../workgroup/DeleteHandlerTest.java | 18 +++------- .../workgroup/ReadHandlerTest.java | 3 ++ 10 files changed, 59 insertions(+), 48 deletions(-) diff --git a/aws-redshiftserverless-workgroup/aws-redshiftserverless-workgroup.json b/aws-redshiftserverless-workgroup/aws-redshiftserverless-workgroup.json index 0be1e15..d2f479f 100644 --- a/aws-redshiftserverless-workgroup/aws-redshiftserverless-workgroup.json +++ b/aws-redshiftserverless-workgroup/aws-redshiftserverless-workgroup.json @@ -316,10 +316,7 @@ "/properties/MaxCapacity", "/properties/ConfigParameters", "/properties/SecurityGroupIds", - "/properties/SubnetIds", - "/properties/Tags", - "/properties/Tags/*/Key", - "/properties/Tags/*/Value" + "/properties/SubnetIds" ], "primaryIdentifier": [ "/properties/WorkgroupName" diff --git a/aws-redshiftserverless-workgroup/src/main/java/software/amazon/redshiftserverless/workgroup/BaseHandlerStd.java b/aws-redshiftserverless-workgroup/src/main/java/software/amazon/redshiftserverless/workgroup/BaseHandlerStd.java index 98abce4..bfe6025 100644 --- a/aws-redshiftserverless-workgroup/src/main/java/software/amazon/redshiftserverless/workgroup/BaseHandlerStd.java +++ b/aws-redshiftserverless-workgroup/src/main/java/software/amazon/redshiftserverless/workgroup/BaseHandlerStd.java @@ -10,6 +10,8 @@ import software.amazon.awssdk.services.redshiftserverless.model.GetNamespaceResponse; import software.amazon.awssdk.services.redshiftserverless.model.GetWorkgroupRequest; import software.amazon.awssdk.services.redshiftserverless.model.GetWorkgroupResponse; +import software.amazon.awssdk.services.redshiftserverless.model.ListTagsForResourceRequest; +import software.amazon.awssdk.services.redshiftserverless.model.ListTagsForResourceResponse; import software.amazon.awssdk.services.redshiftserverless.model.Namespace; import software.amazon.awssdk.services.redshiftserverless.model.NamespaceStatus; import software.amazon.awssdk.services.redshiftserverless.model.WorkgroupStatus; @@ -231,6 +233,15 @@ protected boolean isWorkgroupDeleted(final Object awsRequest, return false; } + protected ListTagsForResourceResponse readTags(final ListTagsForResourceRequest awsRequest, + final ProxyClient proxyClient) { + ListTagsForResourceResponse awsResponse; + awsResponse = proxyClient.injectCredentialsAndInvokeV2(awsRequest, proxyClient.client()::listTagsForResource); + + logger.log(String.format("%s's tags have successfully been read.", ResourceModel.TYPE_NAME)); + return awsResponse; + } + protected ProgressEvent defaultWorkgroupErrorHandler(final Object awsRequest, final Exception exception, final ProxyClient client, diff --git a/aws-redshiftserverless-workgroup/src/main/java/software/amazon/redshiftserverless/workgroup/CreateHandler.java b/aws-redshiftserverless-workgroup/src/main/java/software/amazon/redshiftserverless/workgroup/CreateHandler.java index 2795646..31cee93 100644 --- a/aws-redshiftserverless-workgroup/src/main/java/software/amazon/redshiftserverless/workgroup/CreateHandler.java +++ b/aws-redshiftserverless-workgroup/src/main/java/software/amazon/redshiftserverless/workgroup/CreateHandler.java @@ -78,6 +78,10 @@ private ProgressEvent createWorkgroupErrorHandle logger.log(String.format("Operation: %s : encountered exception for model: %s", awsRequest.getClass().getName(), ResourceModel.TYPE_NAME)); logger.log(awsRequest.toString()); + Pattern pattern = Pattern.compile(".*is not authorized to perform: redshift-serverless:TagResource.*", Pattern.CASE_INSENSITIVE); + if(pattern.matcher(exception.getMessage()).matches()){ + return ProgressEvent.failed(model, context, HandlerErrorCode.UnauthorizedTaggingOperation, exception.getMessage()); + } return this.defaultWorkgroupErrorHandler(awsRequest, exception, client, model, context); } diff --git a/aws-redshiftserverless-workgroup/src/main/java/software/amazon/redshiftserverless/workgroup/DeleteHandler.java b/aws-redshiftserverless-workgroup/src/main/java/software/amazon/redshiftserverless/workgroup/DeleteHandler.java index 9da8768..ae86179 100644 --- a/aws-redshiftserverless-workgroup/src/main/java/software/amazon/redshiftserverless/workgroup/DeleteHandler.java +++ b/aws-redshiftserverless-workgroup/src/main/java/software/amazon/redshiftserverless/workgroup/DeleteHandler.java @@ -64,16 +64,6 @@ protected ProgressEvent handleRequest(final Amaz return ProgressEvent.progress(Translator.translateFromDeleteResponse(awsResponse), callbackContext); }) ) - .then(progress -> { - if (progress.getCallbackContext().isPropagationDelay()) { - logger.log("Propagation delay completed"); - return ProgressEvent.progress(progress.getResourceModel(), progress.getCallbackContext()); - } - progress.getCallbackContext().setPropagationDelay(true); - logger.log("Setting propagation delay"); - return ProgressEvent.defaultInProgressHandler(progress.getCallbackContext(), - EVENTUAL_CONSISTENCY_DELAY_SECONDS, progress.getResourceModel()); - }) .then(progress -> proxy.initiate("AWS-RedshiftServerless-Workgroup::ReadNameSpaceAfterDelete", proxyClient, progress.getResourceModel(), progress.getCallbackContext()) .translateToServiceRequest(Translator::translateToReadNamespaceRequest) diff --git a/aws-redshiftserverless-workgroup/src/main/java/software/amazon/redshiftserverless/workgroup/ReadHandler.java b/aws-redshiftserverless-workgroup/src/main/java/software/amazon/redshiftserverless/workgroup/ReadHandler.java index 326e776..349f228 100644 --- a/aws-redshiftserverless-workgroup/src/main/java/software/amazon/redshiftserverless/workgroup/ReadHandler.java +++ b/aws-redshiftserverless-workgroup/src/main/java/software/amazon/redshiftserverless/workgroup/ReadHandler.java @@ -26,15 +26,32 @@ protected ProgressEvent handleRequest( final Logger logger) { this.logger = logger; + final ResourceModel model = request.getDesiredResourceState(); - return proxy.initiate("AWS-RedshiftServerless-Workgroup::Read", proxyClient, request.getDesiredResourceState(), callbackContext) - .translateToServiceRequest(Translator::translateToReadRequest) - .makeServiceCall(this::readWorkgroup) - .handleError((awsRequest, exception, client, resourceModel, cxt) -> { - logger.log(String.format("Operation: %s : encountered exception for model: %s", awsRequest.getClass().getName(), ResourceModel.TYPE_NAME)); - logger.log(awsRequest.toString()); - return this.defaultWorkgroupErrorHandler(awsRequest, exception, client, resourceModel, cxt); - }) - .done(awsResponse -> ProgressEvent.defaultSuccessHandler(Translator.translateFromReadResponse(awsResponse))); + return ProgressEvent.progress(model, callbackContext) + .then (progress -> { + progress = proxy.initiate("AWS-RedshiftServerless-Workgroup::Read", proxyClient, request.getDesiredResourceState(), callbackContext) + .translateToServiceRequest(Translator::translateToReadRequest) + .makeServiceCall(this::readWorkgroup) + .handleError((awsRequest, exception, client, resourceModel, cxt) -> { + logger.log(String.format("Operation: %s : encountered exception for model: %s", awsRequest.getClass().getName(), ResourceModel.TYPE_NAME)); + logger.log(awsRequest.toString()); + return this.defaultWorkgroupErrorHandler(awsRequest, exception, client, resourceModel, cxt); + }) + .done(awsResponse -> ProgressEvent.progress(Translator.translateFromReadResponse(awsResponse), callbackContext)); + + return progress; + }).then(progress -> { + progress = proxy.initiate("AWS-RedshiftServerless-Namespace::Read::ReadTags", proxyClient, progress.getResourceModel(), progress.getCallbackContext()) + .translateToServiceRequest(Translator::translateToReadTagsRequest) + .makeServiceCall(this::readTags) + .handleError((_awsRequest, _sdkEx, _client, _model, _callbackContext) -> + ProgressEvent.failed(_model, _callbackContext, HandlerErrorCode.UnauthorizedTaggingOperation, _sdkEx.getMessage()) + ) + .done((_request, _response, _client, _model, _context) -> { + return ProgressEvent.defaultSuccessHandler(Translator.translateFromReadTagsResponse(_response, _model)); + }); + return progress; + }); } } diff --git a/aws-redshiftserverless-workgroup/src/main/java/software/amazon/redshiftserverless/workgroup/UpdateHandler.java b/aws-redshiftserverless-workgroup/src/main/java/software/amazon/redshiftserverless/workgroup/UpdateHandler.java index 33291bd..5a45a8a 100644 --- a/aws-redshiftserverless-workgroup/src/main/java/software/amazon/redshiftserverless/workgroup/UpdateHandler.java +++ b/aws-redshiftserverless-workgroup/src/main/java/software/amazon/redshiftserverless/workgroup/UpdateHandler.java @@ -61,7 +61,9 @@ protected ProgressEvent handleRequest( proxy.initiate("AWS-RedshiftServerless-Workgroup::Update::ReadTags", proxyClient, progress.getResourceModel(), progress.getCallbackContext()) .translateToServiceRequest(Translator::translateToReadTagsRequest) .makeServiceCall(this::readTags) - .handleError(this::updateWorkgroupErrorHandler) + .handleError((_awsRequest, _sdkEx, _client, _model, _callbackContext) -> + ProgressEvent.failed(_model, _callbackContext, HandlerErrorCode.UnauthorizedTaggingOperation, _sdkEx.getMessage()) + ) .done((tagsRequest, tagsResponse, client, model, context) -> ProgressEvent.builder() .callbackContext(context) .callbackDelaySeconds(0) @@ -75,7 +77,9 @@ protected ProgressEvent handleRequest( .backoffDelay(BACKOFF_STRATEGY) .makeServiceCall(this::updateTags) .stabilize(this::isWorkgroupStable) - .handleError(this::updateWorkgroupErrorHandler) + .handleError((_awsRequest, _sdkEx, _client, _model, _callbackContext) -> + ProgressEvent.failed(_model, _callbackContext, HandlerErrorCode.UnauthorizedTaggingOperation, _sdkEx.getMessage()) + ) .progress()) .then(progress -> @@ -122,15 +126,6 @@ private ResourceModel getUpdatableResourceModel(ResourceModel desiredModel, Reso .build(); } - private ListTagsForResourceResponse readTags(final ListTagsForResourceRequest awsRequest, - final ProxyClient proxyClient) { - ListTagsForResourceResponse awsResponse; - awsResponse = proxyClient.injectCredentialsAndInvokeV2(awsRequest, proxyClient.client()::listTagsForResource); - - logger.log(String.format("%s's tags have successfully been read.", ResourceModel.TYPE_NAME)); - return awsResponse; - } - private TagResourceResponse updateTags(final UpdateTagsRequest awsRequest, final ProxyClient proxyClient) { TagResourceResponse awsResponse = null; diff --git a/aws-redshiftserverless-workgroup/src/test/java/software/amazon/redshiftserverless/workgroup/AbstractTestBase.java b/aws-redshiftserverless-workgroup/src/test/java/software/amazon/redshiftserverless/workgroup/AbstractTestBase.java index 26c328d..71de3f9 100644 --- a/aws-redshiftserverless-workgroup/src/test/java/software/amazon/redshiftserverless/workgroup/AbstractTestBase.java +++ b/aws-redshiftserverless-workgroup/src/test/java/software/amazon/redshiftserverless/workgroup/AbstractTestBase.java @@ -125,6 +125,7 @@ public static ResourceModel getReadResponseResourceModel() { .configParameters(CONFIG_PARAMETERS) .port(DEFAULT_PORT) .publiclyAccessible(true) + .tags(List.of()) .workgroup(Workgroup.builder() .workgroupName(WORKGROUP_NAME) .namespaceName(NAMESPACE_NAME) diff --git a/aws-redshiftserverless-workgroup/src/test/java/software/amazon/redshiftserverless/workgroup/CreateHandlerTest.java b/aws-redshiftserverless-workgroup/src/test/java/software/amazon/redshiftserverless/workgroup/CreateHandlerTest.java index da59295..98cb24b 100644 --- a/aws-redshiftserverless-workgroup/src/test/java/software/amazon/redshiftserverless/workgroup/CreateHandlerTest.java +++ b/aws-redshiftserverless-workgroup/src/test/java/software/amazon/redshiftserverless/workgroup/CreateHandlerTest.java @@ -10,6 +10,8 @@ import software.amazon.awssdk.services.redshiftserverless.model.CreateWorkgroupRequest; import software.amazon.awssdk.services.redshiftserverless.model.GetNamespaceRequest; import software.amazon.awssdk.services.redshiftserverless.model.GetWorkgroupRequest; +import software.amazon.awssdk.services.redshiftserverless.model.ListTagsForResourceRequest; +import software.amazon.awssdk.services.redshiftserverless.model.ListTagsForResourceResponse; import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy; import software.amazon.cloudformation.proxy.OperationStatus; import software.amazon.cloudformation.proxy.ProgressEvent; @@ -62,6 +64,7 @@ public void handleRequest_SimpleSuccess() { when(proxyClient.client().createWorkgroup(any(CreateWorkgroupRequest.class))).thenReturn(createResponseSdk()); when(proxyClient.client().getWorkgroup(any(GetWorkgroupRequest.class))).thenReturn(getReadResponseSdk()); + when(proxyClient.client().listTagsForResource(any(ListTagsForResourceRequest.class))).thenReturn(ListTagsForResourceResponse.builder().build()); when(proxyClient.client().getNamespace(any(GetNamespaceRequest.class))).thenReturn(getNamespaceResponseSdk()); final ProgressEvent response = handler.handleRequest(proxy, request, new CallbackContext(), proxyClient, logger); diff --git a/aws-redshiftserverless-workgroup/src/test/java/software/amazon/redshiftserverless/workgroup/DeleteHandlerTest.java b/aws-redshiftserverless-workgroup/src/test/java/software/amazon/redshiftserverless/workgroup/DeleteHandlerTest.java index d42d4bf..76db39b 100644 --- a/aws-redshiftserverless-workgroup/src/test/java/software/amazon/redshiftserverless/workgroup/DeleteHandlerTest.java +++ b/aws-redshiftserverless-workgroup/src/test/java/software/amazon/redshiftserverless/workgroup/DeleteHandlerTest.java @@ -70,21 +70,11 @@ public void handleRequest_SimpleSuccess() { final ProgressEvent response = handler.handleRequest(proxy, request, new CallbackContext(), proxyClient, logger); assertThat(response).isNotNull(); - assertThat(response.getStatus()).isEqualTo(OperationStatus.IN_PROGRESS); - assertThat(response.getCallbackDelaySeconds()).isEqualTo(300); - assertThat(response.getResourceModel()).isNotNull(); + assertThat(response.getStatus()).isEqualTo(OperationStatus.SUCCESS); + assertThat(response.getCallbackDelaySeconds()).isEqualTo(0); + assertThat(response.getResourceModel()).isNull(); + assertThat(response.getResourceModels()).isNull(); assertThat(response.getMessage()).isNull(); assertThat(response.getErrorCode()).isNull(); - - final ProgressEvent response2 = - handler.handleRequest(proxy, request,response.getCallbackContext(), proxyClient, logger); - - assertThat(response2).isNotNull(); - assertThat(response2.getStatus()).isEqualTo(OperationStatus.SUCCESS); - assertThat(response2.getCallbackDelaySeconds()).isEqualTo(0); - assertThat(response2.getResourceModel()).isNull(); - assertThat(response2.getResourceModels()).isNull(); - assertThat(response2.getMessage()).isNull(); - assertThat(response2.getErrorCode()).isNull(); } } diff --git a/aws-redshiftserverless-workgroup/src/test/java/software/amazon/redshiftserverless/workgroup/ReadHandlerTest.java b/aws-redshiftserverless-workgroup/src/test/java/software/amazon/redshiftserverless/workgroup/ReadHandlerTest.java index 703918e..8da2dba 100644 --- a/aws-redshiftserverless-workgroup/src/test/java/software/amazon/redshiftserverless/workgroup/ReadHandlerTest.java +++ b/aws-redshiftserverless-workgroup/src/test/java/software/amazon/redshiftserverless/workgroup/ReadHandlerTest.java @@ -8,6 +8,8 @@ import org.mockito.junit.jupiter.MockitoExtension; import software.amazon.awssdk.services.redshiftserverless.RedshiftServerlessClient; import software.amazon.awssdk.services.redshiftserverless.model.GetWorkgroupRequest; +import software.amazon.awssdk.services.redshiftserverless.model.ListTagsForResourceRequest; +import software.amazon.awssdk.services.redshiftserverless.model.ListTagsForResourceResponse; import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy; import software.amazon.cloudformation.proxy.OperationStatus; import software.amazon.cloudformation.proxy.ProgressEvent; @@ -60,6 +62,7 @@ public void handleRequest_SimpleSuccess() { .build(); when(proxyClient.client().getWorkgroup(any(GetWorkgroupRequest.class))).thenReturn(getReadResponseSdk()); + when(proxyClient.client().listTagsForResource(any(ListTagsForResourceRequest.class))).thenReturn(ListTagsForResourceResponse.builder().build()); final ProgressEvent response = handler.handleRequest(proxy, request, new CallbackContext(), proxyClient, logger);