/*************************************************************************
* (c) Copyright 2016 Hewlett Packard Enterprise Development Company LP
*
* 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/.
************************************************************************/
package com.eucalyptus.cloudformation.resources.standard.actions;
import com.eucalyptus.cloudformation.ValidationErrorException;
import com.eucalyptus.cloudformation.resources.IAMHelper;
import com.eucalyptus.cloudformation.resources.ResourceAction;
import com.eucalyptus.cloudformation.resources.ResourceInfo;
import com.eucalyptus.cloudformation.resources.ResourceProperties;
import com.eucalyptus.cloudformation.resources.standard.info.AWSIAMUserToGroupAdditionResourceInfo;
import com.eucalyptus.cloudformation.resources.standard.info.AWSSQSQueuePolicyResourceInfo;
import com.eucalyptus.cloudformation.resources.standard.propertytypes.AWSIAMUserToGroupAdditionProperties;
import com.eucalyptus.cloudformation.resources.standard.propertytypes.AWSSQSQueuePolicyProperties;
import com.eucalyptus.cloudformation.template.JsonHelper;
import com.eucalyptus.cloudformation.util.MessageHelper;
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.ServiceConfiguration;
import com.eucalyptus.component.Topology;
import com.eucalyptus.simplequeue.Attribute;
import com.eucalyptus.simplequeue.SetQueueAttributesType;
import com.eucalyptus.simplequeue.SimpleQueue;
import com.eucalyptus.util.async.AsyncExceptions;
import com.eucalyptus.util.async.AsyncRequests;
import com.fasterxml.jackson.databind.node.TextNode;
import com.google.common.base.Optional;
import com.google.common.base.Strings;
import com.google.common.collect.Sets;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.Objects;
import java.util.Set;
/**
* Created by ethomas on 2/3/14.
*/
public class AWSSQSQueuePolicyResourceAction extends StepBasedResourceAction {
private AWSSQSQueuePolicyProperties properties = new AWSSQSQueuePolicyProperties();
private AWSSQSQueuePolicyResourceInfo info = new AWSSQSQueuePolicyResourceInfo();
public AWSSQSQueuePolicyResourceAction() {
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;
AWSSQSQueuePolicyResourceAction otherAction = (AWSSQSQueuePolicyResourceAction) resourceAction;
if (!Objects.equals(properties.getPolicyDocument(), properties.getPolicyDocument())) {
updateType = UpdateType.max(updateType, UpdateType.NO_INTERRUPTION);
}
if (!Objects.equals(properties.getQueues(), otherAction.properties.getQueues())) {
updateType = UpdateType.max(updateType, UpdateType.NO_INTERRUPTION);
}
return updateType;
}
private enum CreateSteps implements Step {
ADD_POLICY_TO_QUEUES {
@Override
public ResourceAction perform(ResourceAction resourceAction) throws Exception {
AWSSQSQueuePolicyResourceAction action = (AWSSQSQueuePolicyResourceAction) resourceAction;
ServiceConfiguration configuration = Topology.lookup(SimpleQueue.class);
if (action.properties.getQueues() != null) {
for (String queueUrl : action.properties.getQueues()) {
SetQueueAttributesType setQueueAttributesType = MessageHelper.createMessage(SetQueueAttributesType.class, action.info.getEffectiveUserId());
setQueueAttributesType.setQueueUrl(queueUrl);
Attribute attribute = new Attribute();
attribute.setName("Policy");
attribute.setValue(action.properties.getPolicyDocument().toString());
setQueueAttributesType.getAttribute().add(attribute);
AsyncRequests.sendSync(configuration, setQueueAttributesType);
}
}
action.info.setPhysicalResourceId(action.getDefaultPhysicalResourceId());
action.info.setCreatedEnoughToDelete(true);
action.info.setReferenceValueJson(JsonHelper.getStringFromJsonNode(new TextNode(action.info.getPhysicalResourceId())));
return action;
}
};
@Nullable
@Override
public Integer getTimeout() {
return null;
}
}
private enum DeleteSteps implements Step {
REMOVE_POLICY_FROM_QUEUES {
@Override
public ResourceAction perform(ResourceAction resourceAction) throws Exception {
AWSSQSQueuePolicyResourceAction action = (AWSSQSQueuePolicyResourceAction) resourceAction;
ServiceConfiguration configuration = Topology.lookup(SimpleQueue.class);
if (action.properties.getQueues() != null) {
for (String queueUrl : action.properties.getQueues()) {
removePolicyFromQueue(action, configuration, queueUrl);
}
}
return action;
}
};
@Nullable
@Override
public Integer getTimeout() {
return null;
}
}
private static void removePolicyFromQueue(AWSSQSQueuePolicyResourceAction action, ServiceConfiguration configuration, String queueUrl) throws Exception {
SetQueueAttributesType setQueueAttributesType = MessageHelper.createMessage(SetQueueAttributesType.class, action.info.getEffectiveUserId());
setQueueAttributesType.setQueueUrl(queueUrl);
Attribute attribute = new Attribute();
attribute.setName("Policy");
attribute.setValue("");
setQueueAttributesType.getAttribute().add(attribute);
try {
AsyncRequests.sendSync(configuration, setQueueAttributesType);
} catch (final Exception e) {
final Optional<AsyncExceptions.AsyncWebServiceError> error = AsyncExceptions.asWebServiceError(e);
if (error.isPresent()) switch (Strings.nullToEmpty(error.get().getCode())) {
case "QueueDoesNotExist":
break;
default:
throw e;
}
else {
throw e;
}
}
}
@Override
public ResourceProperties getResourceProperties() {
return properties;
}
@Override
public void setResourceProperties(ResourceProperties resourceProperties) {
properties = (AWSSQSQueuePolicyProperties) resourceProperties;
}
@Override
public ResourceInfo getResourceInfo() {
return info;
}
@Override
public void setResourceInfo(ResourceInfo resourceInfo) {
info = (AWSSQSQueuePolicyResourceInfo) resourceInfo;
}
private enum UpdateNoInterruptionSteps implements UpdateStep {
UPDATE_POLICY_IN_QUEUES {
@Override
public ResourceAction perform(ResourceAction oldResourceAction, ResourceAction newResourceAction) throws Exception {
AWSSQSQueuePolicyResourceAction oldAction = (AWSSQSQueuePolicyResourceAction) oldResourceAction;
AWSSQSQueuePolicyResourceAction newAction = (AWSSQSQueuePolicyResourceAction) newResourceAction;
ServiceConfiguration configuration = Topology.lookup(SimpleQueue.class);
Set<String> newQueueUrls = Sets.newHashSet();
Set<String> oldQueueUrls = Sets.newHashSet();
if (oldAction.properties.getQueues() != null) {
oldQueueUrls.addAll(oldAction.properties.getQueues());
}
if (newAction.properties.getQueues() != null) {
newQueueUrls.addAll(newAction.properties.getQueues());
oldQueueUrls.removeAll(newAction.properties.getQueues());
}
for (String queueUrl : newQueueUrls) {
SetQueueAttributesType setQueueAttributesType = MessageHelper.createMessage(SetQueueAttributesType.class, newAction.info.getEffectiveUserId());
setQueueAttributesType.setQueueUrl(queueUrl);
Attribute attribute = new Attribute();
attribute.setName("Policy");
attribute.setValue(newAction.properties.getPolicyDocument().toString());
setQueueAttributesType.getAttribute().add(attribute);
AsyncRequests.sendSync(configuration, setQueueAttributesType);
}
for (String queueUrl : oldQueueUrls) {
removePolicyFromQueue(newAction, configuration, queueUrl);
}
return newAction;
}
};
@Nullable
@Override
public Integer getTimeout() {
return null;
}
}
}