package software.amazon.codeartifact.domain; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import com.google.common.collect.Sets; import software.amazon.awssdk.awscore.exception.AwsServiceException; import software.amazon.awssdk.services.codeartifact.CodeartifactClient; import software.amazon.awssdk.services.codeartifact.model.Tag; import software.amazon.awssdk.services.codeartifact.model.TagResourceRequest; import software.amazon.awssdk.services.codeartifact.model.UntagResourceRequest; import software.amazon.awssdk.utils.CollectionUtils; import software.amazon.cloudformation.exceptions.CfnNotUpdatableException; import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy; import software.amazon.cloudformation.proxy.Logger; import software.amazon.cloudformation.proxy.ProgressEvent; import software.amazon.cloudformation.proxy.ProxyClient; import software.amazon.cloudformation.proxy.ResourceHandlerRequest; public class UpdateHandler extends BaseHandlerStd { private Logger logger; protected ProgressEvent<ResourceModel, CallbackContext> handleRequest( final AmazonWebServicesClientProxy proxy, final ResourceHandlerRequest<ResourceModel> request, final CallbackContext callbackContext, final ProxyClient<CodeartifactClient> proxyClient, final Logger logger ) { this.logger = logger; ResourceModel desiredResourceState = request.getDesiredResourceState(); ResourceModel previousResourceState = request.getPreviousResourceState(); if (!Objects.equals(previousResourceState.getDomainName(), desiredResourceState.getDomainName()) || !Objects.equals(previousResourceState.getEncryptionKey(), desiredResourceState.getEncryptionKey()) ) { // cannot update domainName/EncryptionKey because it's CreateOnly throw new CfnNotUpdatableException(ResourceModel.TYPE_NAME, desiredResourceState.getDomainName()); } return ProgressEvent.progress(request.getDesiredResourceState(), callbackContext) .then(progress -> updateDomainPermissionsPolicy(proxy, progress, callbackContext, request, proxyClient, logger)) .then(progress -> updateTags(proxy, proxyClient, progress, desiredResourceState.getDomainName(), request)) .then(progress -> new ReadHandler().handleRequest(proxy, request, callbackContext, proxyClient, logger)); } private ProgressEvent<ResourceModel,CallbackContext> updateTags( final AmazonWebServicesClientProxy proxy, final ProxyClient<CodeartifactClient> proxyClient, final ProgressEvent<ResourceModel, CallbackContext> progress, String domainName, final ResourceHandlerRequest<ResourceModel> request ) { Map<String, String> desiredResourceTags = request.getDesiredResourceTags(); Map<String, String> previousResourceTags = request.getPreviousResourceTags(); final Set<Tag> desiredSdkTags = new HashSet<>(Translator.translateTagsToSdk(desiredResourceTags)); final Set<Tag> previousSdkTags = new HashSet<>(Translator.translateTagsToSdk(previousResourceTags)); final Set<Tag> setTagsToRemove = Sets.difference(previousSdkTags, desiredSdkTags); final Set<Tag> setTagsToAdd = Sets.difference(desiredSdkTags, previousSdkTags); final List<Tag> tagsToRemove = new ArrayList<>(setTagsToRemove); final List<Tag> tagsToAdd = new ArrayList<>(setTagsToAdd); try { // Deletes tags only if tagsToRemove is not empty. if (!CollectionUtils.isNullOrEmpty(tagsToRemove)) { UntagResourceRequest untagRequest = Translator.untagResourceRequest(request, tagsToRemove, domainName); proxy.injectCredentialsAndInvokeV2(untagRequest, proxyClient.client()::untagResource); } } catch (AwsServiceException e) { Translator.throwCfnException(e, Constants.UNTAG_RESOURCE, domainName); } try { // Adds tags only if tagsToAdd is not empty. if (!CollectionUtils.isNullOrEmpty(tagsToAdd)) { TagResourceRequest tagRequest = Translator.tagResourceRequest(request, tagsToAdd, domainName); proxy.injectCredentialsAndInvokeV2(tagRequest, proxyClient.client()::tagResource); } } catch (AwsServiceException e) { Translator.throwCfnException(e, Constants.TAG_RESOURCE, domainName); } return ProgressEvent.progress(progress.getResourceModel(), progress.getCallbackContext()); } protected ProgressEvent<ResourceModel, CallbackContext> updateDomainPermissionsPolicy( final AmazonWebServicesClientProxy proxy, final ProgressEvent<ResourceModel, CallbackContext> progress, final CallbackContext callbackContext, final ResourceHandlerRequest<ResourceModel> request, final ProxyClient<CodeartifactClient> proxyClient, final Logger logger ) { final ResourceModel desiredModel = request.getDesiredResourceState(); if (desiredModel.getPermissionsPolicyDocument() != null) { return putDomainPermissionsPolicy(proxy, progress, callbackContext, request, proxyClient, logger); } return deleteDomainPermissionsPolicy(proxy, progress, callbackContext, request, proxyClient); } private ProgressEvent<ResourceModel, CallbackContext> deleteDomainPermissionsPolicy( AmazonWebServicesClientProxy proxy, ProgressEvent<ResourceModel, CallbackContext> progress, CallbackContext callbackContext, ResourceHandlerRequest<ResourceModel> request, ProxyClient<CodeartifactClient> proxyClient ) { final ResourceModel desiredModel = request.getDesiredResourceState(); final ResourceModel previousModel = request.getPreviousResourceState(); if (desiredModel.getPermissionsPolicyDocument() != null || previousModel.getPermissionsPolicyDocument() == null) { return ProgressEvent.progress(desiredModel, callbackContext); } return proxy.initiate("AWS-CodeArtifact-Domain::Update::DeleteDomainPermissionsPolicy", proxyClient, progress.getResourceModel(), progress.getCallbackContext()) .translateToServiceRequest(Translator::translateDeleteDomainPolicyRequest) .makeServiceCall((awsRequest, client) -> { try { client.injectCredentialsAndInvokeV2(awsRequest, client.client()::deleteDomainPermissionsPolicy); logger.log("Domain permission policy successfully deleted."); } catch (final AwsServiceException e) { String domainName = desiredModel.getDomainName(); Translator.throwCfnException(e, Constants.DELETE_DOMAIN_PERMISSION_POLICY, domainName); } return awsRequest; }) .progress(); } }