/************************************************************************* * Copyright 2009-2015 Eucalyptus Systems, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see http://www.gnu.org/licenses/. * * Please contact Eucalyptus Systems, Inc., 6755 Hollister Ave., Goleta * CA 93117, USA or visit http://www.eucalyptus.com/licenses/ if you need * additional information or have any questions. ************************************************************************/ package com.eucalyptus.cloudformation.resources.standard.actions; import com.amazonaws.services.s3.model.AmazonS3Exception; import com.amazonaws.services.s3.model.BucketCrossOriginConfiguration; import com.amazonaws.services.s3.model.BucketLifecycleConfiguration; import com.amazonaws.services.s3.model.BucketLoggingConfiguration; import com.amazonaws.services.s3.model.BucketNotificationConfiguration; import com.amazonaws.services.s3.model.BucketTaggingConfiguration; import com.amazonaws.services.s3.model.BucketVersioningConfiguration; import com.amazonaws.services.s3.model.BucketWebsiteConfiguration; import com.amazonaws.services.s3.model.CORSRule; import com.amazonaws.services.s3.model.CannedAccessControlList; import com.amazonaws.services.s3.model.RedirectRule; import com.amazonaws.services.s3.model.RoutingRule; import com.amazonaws.services.s3.model.RoutingRuleCondition; import com.amazonaws.services.s3.model.SetBucketLoggingConfigurationRequest; import com.amazonaws.services.s3.model.SetBucketVersioningConfigurationRequest; import com.amazonaws.services.s3.model.StorageClass; import com.amazonaws.services.s3.model.TagSet; import com.eucalyptus.auth.Accounts; import com.eucalyptus.auth.AuthException; import com.eucalyptus.auth.login.AuthenticationException; import com.eucalyptus.auth.principal.AccountFullName; import com.eucalyptus.auth.principal.User; import com.eucalyptus.auth.tokens.SecurityTokenAWSCredentialsProvider; import com.eucalyptus.cloudformation.CloudFormationException; import com.eucalyptus.cloudformation.InternalFailureException; import com.eucalyptus.cloudformation.ValidationErrorException; import com.eucalyptus.cloudformation.resources.ResourceAction; import com.eucalyptus.cloudformation.resources.ResourceInfo; import com.eucalyptus.cloudformation.resources.ResourceProperties; import com.eucalyptus.cloudformation.resources.standard.TagHelper; import com.eucalyptus.cloudformation.resources.standard.info.AWSS3BucketResourceInfo; import com.eucalyptus.cloudformation.resources.standard.propertytypes.AWSS3BucketProperties; import com.eucalyptus.cloudformation.resources.standard.propertytypes.CloudFormationResourceTag; import com.eucalyptus.cloudformation.resources.standard.propertytypes.S3CorsConfiguration; import com.eucalyptus.cloudformation.resources.standard.propertytypes.S3CorsConfigurationRule; import com.eucalyptus.cloudformation.resources.standard.propertytypes.S3LifecycleConfiguration; import com.eucalyptus.cloudformation.resources.standard.propertytypes.S3LifecycleRule; import com.eucalyptus.cloudformation.resources.standard.propertytypes.S3LoggingConfiguration; import com.eucalyptus.cloudformation.resources.standard.propertytypes.S3NotificationConfiguration; import com.eucalyptus.cloudformation.resources.standard.propertytypes.S3NotificationTopicConfiguration; import com.eucalyptus.cloudformation.resources.standard.propertytypes.S3VersioningConfiguration; import com.eucalyptus.cloudformation.resources.standard.propertytypes.S3WebsiteConfiguration; import com.eucalyptus.cloudformation.resources.standard.propertytypes.S3WebsiteConfigurationRoutingRule; import com.eucalyptus.cloudformation.template.JsonHelper; import com.eucalyptus.cloudformation.workflow.steps.Step; import com.eucalyptus.cloudformation.workflow.steps.StepBasedResourceAction; import com.eucalyptus.cloudformation.workflow.steps.UpdateStep; import com.eucalyptus.cloudformation.workflow.updateinfo.UpdateType; import com.eucalyptus.component.ServiceUris; import com.eucalyptus.crypto.util.Timestamps; import com.eucalyptus.objectstorage.ObjectStorage; import com.eucalyptus.objectstorage.client.EucaS3Client; import com.eucalyptus.objectstorage.client.EucaS3ClientFactory; import com.fasterxml.jackson.databind.node.TextNode; import com.google.common.collect.Lists; import javax.annotation.Nullable; import java.net.URI; import java.util.Collection; import java.util.List; import java.util.Objects; /** * Created by ethomas on 2/3/14. */ public class AWSS3BucketResourceAction extends StepBasedResourceAction { private AWSS3BucketProperties properties = new AWSS3BucketProperties(); private AWSS3BucketResourceInfo info = new AWSS3BucketResourceInfo(); public AWSS3BucketResourceAction() { super(fromEnum(CreateSteps.class), fromEnum(DeleteSteps.class), fromUpdateEnum(UpdateNoInterruptionSteps.class), null); } @Override public UpdateType getUpdateType(ResourceAction resourceAction, boolean stackTagsChanged) { UpdateType updateType = info.supportsTags() && stackTagsChanged ? UpdateType.NO_INTERRUPTION : UpdateType.NONE; AWSS3BucketResourceAction otherAction = (AWSS3BucketResourceAction) resourceAction; if (!Objects.equals(properties.getAccessControl(), otherAction.properties.getAccessControl())) { updateType = UpdateType.max(updateType, UpdateType.NO_INTERRUPTION); } if (!Objects.equals(properties.getBucketName(), otherAction.properties.getBucketName())) { updateType = UpdateType.max(updateType, UpdateType.NEEDS_REPLACEMENT); } if (!Objects.equals(properties.getCorsConfiguration(), otherAction.properties.getCorsConfiguration())) { updateType = UpdateType.max(updateType, UpdateType.NO_INTERRUPTION); } if (!Objects.equals(properties.getLifecycleConfiguration(), otherAction.properties.getLifecycleConfiguration())) { updateType = UpdateType.max(updateType, UpdateType.NO_INTERRUPTION); } if (!Objects.equals(properties.getLoggingConfiguration(), otherAction.properties.getLoggingConfiguration())) { updateType = UpdateType.max(updateType, UpdateType.NO_INTERRUPTION); } if (!Objects.equals(properties.getNotificationConfiguration(), otherAction.properties.getNotificationConfiguration())) { updateType = UpdateType.max(updateType, UpdateType.NO_INTERRUPTION); } if (!Objects.equals(properties.getTags(), otherAction.properties.getTags())) { updateType = UpdateType.max(updateType, UpdateType.NO_INTERRUPTION); } if (!Objects.equals(properties.getVersioningConfiguration(), otherAction.properties.getVersioningConfiguration())) { updateType = UpdateType.max(updateType, UpdateType.NO_INTERRUPTION); } if (!Objects.equals(properties.getWebsiteConfiguration(), otherAction.properties.getWebsiteConfiguration())) { updateType = UpdateType.max(updateType, UpdateType.NO_INTERRUPTION); } if (!Objects.equals(properties.getReplicationConfiguration(), otherAction.properties.getReplicationConfiguration())) { updateType = UpdateType.max(updateType, UpdateType.NO_INTERRUPTION); } return updateType; } private enum CreateSteps implements Step { CREATE_BUCKET { @Override public ResourceAction perform(ResourceAction resourceAction) throws Exception { AWSS3BucketResourceAction action = (AWSS3BucketResourceAction) resourceAction; User user = Accounts.lookupPrincipalByUserId(action.getResourceInfo().getEffectiveUserId()); if ( action.properties.getVersioningConfiguration() != null && action.properties.getLifecycleConfiguration() != null) { throw new ValidationErrorException("Unable to set both lifecycle configuration and versioning configuration for buckets"); } try ( final EucaS3Client s3c = EucaS3ClientFactory.getEucaS3Client( new SecurityTokenAWSCredentialsProvider( user ) ) ) { String bucketName = action.properties.getBucketName() != null ? action.properties.getBucketName() : action.getDefaultPhysicalResourceId(63).toLowerCase(); if (s3c.doesBucketExist(bucketName)) { throw new Exception("Bucket " + bucketName + " exists"); } s3c.createBucket(bucketName); action.info.setPhysicalResourceId(bucketName); action.info.setCreatedEnoughToDelete(true); } return action; } }, ADD_BUCKET_STUFF { @Override public ResourceAction perform(ResourceAction resourceAction) throws Exception { AWSS3BucketResourceAction action = (AWSS3BucketResourceAction) resourceAction; URI serviceURI = ServiceUris.remotePublicify(ObjectStorage.class); User user = Accounts.lookupPrincipalByUserId(action.getResourceInfo().getEffectiveUserId()); String bucketName = action.info.getPhysicalResourceId(); try ( final EucaS3Client s3c = EucaS3ClientFactory.getEucaS3Client( new SecurityTokenAWSCredentialsProvider( user ) ) ) { if ( action.properties.getAccessControl() != null ) { s3c.setBucketAcl( bucketName, CannedAccessControlList.valueOf( action.properties.getAccessControl() ) ); } if ( action.properties.getCorsConfiguration() != null ) { s3c.setBucketCrossOriginConfiguration(bucketName, convertCrossOriginConfiguration(action.properties.getCorsConfiguration())); } if ( action.properties.getLifecycleConfiguration() != null ) { // only allow lifecycle configuration if no versioning exists BucketVersioningConfiguration bucketVersioningConfiguration = s3c.getBucketVersioningConfiguration(bucketName); if (!"Off".equals(bucketVersioningConfiguration.getStatus())) { throw new ValidationErrorException("Unable to set lifecycle configuration on bucket " + bucketName + ", versioning is not Off, it is " + bucketVersioningConfiguration.getStatus()); } s3c.setBucketLifecycleConfiguration(bucketName, convertLifecycleConfiguration(action.properties.getLifecycleConfiguration())); } if ( action.properties.getLoggingConfiguration() != null ) { throw new InternalFailureException("LoggingConfiguration not yet implemented"); // s3c.setBucketLoggingConfiguration(convertLoggingConfiguration(bucketName, action.properties.getLoggingConfiguration())); } if ( action.properties.getNotificationConfiguration() != null ) { throw new InternalFailureException("NotificationConfiguration not yet implemented"); // s3c.setBucketNotificationConfiguration(bucketName, convertNotificationConfiguration(action.properties.getNotificationConfiguration())); } if ( action.properties.getReplicationConfiguration() != null ) { throw new InternalFailureException("ReplicationConfiguration not yet implemented"); // s3c.setBucketReplicationConfiguration(bucketName, convertReplicationConfiguration(action.properties.getReplicationConfiguration())); } setBucketTags(action, user, bucketName, s3c); if ( action.properties.getVersioningConfiguration() != null ) { // only allow versioning if no lifecycle configuration exists BucketLifecycleConfiguration bucketLifecycleConfiguration = s3c.getBucketLifecycleConfiguration(bucketName); if (bucketLifecycleConfiguration.getRules().size() > 0) { throw new ValidationErrorException("Unable to set versioning configuration bucket " + bucketName + ". Lifecycle configuration has been set."); } s3c.setBucketVersioningConfiguration(convertVersioningConfiguration(bucketName, action.properties.getVersioningConfiguration())); } // TODO: website configuration throws an error if called (currently) if ( action.properties.getWebsiteConfiguration() != null ) { throw new InternalFailureException("WebsiteConfiguration not yet implemented"); // s3c.setBucketWebsiteConfiguration(bucketName, convertWebsiteConfiguration(action.properties.getWebsiteConfiguration())); } } String domainName = null; if ((serviceURI.getPath() == null || serviceURI.getPath().replace("/","").isEmpty())) { domainName = bucketName + "." + serviceURI.getHost() + (serviceURI.getPort() != -1 ? ":" + serviceURI.getPort() : ""); } else { domainName = serviceURI.getHost() + (serviceURI.getPort() != -1 ? ":" + serviceURI.getPort() : "") + serviceURI.getPath() + "/" + bucketName; } action.info.setDomainName(JsonHelper.getStringFromJsonNode(new TextNode(domainName))); action.info.setWebsiteURL(JsonHelper.getStringFromJsonNode(new TextNode("http://" + domainName))); action.info.setReferenceValueJson(JsonHelper.getStringFromJsonNode(new TextNode(action.info.getPhysicalResourceId()))); return action; } }; @Nullable @Override public Integer getTimeout() { return null; } } private enum DeleteSteps implements Step { KICK_BUCKET { @Override public ResourceAction perform(ResourceAction resourceAction) throws Exception { AWSS3BucketResourceAction action = (AWSS3BucketResourceAction) resourceAction; if (!Boolean.TRUE.equals(action.info.getCreatedEnoughToDelete())) return action; User user = Accounts.lookupPrincipalByUserId(action.getResourceInfo().getEffectiveUserId()); try ( final EucaS3Client s3c = EucaS3ClientFactory.getEucaS3Client( new SecurityTokenAWSCredentialsProvider( user ) ) ) { s3c.deleteBucket(action.info.getPhysicalResourceId()); } catch (AmazonS3Exception ex) { if ("NoSuchBucket".equalsIgnoreCase(ex.getErrorCode())) { // do nothing. (We check existence this way rather than if -> delete due to possible race conditions } else { throw ex; } } return action; } }; @Nullable @Override public Integer getTimeout() { return null; } } @Override public ResourceProperties getResourceProperties() { return properties; } @Override public void setResourceProperties(ResourceProperties resourceProperties) { properties = (AWSS3BucketProperties) resourceProperties; } @Override public ResourceInfo getResourceInfo() { return info; } @Override public void setResourceInfo(ResourceInfo resourceInfo) { info = (AWSS3BucketResourceInfo) resourceInfo; } private static BucketWebsiteConfiguration convertWebsiteConfiguration(S3WebsiteConfiguration websiteConfiguration) { BucketWebsiteConfiguration bucketWebsiteConfiguration = new BucketWebsiteConfiguration(); bucketWebsiteConfiguration.setErrorDocument(websiteConfiguration.getErrorDocument()); bucketWebsiteConfiguration.setIndexDocumentSuffix(websiteConfiguration.getIndexDocument()); if (websiteConfiguration.getRedirectAllRequestsTo() != null) { RedirectRule redirectAllRequestsTo = new RedirectRule(); redirectAllRequestsTo.setHostName(websiteConfiguration.getRedirectAllRequestsTo().getHostName()); redirectAllRequestsTo.setProtocol(websiteConfiguration.getRedirectAllRequestsTo().getProtocol()); bucketWebsiteConfiguration.setRedirectAllRequestsTo(redirectAllRequestsTo); } if (websiteConfiguration.getRoutingRules() != null) { List<RoutingRule> routingRules = Lists.newArrayList(); for (S3WebsiteConfigurationRoutingRule s3WebsiteConfigurationRoutingRule: websiteConfiguration.getRoutingRules()) { RoutingRule routingRule = new RoutingRule(); if (s3WebsiteConfigurationRoutingRule.getRoutingRuleCondition() != null) { RoutingRuleCondition condition = new RoutingRuleCondition(); condition.setHttpErrorCodeReturnedEquals(s3WebsiteConfigurationRoutingRule.getRoutingRuleCondition().getHttpErrorCodeReturnedEquals()); condition.setKeyPrefixEquals(s3WebsiteConfigurationRoutingRule.getRoutingRuleCondition().getKeyPrefixEquals()); routingRule.setCondition(condition); } if (s3WebsiteConfigurationRoutingRule.getRedirectRule() != null) { RedirectRule redirect = new RedirectRule(); redirect.setReplaceKeyWith(s3WebsiteConfigurationRoutingRule.getRedirectRule().getReplaceKeyWith()); redirect.setReplaceKeyPrefixWith(s3WebsiteConfigurationRoutingRule.getRedirectRule().getReplaceKeyPrefixWith()); redirect.setProtocol(s3WebsiteConfigurationRoutingRule.getRedirectRule().getProtocol()); redirect.setHttpRedirectCode(s3WebsiteConfigurationRoutingRule.getRedirectRule().getHttpRedirectCode()); redirect.setHostName(s3WebsiteConfigurationRoutingRule.getRedirectRule().getHostName()); routingRule.setRedirect(redirect); } routingRules.add(routingRule); } bucketWebsiteConfiguration.setRoutingRules(routingRules); } return bucketWebsiteConfiguration; } private static SetBucketVersioningConfigurationRequest convertVersioningConfiguration(String bucketName, S3VersioningConfiguration versioningConfiguration) { BucketVersioningConfiguration bucketVersioningConfiguration = new BucketVersioningConfiguration(versioningConfiguration.getStatus()); return new SetBucketVersioningConfigurationRequest(bucketName, bucketVersioningConfiguration); } private static BucketTaggingConfiguration convertTags(List<CloudFormationResourceTag> tags) { BucketTaggingConfiguration bucketTaggingConfiguration = new BucketTaggingConfiguration(); // In theory BucketTaggingConfiguration Collection<TagSet> tagSets = Lists.newArrayList(); TagSet tagSet = new TagSet(); for (CloudFormationResourceTag cloudformationResourceTag: tags) { tagSet.setTag(cloudformationResourceTag.getKey(), cloudformationResourceTag.getValue()); } tagSets.add(tagSet); bucketTaggingConfiguration.setTagSets(tagSets); return bucketTaggingConfiguration; } private static BucketNotificationConfiguration convertNotificationConfiguration(S3NotificationConfiguration notificationConfiguration) { BucketNotificationConfiguration bucketNotificationConfiguration = new BucketNotificationConfiguration(); if (notificationConfiguration.getTopicConfigurations() != null) { Collection<BucketNotificationConfiguration.TopicConfiguration> topicConfigurations = Lists.newArrayList(); for (S3NotificationTopicConfiguration s3NotificationTopicConfiguration : notificationConfiguration.getTopicConfigurations()) { topicConfigurations.add(new BucketNotificationConfiguration.TopicConfiguration(s3NotificationTopicConfiguration.getTopic(), s3NotificationTopicConfiguration.getEvent())); } bucketNotificationConfiguration.setTopicConfigurations(topicConfigurations); } return bucketNotificationConfiguration; } private static SetBucketLoggingConfigurationRequest convertLoggingConfiguration(String bucketName, S3LoggingConfiguration loggingConfiguration) { BucketLoggingConfiguration bucketLoggingConfiguration = new BucketLoggingConfiguration(); bucketLoggingConfiguration.setDestinationBucketName(loggingConfiguration.getDestinationBucketName()); bucketLoggingConfiguration.setLogFilePrefix(loggingConfiguration.getLogFilePrefix()); return new SetBucketLoggingConfigurationRequest(bucketName, bucketLoggingConfiguration); } private static BucketLifecycleConfiguration convertLifecycleConfiguration(S3LifecycleConfiguration lifecycleConfiguration) throws AuthenticationException { BucketLifecycleConfiguration bucketLifecycleConfiguration = new BucketLifecycleConfiguration(); if (lifecycleConfiguration.getRules() != null) { List<BucketLifecycleConfiguration.Rule> rules = Lists.newArrayList(); for (S3LifecycleRule s3LifecycleRule : lifecycleConfiguration.getRules()) { BucketLifecycleConfiguration.Rule rule = new BucketLifecycleConfiguration.Rule(); rule.setId(s3LifecycleRule.getId()); if (s3LifecycleRule.getExpirationDate() != null) { rule.setExpirationDate(Timestamps.parseIso8601Timestamp(s3LifecycleRule.getExpirationDate())); } if (s3LifecycleRule.getExpirationInDays() != null) { rule.setExpirationInDays(s3LifecycleRule.getExpirationInDays()); } rule.setPrefix(s3LifecycleRule.getPrefix()); rule.setStatus(s3LifecycleRule.getStatus()); if (s3LifecycleRule.getTransition() != null) { BucketLifecycleConfiguration.Transition transition = new BucketLifecycleConfiguration.Transition(); if (s3LifecycleRule.getTransition().getStorageClass() != null) { transition.setStorageClass(StorageClass.valueOf(s3LifecycleRule.getTransition().getStorageClass())); } if (s3LifecycleRule.getExpirationDate() != null) { transition.setDate(Timestamps.parseIso8601Timestamp(s3LifecycleRule.getExpirationDate())); } if (s3LifecycleRule.getExpirationInDays() != null) { transition.setDays(s3LifecycleRule.getExpirationInDays()); } rule.setTransition(transition); } rules.add(rule); } bucketLifecycleConfiguration.setRules(rules); } return bucketLifecycleConfiguration; } private static BucketCrossOriginConfiguration convertCrossOriginConfiguration(S3CorsConfiguration corsConfiguration) { BucketCrossOriginConfiguration bucketCrossOriginConfiguration = new BucketCrossOriginConfiguration(); if (corsConfiguration.getCorsRules() != null) { List<CORSRule> rules = Lists.newArrayList(); for (S3CorsConfigurationRule s3CorsConfigurationRule: corsConfiguration.getCorsRules()) { CORSRule rule = new CORSRule(); rule.setAllowedHeaders(s3CorsConfigurationRule.getAllowedHeaders()); if (s3CorsConfigurationRule.getAllowedMethods() != null) { List<CORSRule.AllowedMethods> allowedMethods = Lists.newArrayList(); for (String allowedMethodStr: s3CorsConfigurationRule.getAllowedMethods()) { allowedMethods.add(CORSRule.AllowedMethods.valueOf(allowedMethodStr)); } rule.setAllowedMethods(allowedMethods); } rule.setAllowedOrigins(s3CorsConfigurationRule.getAllowedOrigins()); rule.setExposedHeaders(s3CorsConfigurationRule.getExposedHeaders()); rule.setId(s3CorsConfigurationRule.getId()); rule.setMaxAgeSeconds(s3CorsConfigurationRule.getMaxAge()); rules.add(rule); } bucketCrossOriginConfiguration.setRules(rules); } return bucketCrossOriginConfiguration; } private enum UpdateNoInterruptionSteps implements UpdateStep { UPDATE_BUCKET_STUFF { @Override public ResourceAction perform(ResourceAction oldResourceAction, ResourceAction newResourceAction) throws Exception { AWSS3BucketResourceAction oldAction = (AWSS3BucketResourceAction) oldResourceAction; AWSS3BucketResourceAction newAction = (AWSS3BucketResourceAction) newResourceAction; if ( newAction.properties.getVersioningConfiguration() != null && newAction.properties.getLifecycleConfiguration() != null) { throw new ValidationErrorException("Unable to set both lifecycle configuration and versioning configuration for buckets"); } URI serviceURI = ServiceUris.remotePublicify(ObjectStorage.class); User user = Accounts.lookupPrincipalByUserId(oldAction.getResourceInfo().getEffectiveUserId()); String bucketName = oldAction.info.getPhysicalResourceId(); try (final EucaS3Client s3c = EucaS3ClientFactory.getEucaS3Client(new SecurityTokenAWSCredentialsProvider(user))) { if (newAction.properties.getAccessControl() != null) { s3c.setBucketAcl(bucketName, CannedAccessControlList.valueOf(newAction.properties.getAccessControl())); } else { s3c.setBucketAcl(bucketName, CannedAccessControlList.BucketOwnerFullControl); } if (newAction.properties.getCorsConfiguration() != null) { s3c.setBucketCrossOriginConfiguration(bucketName, convertCrossOriginConfiguration(newAction.properties.getCorsConfiguration())); } else { s3c.deleteBucketCrossOriginConfiguration(bucketName); } if (newAction.properties.getLifecycleConfiguration() != null) { // only set if versioning off BucketVersioningConfiguration bucketVersioningConfiguration = s3c.getBucketVersioningConfiguration(bucketName); if (!"Off".equals(bucketVersioningConfiguration.getStatus())) { throw new ValidationErrorException("Unable to set lifecycle configuration on bucket " + bucketName + ", versioning is not Off, it is " + bucketVersioningConfiguration.getStatus()); } s3c.setBucketLifecycleConfiguration(bucketName, convertLifecycleConfiguration(newAction.properties.getLifecycleConfiguration())); } else { s3c.deleteBucketLifecycleConfiguration(bucketName); } if (newAction.properties.getLoggingConfiguration() != null) { throw new InternalFailureException("LoggingConfiguration not yet implemented"); // s3c.setBucketLoggingConfiguration(convertLoggingConfiguration(bucketName, newAction.properties.getLoggingConfiguration())); // } else { // s3c.setBucketLoggingConfiguration(new SetBucketLoggingConfigurationRequest(bucketName, new BucketLoggingConfiguration())); } if (newAction.properties.getNotificationConfiguration() != null) { throw new InternalFailureException("NotificationConfiguration not yet implemented"); // s3c.setBucketNotificationConfiguration(bucketName, convertNotificationConfiguration(newAction.properties.getNotificationConfiguration())); // } else { // s3c.setBucketNotificationConfiguration(bucketName, new BucketNotificationConfiguration()); } if (newAction.properties.getReplicationConfiguration() != null) { throw new InternalFailureException("ReplicationConfiguration not yet implemented"); // s3c.setBucketReplicationConfiguration(bucketName, convertReplicationConfiguration(newAction.properties.getReplicationConfiguration())); // } else { // s3c.setBucketReplicationConfiguration(bucketName, new BucketReplicationConfiguration()); } setBucketTags(newAction, user, bucketName, s3c); if (newAction.properties.getVersioningConfiguration() != null) { BucketLifecycleConfiguration bucketLifecycleConfiguration = s3c.getBucketLifecycleConfiguration(bucketName); if (bucketLifecycleConfiguration.getRules().size() > 0) { throw new ValidationErrorException("Unable to set versioning configuration bucket " + bucketName + ". Lifecycle configuration has been set."); } s3c.setBucketVersioningConfiguration(convertVersioningConfiguration(bucketName, newAction.properties.getVersioningConfiguration())); } else { BucketVersioningConfiguration bucketVersioningConfiguration = s3c.getBucketVersioningConfiguration(bucketName); if (bucketVersioningConfiguration == null) { throw new InternalFailureException("Unable to get bucketVersioningConfiguration for " + bucketName + " , does bucket exist?"); } // Once a bucket is versioned, Off is never a valid status again, so if the current value is not "Off", and there is no configuration option, // use suspended instead. if (!bucketVersioningConfiguration.getStatus().equals("Off")) { s3c.setBucketVersioningConfiguration(new SetBucketVersioningConfigurationRequest(bucketName, new BucketVersioningConfiguration("Suspended"))); } } // TODO: website configuration throws an error if called (currently) if (newAction.properties.getWebsiteConfiguration() != null) { throw new InternalFailureException("WebsiteConfiguration not yet implemented"); // s3c.setBucketWebsiteConfiguration(bucketName, convertWebsiteConfiguration(newAction.properties.getWebsiteConfiguration())); // } else { // s3c.deleteBucketWebsiteConfiguration(bucketName); } String domainName = null; if ((serviceURI.getPath() == null || serviceURI.getPath().replace("/", "").isEmpty())) { domainName = bucketName + "." + serviceURI.getHost() + (serviceURI.getPort() != -1 ? ":" + serviceURI.getPort() : ""); } else { domainName = serviceURI.getHost() + (serviceURI.getPort() != -1 ? ":" + serviceURI.getPort() : "") + serviceURI.getPath() + "/" + bucketName; } newAction.info.setDomainName(JsonHelper.getStringFromJsonNode(new TextNode(domainName))); newAction.info.setWebsiteURL(JsonHelper.getStringFromJsonNode(new TextNode("http://" + domainName))); newAction.info.setReferenceValueJson(JsonHelper.getStringFromJsonNode(new TextNode(newAction.info.getPhysicalResourceId()))); return newAction; } } @Nullable @Override public Integer getTimeout() { return null; } } } private static void setBucketTags(AWSS3BucketResourceAction newAction, User user, String bucketName, EucaS3Client s3c) throws CloudFormationException, AuthException { // Dealing with tags has to be done differently than with other resources. // 1) there is only the s3c.setBucketTaggingConfiguration(), tags can not be "added", they can only be replaced wholesale. // This means that the user must have the ability to add tags even to use system tags. // 2) There is no (current) verification of aws: or euca: prefixes // Given the above, and given that we have a few system tags that must be added, we will add tags all at once either as the admin // (if no non-system tags exist) or as the user (if non system-tags exist). If the user version fails due to IAM, it would have done so anyway. List<CloudFormationResourceTag> systemTags = TagHelper.getCloudFormationResourceSystemTags(newAction.info, newAction.getStackEntity()); List<CloudFormationResourceTag> nonSystemTags = TagHelper.getCloudFormationResourceStackTags( newAction.getStackEntity() ); if ( newAction.properties.getTags() != null && !newAction.properties.getTags().isEmpty() ) { TagHelper.checkReservedCloudFormationResourceTemplateTags( newAction.properties.getTags() ); nonSystemTags.addAll(newAction.properties.getTags()); } if (!nonSystemTags.isEmpty()) { // add as user List<CloudFormationResourceTag> allTags = Lists.newArrayList(); allTags.addAll(systemTags); allTags.addAll(nonSystemTags); s3c.setBucketTaggingConfiguration( bucketName, convertTags( allTags ) ); } else { // add as admin try ( EucaS3Client s3cAdmin = EucaS3ClientFactory.getEucaS3Client(new SecurityTokenAWSCredentialsProvider(AccountFullName.getInstance(user.getAccountNumber()))) ) { s3cAdmin.setBucketTaggingConfiguration( bucketName, AWSS3BucketResourceAction.convertTags( systemTags )); } } } }