/*************************************************************************
* Copyright 2009-2016 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.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.info.AWSEC2NatGatewayResourceInfo;
import com.eucalyptus.cloudformation.resources.standard.propertytypes.AWSEC2NatGatewayProperties;
import com.eucalyptus.cloudformation.template.JsonHelper;
import com.eucalyptus.cloudformation.util.MessageHelper;
import com.eucalyptus.cloudformation.workflow.RetryAfterConditionCheckFailedException;
import com.eucalyptus.cloudformation.workflow.steps.Step;
import com.eucalyptus.cloudformation.workflow.steps.StepBasedResourceAction;
import com.eucalyptus.cloudformation.workflow.updateinfo.UpdateType;
import com.eucalyptus.component.ServiceConfiguration;
import com.eucalyptus.component.Topology;
import com.eucalyptus.compute.common.Compute;
import com.eucalyptus.compute.common.CreateNatGatewayResponseType;
import com.eucalyptus.compute.common.CreateNatGatewayType;
import com.eucalyptus.compute.common.DeleteNatGatewayType;
import com.eucalyptus.compute.common.DescribeNatGatewaysResponseType;
import com.eucalyptus.compute.common.DescribeNatGatewaysType;
import com.eucalyptus.compute.common.Filter;
import com.eucalyptus.configurable.ConfigurableField;
import com.eucalyptus.util.async.AsyncExceptions;
import com.eucalyptus.util.async.AsyncRequests;
import com.eucalyptus.ws.WebServiceError;
import com.fasterxml.jackson.databind.node.TextNode;
import org.apache.log4j.Logger;
import javax.annotation.Nullable;
import java.util.Objects;
import static com.eucalyptus.util.async.AsyncExceptions.asWebServiceErrorMessage;
/**
* Created by ethomas on 2/3/14.
*/
public class AWSEC2NatGatewayResourceAction extends StepBasedResourceAction {
private static final Logger LOG = Logger.getLogger(AWSEC2NatGatewayResourceAction.class);
private AWSEC2NatGatewayProperties properties = new AWSEC2NatGatewayProperties();
private AWSEC2NatGatewayResourceInfo info = new AWSEC2NatGatewayResourceInfo();
@ConfigurableField(initial = "300", description = "The amount of time (in seconds) to wait for a nat gateway to be available after create)")
public static volatile Integer NAT_GATEWAY_AVAILABLE_MAX_CREATE_RETRY_SECS = 300;
public AWSEC2NatGatewayResourceAction() {
// all updates are replacement
super(fromEnum(CreateSteps.class), fromEnum(DeleteSteps.class), null, null);
}
@Override
public UpdateType getUpdateType(ResourceAction resourceAction, boolean stackTagsChanged) {
UpdateType updateType = info.supportsTags() && stackTagsChanged ? UpdateType.NO_INTERRUPTION : UpdateType.NONE;
AWSEC2NatGatewayResourceAction otherAction = (AWSEC2NatGatewayResourceAction) resourceAction;
if (!Objects.equals(properties.getAllocationId(), otherAction.properties.getAllocationId())) {
updateType = UpdateType.max(updateType, UpdateType.NEEDS_REPLACEMENT);
}
if (!Objects.equals(properties.getSubnetId(), otherAction.properties.getSubnetId())) {
updateType = UpdateType.max(updateType, UpdateType.NEEDS_REPLACEMENT);
}
return updateType;
}
private enum CreateSteps implements Step {
CREATE_NAT_GATEWAY {
@Override
public ResourceAction perform(ResourceAction resourceAction) throws Exception {
AWSEC2NatGatewayResourceAction action = (AWSEC2NatGatewayResourceAction) resourceAction;
ServiceConfiguration configuration = Topology.lookup(Compute.class);
CreateNatGatewayType createNatGatewayType = MessageHelper.createMessage(CreateNatGatewayType.class, action.info.getEffectiveUserId());
createNatGatewayType.setAllocationId(action.properties.getAllocationId());
createNatGatewayType.setSubnetId(action.properties.getSubnetId());
CreateNatGatewayResponseType createNatGatewayResponseType = AsyncRequests.sendSync(configuration, createNatGatewayType);
action.info.setPhysicalResourceId(createNatGatewayResponseType.getNatGateway().getNatGatewayId());
action.info.setCreatedEnoughToDelete(true);
action.info.setReferenceValueJson(JsonHelper.getStringFromJsonNode(new TextNode(action.info.getPhysicalResourceId())));
return action;
}
},
VERIFY_AVAILABLE {
@Override
public ResourceAction perform(ResourceAction resourceAction) throws Exception {
AWSEC2NatGatewayResourceAction action = (AWSEC2NatGatewayResourceAction) resourceAction;
ServiceConfiguration configuration = Topology.lookup(Compute.class);
DescribeNatGatewaysType describeNatGatewaysType = MessageHelper.createMessage(DescribeNatGatewaysType.class, action.info.getEffectiveUserId());
describeNatGatewaysType.getFilterSet( ).add( Filter.filter( "nat-gateway-id", action.info.getPhysicalResourceId( ) ) );
DescribeNatGatewaysResponseType describeNatGatewaysResponseType;
try {
describeNatGatewaysResponseType = AsyncRequests.sendSync( configuration, describeNatGatewaysType);
} catch ( final Exception e ) {
throw new ValidationErrorException("Error describing nat gateway " + action.info.getPhysicalResourceId() + ":" + asWebServiceErrorMessage( e, e.getMessage() ) );
}
if (describeNatGatewaysResponseType.getNatGatewaySet() == null || describeNatGatewaysResponseType.getNatGatewaySet().getItem() == null ||
describeNatGatewaysResponseType.getNatGatewaySet().getItem().size() == 0) {
throw new RetryAfterConditionCheckFailedException("Nat gateway " + action.info.getPhysicalResourceId() + " not yet available");
}
if (!"available".equals(describeNatGatewaysResponseType.getNatGatewaySet().getItem().get(0).getState())) {
throw new RetryAfterConditionCheckFailedException("Nat gateway " + action.info.getPhysicalResourceId() + " not yet available");
}
return action;
}
@Override
public Integer getTimeout() {
return NAT_GATEWAY_AVAILABLE_MAX_CREATE_RETRY_SECS;
}
};
@Nullable
@Override
public Integer getTimeout() {
return null;
}
}
private enum DeleteSteps implements Step {
DELETE_NAT_GATEWAY {
@Override
public ResourceAction perform(ResourceAction resourceAction) throws Exception {
AWSEC2NatGatewayResourceAction action = (AWSEC2NatGatewayResourceAction) resourceAction;
ServiceConfiguration configuration = Topology.lookup(Compute.class);
if (!Boolean.TRUE.equals(action.info.getCreatedEnoughToDelete())) return action;
DeleteNatGatewayType deleteNatGatewayType = MessageHelper.createMessage(DeleteNatGatewayType.class, action.info.getEffectiveUserId());
deleteNatGatewayType.setNatGatewayId(action.info.getPhysicalResourceId());
try {
AsyncRequests.sendSync( configuration, deleteNatGatewayType );
} catch ( final Exception e ) {
if ( !AsyncExceptions.isWebServiceErrorCode( e, "NatGatewayNotFound" ) ) {
throw e;
}
}
return action;
}
};
@Nullable
@Override
public Integer getTimeout() {
return null;
}
}
@Override
public ResourceProperties getResourceProperties() {
return properties;
}
@Override
public void setResourceProperties(ResourceProperties resourceProperties) {
properties = (AWSEC2NatGatewayProperties) resourceProperties;
}
@Override
public ResourceInfo getResourceInfo() {
return info;
}
@Override
public void setResourceInfo(ResourceInfo resourceInfo) {
info = (AWSEC2NatGatewayResourceInfo) resourceInfo;
}
}