/*************************************************************************
* (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.resources.ResourceAction;
import com.eucalyptus.cloudformation.resources.ResourceInfo;
import com.eucalyptus.cloudformation.resources.ResourceProperties;
import com.eucalyptus.cloudformation.resources.standard.info.AWSSQSQueueResourceInfo;
import com.eucalyptus.cloudformation.resources.standard.propertytypes.AWSSQSQueueProperties;
import com.eucalyptus.cloudformation.resources.standard.propertytypes.SQSRedrivePolicy;
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.CreateQueueResponseType;
import com.eucalyptus.simplequeue.CreateQueueType;
import com.eucalyptus.simplequeue.DeleteQueueType;
import com.eucalyptus.simplequeue.GetQueueAttributesResponseType;
import com.eucalyptus.simplequeue.GetQueueAttributesType;
import com.eucalyptus.simplequeue.SetQueueAttributesType;
import com.eucalyptus.simplequeue.SimpleQueue;
import com.eucalyptus.util.async.AsyncRequests;
import com.fasterxml.jackson.databind.node.TextNode;
import com.google.common.collect.Lists;
import javax.annotation.Nullable;
import java.util.Objects;
/**
* Created by ethomas on 2/3/14.
*/
public class AWSSQSQueueResourceAction extends StepBasedResourceAction {
private AWSSQSQueueProperties properties = new AWSSQSQueueProperties();
private AWSSQSQueueResourceInfo info = new AWSSQSQueueResourceInfo();
public AWSSQSQueueResourceAction() {
super(fromEnum(CreateSteps.class), fromEnum(DeleteSteps.class), fromUpdateEnum(UpdateNoInterruptionSteps.class), null);
}
private enum CreateSteps implements Step {
CREATE_QUEUE {
@Override
public ResourceAction perform(ResourceAction resourceAction) throws Exception {
AWSSQSQueueResourceAction action = (AWSSQSQueueResourceAction) resourceAction;
ServiceConfiguration configuration = Topology.lookup(SimpleQueue.class);
if (action.properties.getQueueName() == null) {
action.properties.setQueueName(action.getDefaultPhysicalResourceId());
}
// no need to check already exists, service does that
CreateQueueType createQueueType = MessageHelper.createMessage(CreateQueueType.class, action.info.getEffectiveUserId());
createQueueType.setQueueName(action.properties.getQueueName());
if (action.properties.getDelaySeconds() != null) {
Attribute attribute = new Attribute();
attribute.setName("DelaySeconds");
attribute.setValue("" + action.properties.getDelaySeconds());
createQueueType.getAttribute().add(attribute);
}
if (action.properties.getMaximumMessageSize() != null) {
Attribute attribute = new Attribute();
attribute.setName("MaximumMessageSize");
attribute.setValue("" + action.properties.getMaximumMessageSize());
createQueueType.getAttribute().add(attribute);
}
if (action.properties.getMessageRetentionPeriod() != null) {
Attribute attribute = new Attribute();
attribute.setName("MessageRetentionPeriod");
attribute.setValue("" + action.properties.getMessageRetentionPeriod());
createQueueType.getAttribute().add(attribute);
}
if (action.properties.getReceiveMessageWaitTimeSeconds() != null) {
Attribute attribute = new Attribute();
attribute.setName("ReceiveMessageWaitTimeSeconds");
attribute.setValue("" + action.properties.getReceiveMessageWaitTimeSeconds());
createQueueType.getAttribute().add(attribute);
}
if (action.properties.getRedrivePolicy() != null) {
Attribute attribute = new Attribute();
attribute.setName("RedrivePolicy");
attribute.setValue(convertRedrivePolicyToJsonString(action.properties.getRedrivePolicy()));
createQueueType.getAttribute().add(attribute);
}
if (action.properties.getVisibilityTimeout() != null) {
Attribute attribute = new Attribute();
attribute.setName("VisibilityTimeout");
attribute.setValue("" + action.properties.getVisibilityTimeout());
createQueueType.getAttribute().add(attribute);
}
CreateQueueResponseType createQueueResponseType = AsyncRequests.sendSync(configuration, createQueueType);
action.info.setPhysicalResourceId(createQueueResponseType.getCreateQueueResult().getQueueUrl());
action.info.setCreatedEnoughToDelete(true);
action.info.setReferenceValueJson(JsonHelper.getStringFromJsonNode(new TextNode(action.info.getPhysicalResourceId())));
action.info.setQueueName(JsonHelper.getStringFromJsonNode(new TextNode(action.properties.getQueueName())));
GetQueueAttributesType getQueueAttributesType = MessageHelper.createMessage(GetQueueAttributesType.class, action.info.getEffectiveUserId());
getQueueAttributesType.setAttributeName(Lists.newArrayList("QueueArn"));
getQueueAttributesType.setQueueUrl(action.info.getPhysicalResourceId());
GetQueueAttributesResponseType getQueueAttributesResponseType = AsyncRequests.sendSync(configuration, getQueueAttributesType);
// should only return one attribute
action.info.setArn(JsonHelper.getStringFromJsonNode(new TextNode(getQueueAttributesResponseType.getGetQueueAttributesResult().getAttribute().get(0).getValue())));
return action;
}
};
@Nullable
@Override
public Integer getTimeout() {
return null;
}
}
private enum DeleteSteps implements Step {
DELETE_QUEUE {
@Override
public ResourceAction perform(ResourceAction resourceAction) throws Exception {
AWSSQSQueueResourceAction action = (AWSSQSQueueResourceAction) resourceAction;
ServiceConfiguration configuration = Topology.lookup(SimpleQueue.class);
if (!Boolean.TRUE.equals(action.info.getCreatedEnoughToDelete())) return action;
DeleteQueueType deleteQueueType = MessageHelper.createMessage(DeleteQueueType.class, action.info.getEffectiveUserId());
deleteQueueType.setQueueUrl(action.info.getPhysicalResourceId());
AsyncRequests.sendSync(configuration, deleteQueueType);
return action;
}
};
@Nullable
@Override
public Integer getTimeout() {
return null;
}
}
private enum UpdateNoInterruptionSteps implements UpdateStep {
UPDATE_QUEUE {
@Override
public ResourceAction perform(ResourceAction oldResourceAction, ResourceAction newResourceAction) throws Exception {
AWSSQSQueueResourceAction oldAction = (AWSSQSQueueResourceAction) oldResourceAction;
AWSSQSQueueResourceAction newAction = (AWSSQSQueueResourceAction) newResourceAction;
// just update the values.
ServiceConfiguration configuration = Topology.lookup(SimpleQueue.class);
SetQueueAttributesType setQueueAttributesType = MessageHelper.createMessage(SetQueueAttributesType.class, newAction.info.getEffectiveUserId());
setQueueAttributesType.setQueueUrl(newAction.info.getPhysicalResourceId());
Attribute attribute = new Attribute();
attribute.setName("DelaySeconds");
attribute.setValue(newAction.properties.getDelaySeconds() == null ? "0" : "" + newAction.properties.getDelaySeconds());
setQueueAttributesType.getAttribute().add(attribute);
attribute = new Attribute();
attribute.setName("MaximumMessageSize");
attribute.setValue(newAction.properties.getMaximumMessageSize() == null ? "262144" : "" + newAction.properties.getMaximumMessageSize());
setQueueAttributesType.getAttribute().add(attribute);
attribute = new Attribute();
attribute.setName("MessageRetentionPeriod");
attribute.setValue(newAction.properties.getMessageRetentionPeriod() == null ? "345600" : "" + newAction.properties.getMessageRetentionPeriod());
setQueueAttributesType.getAttribute().add(attribute);
attribute = new Attribute();
attribute.setName("ReceiveMessageWaitTimeSeconds");
attribute.setValue(newAction.properties.getReceiveMessageWaitTimeSeconds() == null ? "0" : "" + newAction.properties.getReceiveMessageWaitTimeSeconds());
setQueueAttributesType.getAttribute().add(attribute);
attribute = new Attribute();
attribute.setName("RedrivePolicy");
attribute.setValue(newAction.properties.getRedrivePolicy() == null ? "" : convertRedrivePolicyToJsonString(newAction.properties.getRedrivePolicy()));
setQueueAttributesType.getAttribute().add(attribute);
attribute = new Attribute();
attribute.setName("VisibilityTimeout");
attribute.setValue(newAction.properties.getVisibilityTimeout() == null ? "30" : "" + newAction.properties.getVisibilityTimeout());
setQueueAttributesType.getAttribute().add(attribute);
AsyncRequests.sendSync(configuration, setQueueAttributesType);
return newAction;
}
};
@Nullable
@Override
public Integer getTimeout() {
return null;
}
}
@Override
public ResourceProperties getResourceProperties() {
return properties;
}
@Override
public void setResourceProperties(ResourceProperties resourceProperties) {
properties = (AWSSQSQueueProperties) resourceProperties;
}
@Override
public ResourceInfo getResourceInfo() {
return info;
}
@Override
public void setResourceInfo(ResourceInfo resourceInfo) {
info = (AWSSQSQueueResourceInfo) resourceInfo;
}
@Override
public UpdateType getUpdateType(ResourceAction resourceAction, boolean stackTagsChanged) {
UpdateType updateType = info.supportsTags() && stackTagsChanged ? UpdateType.NO_INTERRUPTION : UpdateType.NONE;
AWSSQSQueueResourceAction otherAction = (AWSSQSQueueResourceAction) resourceAction;
if (!Objects.equals(properties.getDelaySeconds(), otherAction.properties.getDelaySeconds())) {
updateType = UpdateType.max(updateType, UpdateType.NO_INTERRUPTION);
}
if (!Objects.equals(properties.getMaximumMessageSize(), otherAction.properties.getMaximumMessageSize())) {
updateType = UpdateType.max(updateType, UpdateType.NO_INTERRUPTION);
}
if (!Objects.equals(properties.getMessageRetentionPeriod(), otherAction.properties.getMessageRetentionPeriod())) {
updateType = UpdateType.max(updateType, UpdateType.NO_INTERRUPTION);
}
if (!Objects.equals(properties.getQueueName(), otherAction.properties.getQueueName())) {
updateType = UpdateType.max(updateType, UpdateType.NEEDS_REPLACEMENT);
}
if (!Objects.equals(properties.getReceiveMessageWaitTimeSeconds(), otherAction.properties.getReceiveMessageWaitTimeSeconds())) {
updateType = UpdateType.max(updateType, UpdateType.NO_INTERRUPTION);
}
if (!Objects.equals(properties.getRedrivePolicy(), otherAction.properties.getRedrivePolicy())) {
updateType = UpdateType.max(updateType, UpdateType.NO_INTERRUPTION);
}
if (!Objects.equals(properties.getVisibilityTimeout(), otherAction.properties.getVisibilityTimeout())) {
updateType = UpdateType.max(updateType, UpdateType.NO_INTERRUPTION);
}
return updateType;
}
private static String convertRedrivePolicyToJsonString(SQSRedrivePolicy sqsRedrivePolicy) {
if (sqsRedrivePolicy == null) return "";
// Note: we could use a json parser, but this is simple
StringBuilder builder = new StringBuilder("{");
String delimiter = "";
if (sqsRedrivePolicy.getDeadLetterTargetArn() != null) {
builder.append("\"deadLetterTargetArn\" : \""+sqsRedrivePolicy.getDeadLetterTargetArn() + "\"");
delimiter = ",";
}
if (sqsRedrivePolicy.getMaxReceiveCount() != null) {
builder.append(delimiter + "\"maxReceiveCount\" : \""+sqsRedrivePolicy.getMaxReceiveCount() + "\"");
}
builder.append("}");
return builder.toString();
}
}