/*************************************************************************
* 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.eucalyptus.auth.Accounts;
import com.eucalyptus.cloudformation.ValidationErrorException;
import com.eucalyptus.cloudformation.entity.SignalEntity;
import com.eucalyptus.cloudformation.entity.SignalEntityManager;
import com.eucalyptus.cloudformation.entity.StackEventEntityManager;
import com.eucalyptus.cloudformation.resources.EC2Helper;
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.AWSEC2InstanceResourceInfo;
import com.eucalyptus.cloudformation.resources.standard.propertytypes.AWSEC2InstanceProperties;
import com.eucalyptus.cloudformation.resources.standard.propertytypes.EC2BlockDeviceMapping;
import com.eucalyptus.cloudformation.resources.standard.propertytypes.EC2EBSBlockDevice;
import com.eucalyptus.cloudformation.resources.standard.propertytypes.EC2MountPoint;
import com.eucalyptus.cloudformation.resources.standard.propertytypes.EC2NetworkInterface;
import com.eucalyptus.cloudformation.resources.standard.propertytypes.EC2NetworkInterfacePrivateIPSpecification;
import com.eucalyptus.cloudformation.resources.standard.propertytypes.EC2Tag;
import com.eucalyptus.cloudformation.template.CreationPolicy;
import com.eucalyptus.cloudformation.template.JsonHelper;
import com.eucalyptus.cloudformation.util.MessageHelper;
import com.eucalyptus.cloudformation.workflow.ResourceFailureException;
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.steps.UpdateStep;
import com.eucalyptus.cloudformation.workflow.updateinfo.UpdateType;
import com.eucalyptus.component.ServiceConfiguration;
import com.eucalyptus.component.Topology;
import com.eucalyptus.compute.common.AttachVolumeResponseType;
import com.eucalyptus.compute.common.AttachVolumeType;
import com.eucalyptus.compute.common.AttachedVolume;
import com.eucalyptus.compute.common.AttributeBooleanFlatValueType;
import com.eucalyptus.compute.common.AttributeBooleanValueType;
import com.eucalyptus.compute.common.AttributeValueType;
import com.eucalyptus.compute.common.BlockDeviceMappingItemType;
import com.eucalyptus.compute.common.Compute;
import com.eucalyptus.compute.common.CreateTagsResponseType;
import com.eucalyptus.compute.common.CreateTagsType;
import com.eucalyptus.compute.common.DeleteTagsResponseType;
import com.eucalyptus.compute.common.DeleteTagsType;
import com.eucalyptus.compute.common.DescribeInstanceAttributeResponseType;
import com.eucalyptus.compute.common.DescribeInstanceAttributeType;
import com.eucalyptus.compute.common.DescribeInstancesResponseType;
import com.eucalyptus.compute.common.DescribeInstancesType;
import com.eucalyptus.compute.common.DescribeSecurityGroupsResponseType;
import com.eucalyptus.compute.common.DescribeSecurityGroupsType;
import com.eucalyptus.compute.common.DescribeTagsResponseType;
import com.eucalyptus.compute.common.DescribeTagsType;
import com.eucalyptus.compute.common.DescribeVolumesResponseType;
import com.eucalyptus.compute.common.DescribeVolumesType;
import com.eucalyptus.compute.common.DetachVolumeType;
import com.eucalyptus.compute.common.EbsDeviceMapping;
import com.eucalyptus.compute.common.Filter;
import com.eucalyptus.compute.common.GroupIdSetType;
import com.eucalyptus.compute.common.GroupItemType;
import com.eucalyptus.compute.common.InstanceBlockDeviceMapping;
import com.eucalyptus.compute.common.InstanceBlockDeviceMappingItemType;
import com.eucalyptus.compute.common.InstanceBlockDeviceMappingSetType;
import com.eucalyptus.compute.common.InstanceEbsBlockDeviceType;
import com.eucalyptus.compute.common.InstanceNetworkInterfaceSetItemRequestType;
import com.eucalyptus.compute.common.InstanceNetworkInterfaceSetRequestType;
import com.eucalyptus.compute.common.ModifyInstanceAttributeType;
import com.eucalyptus.compute.common.MonitorInstancesType;
import com.eucalyptus.compute.common.PrivateIpAddressesSetItemRequestType;
import com.eucalyptus.compute.common.PrivateIpAddressesSetRequestType;
import com.eucalyptus.compute.common.ResetInstanceAttributeType;
import com.eucalyptus.compute.common.RunInstancesResponseType;
import com.eucalyptus.compute.common.RunInstancesType;
import com.eucalyptus.compute.common.RunningInstancesItemType;
import com.eucalyptus.compute.common.SecurityGroupIdSetItemType;
import com.eucalyptus.compute.common.SecurityGroupIdSetType;
import com.eucalyptus.compute.common.StartInstancesType;
import com.eucalyptus.compute.common.StopInstancesType;
import com.eucalyptus.compute.common.TagInfo;
import com.eucalyptus.compute.common.TerminateInstancesResponseType;
import com.eucalyptus.compute.common.TerminateInstancesType;
import com.eucalyptus.compute.common.UnmonitorInstancesType;
import com.eucalyptus.compute.common.Volume;
import com.eucalyptus.configurable.ConfigurableClass;
import com.eucalyptus.configurable.ConfigurableField;
import com.eucalyptus.util.async.AsyncRequests;
import com.fasterxml.jackson.databind.node.TextNode;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import static com.eucalyptus.util.async.AsyncExceptions.asWebServiceErrorMessage;
/**
* Created by ethomas on 2/3/14.
*/
@ConfigurableClass( root = "cloudformation", description = "Parameters controlling cloud formation")
public class AWSEC2InstanceResourceAction extends StepBasedResourceAction {
@ConfigurableField(initial = "300", description = "The amount of time (in seconds) to wait for an instance to be running after creation)")
public static volatile Integer INSTANCE_RUNNING_MAX_CREATE_RETRY_SECS = 300;
@ConfigurableField(initial = "300", description = "The amount of time (in seconds) to wait for an instance to have volumes attached after creation)")
public static volatile Integer INSTANCE_ATTACH_VOLUME_MAX_CREATE_RETRY_SECS = 300;
@ConfigurableField(initial = "300", description = "The amount of time (in seconds) to wait for an instance to have new volumes attached after update)")
public static volatile Integer INSTANCE_ATTACH_VOLUME_MAX_UPDATE_RETRY_SECS = 300;
@ConfigurableField(initial = "300", description = "The amount of time (in seconds) to wait for an instance to detach old volumes during update")
public static volatile Integer INSTANCE_DETACH_VOLUME_MAX_UPDATE_RETRY_SECS = 300;
@ConfigurableField(initial = "300", description = "The amount of time (in seconds) to wait for an instance to be terminated after deletion)")
public static volatile Integer INSTANCE_TERMINATED_MAX_DELETE_RETRY_SECS = 300;
@ConfigurableField(initial = "300", description = "The amount of time (in seconds) to wait for an instance to be stopped after update)")
public static volatile Integer INSTANCE_STOPPED_MAX_UPDATE_RETRY_SECS = 300;
@ConfigurableField(initial = "300", description = "The amount of time (in seconds) to wait for an instance to be running after update)")
public static volatile Integer INSTANCE_RUNNING_MAX_UPDATE_RETRY_SECS = 300;
private AWSEC2InstanceProperties properties = new AWSEC2InstanceProperties();
private AWSEC2InstanceResourceInfo info = new AWSEC2InstanceResourceInfo();
public AWSEC2InstanceResourceAction() {
super(fromEnum(CreateSteps.class), fromEnum(DeleteSteps.class), fromUpdateEnum(UpdateNoInterruptionSteps.class), fromUpdateEnum(UpdateSomeInterruptionSteps.class));
}
@Override
public UpdateType getUpdateType(ResourceAction resourceAction, boolean stackTagsChanged) throws Exception {
AWSEC2InstanceResourceAction otherAction = (AWSEC2InstanceResourceAction) resourceAction;
if (info.getPhysicalResourceId() == null) {
throw new ValidationErrorException("Can not call update on this instance. It was never created");
}
ServiceConfiguration configuration = Topology.lookup(Compute.class);
DescribeInstancesType describeInstancesType = MessageHelper.createMessage(DescribeInstancesType.class, info.getEffectiveUserId());
describeInstancesType.getFilterSet( ).add( Filter.filter("instance-id", info.getPhysicalResourceId()) );
DescribeInstancesResponseType describeInstancesResponseType = AsyncRequests.sendSync( configuration, describeInstancesType );
if (describeInstancesResponseType.getReservationSet().size() == 0) {
throw new ValidationErrorException("Instance " + info.getPhysicalResourceId( ) + " does not exist");
}
RunningInstancesItemType runningInstancesItemType = describeInstancesResponseType.getReservationSet().get(0).getInstancesSet().get(0);
boolean isVpc = runningInstancesItemType.getVpcId() != null;
boolean isEbs = "ebs".equals(runningInstancesItemType.getRootDeviceType());
UpdateType updateType = info.supportsTags() && stackTagsChanged ? UpdateType.NO_INTERRUPTION : UpdateType.NONE;
if (!Objects.equals(properties.getAdditionalInfo(), otherAction.properties.getAdditionalInfo())) {
updateType = updateType.max(updateType, isEbs ? UpdateType.SOME_INTERRUPTION : UpdateType.NEEDS_REPLACEMENT);
}
if (!Objects.equals(properties.getAvailabilityZone(), otherAction.properties.getAvailabilityZone())) {
updateType = updateType.max(updateType, UpdateType.NEEDS_REPLACEMENT);
}
if (!Objects.equals(properties.getBlockDeviceMappings(), otherAction.properties.getBlockDeviceMappings())) {
if (onlyChangedDeleteOnTerminate(properties.getBlockDeviceMappings(), otherAction.properties.getBlockDeviceMappings())) {
updateType = updateType.max(updateType, UpdateType.NO_INTERRUPTION);
} else {
updateType = updateType.max(updateType, UpdateType.NEEDS_REPLACEMENT);
}
}
if (!Objects.equals(properties.getDisableApiTermination(), otherAction.properties.getDisableApiTermination())) {
updateType = updateType.max(updateType, UpdateType.NO_INTERRUPTION);
}
if (!Objects.equals(properties.getEbsOptimized(), otherAction.properties.getEbsOptimized())) {
updateType = updateType.max(updateType, isEbs ? UpdateType.SOME_INTERRUPTION : UpdateType.NEEDS_REPLACEMENT);
}
if (!Objects.equals(properties.getIamInstanceProfile(), otherAction.properties.getIamInstanceProfile())) {
updateType = updateType.max(updateType, UpdateType.NEEDS_REPLACEMENT);
}
if (!Objects.equals(properties.getImageId(), otherAction.properties.getImageId())) {
updateType = updateType.max(updateType, UpdateType.NEEDS_REPLACEMENT);
}
if (!Objects.equals(properties.getInstanceInitiatedShutdownBehavior(), otherAction.properties.getInstanceInitiatedShutdownBehavior())) {
updateType = updateType.max(updateType, UpdateType.NO_INTERRUPTION);
}
if (!Objects.equals(properties.getInstanceType(), otherAction.properties.getInstanceType())) {
updateType = updateType.max(updateType, isEbs ? UpdateType.SOME_INTERRUPTION : UpdateType.NEEDS_REPLACEMENT);
}
if (!Objects.equals(properties.getKernelId(), otherAction.properties.getKernelId())) {
updateType = updateType.max(updateType, isEbs ? UpdateType.SOME_INTERRUPTION : UpdateType.NEEDS_REPLACEMENT);
}
if (!Objects.equals(properties.getKeyName(), otherAction.properties.getKeyName())) {
updateType = updateType.max(updateType, UpdateType.NEEDS_REPLACEMENT);
}
if (!Objects.equals(properties.getMonitoring(), otherAction.properties.getMonitoring())) {
updateType = updateType.max(updateType, UpdateType.NO_INTERRUPTION);
}
if (!Objects.equals(properties.getNetworkInterfaces(), otherAction.properties.getNetworkInterfaces())) {
updateType = updateType.max(updateType, UpdateType.NEEDS_REPLACEMENT);
}
if (!Objects.equals(properties.getPlacementGroupName(), otherAction.properties.getPlacementGroupName())) {
updateType = updateType.max(updateType, UpdateType.NEEDS_REPLACEMENT);
}
if (!Objects.equals(properties.getPrivateIpAddress(), otherAction.properties.getPrivateIpAddress())) {
updateType = updateType.max(updateType, UpdateType.NEEDS_REPLACEMENT);
}
if (!Objects.equals(properties.getRamdiskId(), otherAction.properties.getRamdiskId())) {
updateType = updateType.max(updateType, isEbs ? UpdateType.SOME_INTERRUPTION : UpdateType.NEEDS_REPLACEMENT);
}
if (!Objects.equals(properties.getSecurityGroupIds(), otherAction.properties.getSecurityGroupIds())) {
updateType = updateType.max(updateType, isVpc ? UpdateType.NO_INTERRUPTION : UpdateType.NEEDS_REPLACEMENT);
}
if (!Objects.equals(properties.getSecurityGroups(), otherAction.properties.getSecurityGroups())) {
updateType = updateType.max(updateType, UpdateType.NEEDS_REPLACEMENT);
}
if (!Objects.equals(properties.getSourceDestCheck(), otherAction.properties.getSourceDestCheck())) {
updateType = updateType.max(updateType, UpdateType.NO_INTERRUPTION);
}
if (!Objects.equals(properties.getSubnetId(), otherAction.properties.getSubnetId())) {
updateType = updateType.max(updateType, UpdateType.NEEDS_REPLACEMENT);
}
if (!Objects.equals(properties.getTags(), otherAction.properties.getTags())) {
updateType = updateType.max(updateType, UpdateType.NO_INTERRUPTION);
}
if (!Objects.equals(properties.getTenancy(), otherAction.properties.getTenancy())) {
updateType = updateType.max(updateType, UpdateType.NEEDS_REPLACEMENT);
}
if (!Objects.equals(properties.getUserData(), otherAction.properties.getUserData())) {
updateType = updateType.max(updateType, isEbs ? UpdateType.SOME_INTERRUPTION : UpdateType.NEEDS_REPLACEMENT);
}
if (!Objects.equals(properties.getVolumes(), otherAction.properties.getVolumes())) {
updateType = updateType.max(updateType, UpdateType.NO_INTERRUPTION);
}
return updateType;
}
private enum CreateSteps implements Step {
RUN_INSTANCE {
@Override
public ResourceAction perform(ResourceAction resourceAction) throws Exception {
AWSEC2InstanceResourceAction action = (AWSEC2InstanceResourceAction) resourceAction;
ServiceConfiguration configuration = Topology.lookup(Compute.class);
RunInstancesType runInstancesType = MessageHelper.createMessage(RunInstancesType.class, action.info.getEffectiveUserId());
runInstancesType.setImageId(action.properties.getImageId());
if (action.properties.getAdditionalInfo() != null && !action.properties.getAdditionalInfo().isEmpty()) {
runInstancesType.setAdditionalInfo(action.properties.getAdditionalInfo());
}
if (action.properties.getAvailabilityZone() != null && !action.properties.getAvailabilityZone().isEmpty()) {
runInstancesType.setAvailabilityZone(action.properties.getAvailabilityZone());
}
if (action.properties.getBlockDeviceMappings() != null && !action.properties.getBlockDeviceMappings().isEmpty()) {
runInstancesType.setBlockDeviceMapping(action.convertBlockDeviceMappings(action.properties.getBlockDeviceMappings()));
}
if (action.properties.getDisableApiTermination() != null) {
runInstancesType.setDisableTerminate(action.properties.getDisableApiTermination());
}
if (action.properties.getEbsOptimized() != null) {
runInstancesType.setEbsOptimized(action.properties.getEbsOptimized());
}
if (action.properties.getIamInstanceProfile() != null && !action.properties.getIamInstanceProfile().isEmpty()) {
runInstancesType.setIamInstanceProfileName(action.properties.getIamInstanceProfile());
}
if (action.properties.getInstanceType() != null && !action.properties.getInstanceType().isEmpty()) {
runInstancesType.setInstanceType(action.properties.getInstanceType());
}
if (action.properties.getKernelId() != null && !action.properties.getKernelId().isEmpty()) {
runInstancesType.setKernelId(action.properties.getKernelId());
}
if (action.properties.getKeyName() != null && !action.properties.getKeyName().isEmpty()) {
runInstancesType.setKeyName(action.properties.getKeyName());
}
if (action.properties.getMonitoring() != null) {
runInstancesType.setMonitoring(action.properties.getMonitoring());
}
if (action.properties.getNetworkInterfaces() != null && !action.properties.getNetworkInterfaces().isEmpty()) {
runInstancesType.setNetworkInterfaceSet(action.convertNetworkInterfaceSet(action.properties.getNetworkInterfaces()));
}
if (action.properties.getSecurityGroupIds() != null && !action.properties.getSecurityGroupIds().isEmpty() &&
action.properties.getNetworkInterfaces() != null && !action.properties.getNetworkInterfaces().isEmpty()) {
throw new ValidationErrorException("SecurityGroupIds and NetworkInterfaces can not both be set on an AWS::EC2::Instance");
}
if (action.properties.getSecurityGroups() != null && !action.properties.getSecurityGroups().isEmpty() &&
action.properties.getNetworkInterfaces() != null && !action.properties.getNetworkInterfaces().isEmpty()) {
throw new ValidationErrorException("SecurityGroups and NetworkInterfaces can not both be set on an AWS::EC2::Instance");
}
if (action.properties.getPlacementGroupName() != null && !action.properties.getPlacementGroupName().isEmpty()) {
runInstancesType.setPlacementGroup(action.properties.getPlacementGroupName());
}
if (action.properties.getPrivateIpAddress() != null && !action.properties.getPrivateIpAddress().isEmpty()) {
runInstancesType.setPrivateIpAddress(action.properties.getPrivateIpAddress());
}
if (action.properties.getRamdiskId() != null && !action.properties.getRamdiskId().isEmpty()) {
runInstancesType.setRamdiskId(action.properties.getRamdiskId());
}
if (action.properties.getSecurityGroupIds() != null && !action.properties.getSecurityGroupIds().isEmpty() &&
action.properties.getSecurityGroups() != null && !action.properties.getSecurityGroups().isEmpty()) {
throw new ValidationErrorException("SecurityGroupIds and SecurityGroups can not both be set on an AWS::EC2::Instance");
}
if (action.properties.getSecurityGroupIds() != null && !action.properties.getSecurityGroupIds().isEmpty()) {
runInstancesType.setGroupIdSet(Lists.newArrayList(action.properties.getSecurityGroupIds()));
}
if (action.properties.getSecurityGroups() != null && !action.properties.getSecurityGroups().isEmpty()) {
runInstancesType.setGroupSet(Lists.newArrayList(action.properties.getSecurityGroups()));
}
if (action.properties.getSubnetId() != null && !action.properties.getSubnetId().isEmpty()) {
runInstancesType.setSubnetId(action.properties.getSubnetId());
}
if (action.properties.getTenancy() != null && !action.properties.getTenancy().isEmpty()) {
if (!"default".equals(action.properties.getTenancy()) && !"dedicated".equals(action.properties.getTenancy())) {
throw new ValidationErrorException("Tenancy must be 'default' or 'dedicated'");
}
runInstancesType.setPlacementTenancy(action.properties.getTenancy());
}
if (action.properties.getUserData() != null && !action.properties.getUserData().isEmpty()) {
runInstancesType.setUserData(action.properties.getUserData());
}
// make sure all volumes exist and are available
if (action.properties.getVolumes() != null && !action.properties.getVolumes().isEmpty()) {
DescribeVolumesType describeVolumesType = MessageHelper.createMessage(DescribeVolumesType.class, action.info.getEffectiveUserId());
ArrayList<String> volumeIds = Lists.newArrayList();
for (EC2MountPoint ec2MountPoint : action.properties.getVolumes()) {
volumeIds.add(ec2MountPoint.getVolumeId());
}
describeVolumesType.getFilterSet( ).add( Filter.filter( "volume-id", volumeIds ) );
DescribeVolumesResponseType describeVolumesResponseType;
try {
describeVolumesResponseType = AsyncRequests.sendSync( configuration, describeVolumesType );
} catch ( final Exception e ) {
throw new ValidationErrorException("Error checking volumes " + asWebServiceErrorMessage( e, e.getMessage( ) ) );
}
Map<String, String> volumeStatusMap = Maps.newHashMap();
for (Volume volume : describeVolumesResponseType.getVolumeSet()) {
volumeStatusMap.put(volume.getVolumeId(), volume.getStatus());
}
for (String volumeId : volumeIds) {
if (!volumeStatusMap.containsKey(volumeId)) {
throw new ValidationErrorException("No such volume " + volumeId);
} else if (!"available".equals(volumeStatusMap.get(volumeId))) {
throw new ValidationErrorException("Volume " + volumeId + " not available");
}
}
}
runInstancesType.setMinCount(1);
runInstancesType.setMaxCount(1);
RunInstancesResponseType runInstancesResponseType = AsyncRequests.<RunInstancesType, RunInstancesResponseType>sendSync(configuration, runInstancesType);
action.info.setPhysicalResourceId(runInstancesResponseType.getRsvInfo().getInstancesSet().get(0).getInstanceId());
action.info.setCreatedEnoughToDelete(true);
action.info.setEucaCreateStartTime(JsonHelper.getStringFromJsonNode(new TextNode("" + System.currentTimeMillis())));
return action;
}
},
SET_ATTRIBUTES {
@Override
public ResourceAction perform(ResourceAction resourceAction) throws Exception {
AWSEC2InstanceResourceAction action = (AWSEC2InstanceResourceAction) resourceAction;
ServiceConfiguration configuration = Topology.lookup(Compute.class);
if (action.properties.getInstanceInitiatedShutdownBehavior() != null || action.properties.getSourceDestCheck() != null) {
ModifyInstanceAttributeType modifyInstanceAttributeType = MessageHelper.createMessage(ModifyInstanceAttributeType.class, action.info.getEffectiveUserId());
modifyInstanceAttributeType.setInstanceId(action.info.getPhysicalResourceId());
if (action.properties.getInstanceInitiatedShutdownBehavior() != null) {
modifyInstanceAttributeType.setInstanceInitiatedShutdownBehavior(convertToAttributeValueType(action.properties.getInstanceInitiatedShutdownBehavior()));
}
if (action.properties.getSourceDestCheck() != null) {
modifyInstanceAttributeType.setSourceDestCheck(convertToAttributeBooleanValueType(action.properties.getSourceDestCheck()));
}
AsyncRequests.sendSync(configuration, modifyInstanceAttributeType);
}
return action;
}
@Override
public Integer getTimeout() {
return null;
}
},
WAIT_UNTIL_RUNNING {
@Override
public ResourceAction perform(ResourceAction resourceAction) throws Exception {
AWSEC2InstanceResourceAction action = (AWSEC2InstanceResourceAction) resourceAction;
ServiceConfiguration configuration = Topology.lookup(Compute.class);
DescribeInstancesType describeInstancesType = MessageHelper.createMessage(DescribeInstancesType.class, action.info.getEffectiveUserId());
describeInstancesType.getFilterSet( ).add( Filter.filter( "instance-id", action.info.getPhysicalResourceId( ) ) );
DescribeInstancesResponseType describeInstancesResponseType = AsyncRequests.sendSync( configuration, describeInstancesType );
if (describeInstancesResponseType.getReservationSet().size() == 0) {
throw new RetryAfterConditionCheckFailedException("Instance " + action.info.getPhysicalResourceId( ) + " does not yet exist");
}
RunningInstancesItemType runningInstancesItemType = describeInstancesResponseType.getReservationSet().get(0).getInstancesSet().get(0);
if ("running".equals(runningInstancesItemType.getStateName())) {
action.info.setPrivateIp(JsonHelper.getStringFromJsonNode(new TextNode(runningInstancesItemType.getPrivateIpAddress())));
action.info.setPublicIp(JsonHelper.getStringFromJsonNode(new TextNode(runningInstancesItemType.getIpAddress())));
action.info.setAvailabilityZone(JsonHelper.getStringFromJsonNode(new TextNode(runningInstancesItemType.getPlacement())));
action.info.setPrivateDnsName(JsonHelper.getStringFromJsonNode(new TextNode(runningInstancesItemType.getPrivateDnsName())));
action.info.setPublicDnsName(JsonHelper.getStringFromJsonNode(new TextNode(runningInstancesItemType.getDnsName())));
action.info.setReferenceValueJson(JsonHelper.getStringFromJsonNode(new TextNode(action.info.getPhysicalResourceId())));
return action;
}
throw new RetryAfterConditionCheckFailedException(("Instance " + action.info.getPhysicalResourceId() + " is not yet running, currently " + runningInstancesItemType.getStateName()));
}
@Override
public Integer getTimeout() {
return INSTANCE_RUNNING_MAX_CREATE_RETRY_SECS;
}
},
CREATE_TAGS {
@Override
public ResourceAction perform(ResourceAction resourceAction) throws Exception {
AWSEC2InstanceResourceAction action = (AWSEC2InstanceResourceAction) resourceAction;
ServiceConfiguration configuration = Topology.lookup(Compute.class);
// Create 'system' tags as admin user
String effectiveAdminUserId = Accounts.lookupPrincipalByAccountNumber( Accounts.lookupPrincipalByUserId(action.info.getEffectiveUserId()).getAccountNumber( ) ).getUserId();
CreateTagsType createSystemTagsType = MessageHelper.createPrivilegedMessage(CreateTagsType.class, effectiveAdminUserId);
createSystemTagsType.setResourcesSet(Lists.newArrayList(action.info.getPhysicalResourceId()));
createSystemTagsType.setTagSet(EC2Helper.createTagSet(TagHelper.getEC2SystemTags(action.info, action.getStackEntity())));
AsyncRequests.<CreateTagsType, CreateTagsResponseType>sendSync(configuration, createSystemTagsType);
// Create non-system tags as regular user
List<EC2Tag> tags = TagHelper.getEC2StackTags(action.getStackEntity());
if (action.properties.getTags() != null && !action.properties.getTags().isEmpty()) {
TagHelper.checkReservedEC2TemplateTags(action.properties.getTags());
tags.addAll(action.properties.getTags());
}
if (!tags.isEmpty()) {
CreateTagsType createTagsType = MessageHelper.createMessage(CreateTagsType.class, action.info.getEffectiveUserId());
createTagsType.setResourcesSet(Lists.newArrayList(action.info.getPhysicalResourceId()));
createTagsType.setTagSet(EC2Helper.createTagSet(tags));
AsyncRequests.<CreateTagsType, CreateTagsResponseType>sendSync(configuration, createTagsType);
}
return action;
}
},
ATTACH_VOLUMES {
@Override
public ResourceAction perform(ResourceAction resourceAction) throws Exception {
AWSEC2InstanceResourceAction action = (AWSEC2InstanceResourceAction) resourceAction;
ServiceConfiguration configuration = Topology.lookup(Compute.class);
if (action.properties.getVolumes() != null && !action.properties.getVolumes().isEmpty()) {
ArrayList<String> volumeIds = Lists.newArrayList();
Map<String, String> deviceMap = Maps.newHashMap();
for (EC2MountPoint ec2MountPoint : action.properties.getVolumes()) {
volumeIds.add(ec2MountPoint.getVolumeId());
deviceMap.put(ec2MountPoint.getVolumeId(), ec2MountPoint.getDevice());
AttachVolumeType attachVolumeType = MessageHelper.createMessage(AttachVolumeType.class, action.info.getEffectiveUserId());
attachVolumeType.setInstanceId(action.info.getPhysicalResourceId());
attachVolumeType.setVolumeId(ec2MountPoint.getVolumeId());
attachVolumeType.setDevice(ec2MountPoint.getDevice());
AsyncRequests.<AttachVolumeType, AttachVolumeResponseType>sendSync(configuration, attachVolumeType);
}
}
return action;
}
},
WAIT_UNTIL_VOLUMES_ATTACHED {
@Override
public ResourceAction perform(ResourceAction resourceAction) throws Exception {
AWSEC2InstanceResourceAction action = (AWSEC2InstanceResourceAction) resourceAction;
ServiceConfiguration configuration = Topology.lookup(Compute.class);
if (action.properties.getVolumes() != null && !action.properties.getVolumes().isEmpty()) {
ArrayList<String> volumeIds = Lists.newArrayList();
Map<String, String> deviceMap = Maps.newHashMap();
for (EC2MountPoint ec2MountPoint : action.properties.getVolumes()) {
volumeIds.add(ec2MountPoint.getVolumeId());
deviceMap.put(ec2MountPoint.getVolumeId(), ec2MountPoint.getDevice());
}
DescribeVolumesType describeVolumesType = MessageHelper.createMessage(DescribeVolumesType.class, action.info.getEffectiveUserId());
describeVolumesType.getFilterSet( ).add( Filter.filter( "volume-id", volumeIds ) );
DescribeVolumesResponseType describeVolumesResponseType;
try {
describeVolumesResponseType = AsyncRequests.sendSync( configuration, describeVolumesType );
} catch ( Exception e ) {
throw new RetryAfterConditionCheckFailedException("Error describing volumes: " + asWebServiceErrorMessage( e, e.getMessage( ) ) );
}
Map<String, String> volumeStatusMap = Maps.newHashMap();
for (Volume volume : describeVolumesResponseType.getVolumeSet()) {
for (AttachedVolume attachedVolume : volume.getAttachmentSet()) {
if (attachedVolume.getInstanceId().equals(action.info.getPhysicalResourceId()) && attachedVolume.getDevice().equals(deviceMap.get(volume.getVolumeId()))) {
volumeStatusMap.put(volume.getVolumeId(), attachedVolume.getStatus());
}
}
}
for (String volumeId : volumeIds) {
if (!"attached".equals(volumeStatusMap.get(volumeId))) {
throw new RetryAfterConditionCheckFailedException("One or more volumes is not yet attached to the instance");
}
}
}
return action;
}
@Override
public Integer getTimeout() {
return INSTANCE_ATTACH_VOLUME_MAX_CREATE_RETRY_SECS;
}
},
CHECK_SIGNALS {
@Override
public ResourceAction perform(ResourceAction resourceAction) throws Exception {
AWSEC2InstanceResourceAction action = (AWSEC2InstanceResourceAction) resourceAction;
CreationPolicy creationPolicy = CreationPolicy.parse(action.info.getCreationPolicyJson());
if (creationPolicy != null && creationPolicy.getResourceSignal() != null) {
if (creationPolicy.getResourceSignal().getCount() != 1) {
throw new ValidationErrorException("ResourceSignal CreationPolicy property Count cannot be greater than 1 for EC2 instance resources");
}
// check for signals
Collection<SignalEntity> signals = SignalEntityManager.getSignals(action.getStackEntity().getStackId(), action.info.getAccountId(), action.info.getLogicalResourceId(),
action.getStackEntity().getStackVersion());
int numSuccessSignals = 0;
if (signals != null) {
for (SignalEntity signal : signals) {
// For some reason AWS completely ignores signals that do not have the unique id of the instance id
if (!Objects.equals(signal.getUniqueId(), action.info.getPhysicalResourceId())) continue;
if (signal.getStatus() == SignalEntity.Status.FAILURE) {
throw new ResourceFailureException("Received FAILURE signal with UniqueId " + signal.getUniqueId());
}
if (!signal.getProcessed()) {
StackEventEntityManager.addSignalStackEvent(signal);
signal.setProcessed(true);
SignalEntityManager.updateSignal(signal);
}
numSuccessSignals++;
}
}
if (numSuccessSignals < creationPolicy.getResourceSignal().getCount()) {
long durationMs = System.currentTimeMillis() - Long.valueOf(JsonHelper.getJsonNodeFromString(action.info.getEucaCreateStartTime()).asText());
if (TimeUnit.MILLISECONDS.toSeconds(durationMs) > creationPolicy.getResourceSignal().getTimeout()) {
throw new ResourceFailureException("Failed to receive " + creationPolicy.getResourceSignal().getCount() + " resource signal(s) within the specified duration");
}
throw new RetryAfterConditionCheckFailedException("Not enough success signals yet");
}
}
return action;
}
@Nullable
@Override
public Integer getTimeout() {
return (int) TimeUnit.HOURS.toSeconds(12);
}
};
@Nullable
@Override
public Integer getTimeout() {
return null;
}
}
private enum DeleteSteps implements Step {
TERMINATE_INSTANCE {
@Override
public ResourceAction perform(ResourceAction resourceAction) throws Exception {
AWSEC2InstanceResourceAction action = (AWSEC2InstanceResourceAction) resourceAction;
ServiceConfiguration configuration = Topology.lookup(Compute.class);
// See if instance was ever populated
if (!Boolean.TRUE.equals(action.info.getCreatedEnoughToDelete())) return action;
// First see if instance exists or has been terminated
DescribeInstancesType describeInstancesType = MessageHelper.createMessage(DescribeInstancesType.class, action.info.getEffectiveUserId());
describeInstancesType.getFilterSet().add(Filter.filter("instance-id", action.info.getPhysicalResourceId()));
DescribeInstancesResponseType describeInstancesResponseType = AsyncRequests.sendSync(configuration, describeInstancesType);
if (describeInstancesResponseType.getReservationSet().size() == 0) return action; // already terminated
if ("terminated".equals(describeInstancesResponseType.getReservationSet().get(0).getInstancesSet().get(0).getStateName())) {
return action;
}
// Send terminate message (do not need to detatch volumes thankfully
TerminateInstancesType terminateInstancesType = MessageHelper.createMessage(TerminateInstancesType.class, action.info.getEffectiveUserId());
terminateInstancesType.setInstancesSet(Lists.newArrayList(action.info.getPhysicalResourceId()));
AsyncRequests.<TerminateInstancesType, TerminateInstancesResponseType>sendSync(configuration, terminateInstancesType);
return action;
}
},
VERIFY_TERMINATED {
@Override
public ResourceAction perform(ResourceAction resourceAction) throws Exception {
AWSEC2InstanceResourceAction action = (AWSEC2InstanceResourceAction) resourceAction;
ServiceConfiguration configuration = Topology.lookup(Compute.class);
// See if instance was ever populated
if (!Boolean.TRUE.equals(action.info.getCreatedEnoughToDelete())) return action;
DescribeInstancesType describeInstancesType = MessageHelper.createMessage(DescribeInstancesType.class, action.info.getEffectiveUserId());
describeInstancesType.getFilterSet( ).add( Filter.filter( "instance-id", action.info.getPhysicalResourceId( ) ) );
DescribeInstancesResponseType describeInstancesResponseType = AsyncRequests.sendSync( configuration, describeInstancesType );
if (describeInstancesResponseType.getReservationSet().size() == 0) return action; // already terminated
if ("terminated".equals(describeInstancesResponseType.getReservationSet().get(0).getInstancesSet().get(0).getStateName())) {
return action;
}
throw new RetryAfterConditionCheckFailedException(("Instance " + action.info.getPhysicalResourceId() + " is not yet terminated, currently " + describeInstancesResponseType.getReservationSet().get(0).getInstancesSet().get(0).getStateName()));
}
@Override
public Integer getTimeout() {
return INSTANCE_TERMINATED_MAX_DELETE_RETRY_SECS;
}
};
@Nullable
@Override
public Integer getTimeout() {
return null;
}
}
private enum UpdateNoInterruptionSteps implements UpdateStep {
UPDATE_ATTRIBUTES {
@Override
public ResourceAction perform(ResourceAction oldResourceAction, ResourceAction newResourceAction) throws Exception {
AWSEC2InstanceResourceAction oldAction = (AWSEC2InstanceResourceAction) oldResourceAction;
AWSEC2InstanceResourceAction newAction = (AWSEC2InstanceResourceAction) newResourceAction;
ServiceConfiguration configuration = Topology.lookup(Compute.class);
// a couple of checks before we change anything
if (newAction.properties.getSecurityGroups() != null && !newAction.properties.getSecurityGroups().isEmpty() &&
newAction.properties.getNetworkInterfaces() != null && !newAction.properties.getNetworkInterfaces().isEmpty()) {
throw new ValidationErrorException("SecurityGroups and NetworkInterfaces can not both be set on an AWS::EC2::Instance");
}
if (newAction.properties.getSecurityGroupIds() != null && !newAction.properties.getSecurityGroupIds().isEmpty() &&
newAction.properties.getNetworkInterfaces() != null && !newAction.properties.getNetworkInterfaces().isEmpty()) {
throw new ValidationErrorException("SecurityGroupIds and NetworkInterfaces can not both be set on an AWS::EC2::Instance");
}
if (newAction.properties.getSecurityGroupIds() != null && !newAction.properties.getSecurityGroupIds().isEmpty() &&
newAction.properties.getSecurityGroups() != null && !newAction.properties.getSecurityGroups().isEmpty()) {
throw new ValidationErrorException("SecurityGroupIds and SecurityGroups can not both be set on an AWS::EC2::Instance");
}
// first lookup existing attributes
DescribeInstancesType describeInstancesType = MessageHelper.createMessage(DescribeInstancesType.class, newAction.info.getEffectiveUserId());
describeInstancesType.getFilterSet().add(Filter.filter("instance-id", newAction.info.getPhysicalResourceId()));
DescribeInstancesResponseType describeInstancesResponseType = AsyncRequests.sendSync(configuration, describeInstancesType);
if (describeInstancesResponseType.getReservationSet().size() == 0) {
throw new ValidationErrorException("Instance " + newAction.info.getPhysicalResourceId() + " does not exist");
}
RunningInstancesItemType runningInstancesItemType = describeInstancesResponseType.getReservationSet().get(0).getInstancesSet().get(0);
if (!blockDeviceMappingsDeleteOnTerminateEquals(runningInstancesItemType.getBlockDevices(), newAction.properties.getBlockDeviceMappings())) {
if (newAction.properties.getBlockDeviceMappings() != null && !newAction.properties.getBlockDeviceMappings().isEmpty()) {
ModifyInstanceAttributeType modifyInstanceAttributeType = MessageHelper.createMessage(ModifyInstanceAttributeType.class, newAction.info.getEffectiveUserId());
modifyInstanceAttributeType.setInstanceId(newAction.info.getPhysicalResourceId());
modifyInstanceAttributeType.setBlockDeviceMappingSet(convertToBlockMappingWithDeleteOnTerminateValues(newAction.properties.getBlockDeviceMappings()));
AsyncRequests.sendSync(configuration, modifyInstanceAttributeType);
} else {
// If here, oldAction and newAction block device mapping must only differ by a value in the
// 'DeleteOnTermination' in one or more of the EBS attached block devices.
// This means that BlockDeviceMapping can not be null or empty in the old template and
// not-null and not empty in the new template. Experimentation shows a 'null' value
// in block device mapping on an update will not change the delete-on-terminate value of the
// root volume.
// thus we just check the old volume is null or empty, in which case we do nothing,
// otherwise something funny is going on.
if (oldAction.properties.getBlockDeviceMappings() != null && !oldAction.properties.getBlockDeviceMappings().isEmpty()) {
throw new ValidationErrorException("Unable to update block device mappings. Null value somehow equated with non-null value");
}
}
}
if (!Objects.equals(runningInstancesItemType.getDisableApiTermination(), newAction.properties.getDisableApiTermination())) {
if (newAction.properties.getDisableApiTermination() != null) {
ModifyInstanceAttributeType modifyInstanceAttributeType = MessageHelper.createMessage(ModifyInstanceAttributeType.class, newAction.info.getEffectiveUserId());
modifyInstanceAttributeType.setInstanceId(newAction.info.getPhysicalResourceId());
modifyInstanceAttributeType.setDisableApiTermination(convertToAttributeBooleanValueType(newAction.properties.getDisableApiTermination()));
AsyncRequests.sendSync(configuration, modifyInstanceAttributeType);
} else {
resetInstanceAttribute(configuration, newAction, "disableApiTermination");
}
}
if (!Objects.equals(BoolToString(runningInstancesItemType.getInstanceInitiatedShutdownBehavior()), newAction.properties.getInstanceInitiatedShutdownBehavior())) {
if (newAction.properties.getInstanceInitiatedShutdownBehavior() != null) {
ModifyInstanceAttributeType modifyInstanceAttributeType = MessageHelper.createMessage(ModifyInstanceAttributeType.class, newAction.info.getEffectiveUserId());
modifyInstanceAttributeType.setInstanceId(newAction.info.getPhysicalResourceId());
modifyInstanceAttributeType.setInstanceInitiatedShutdownBehavior(convertToAttributeValueType(newAction.properties.getInstanceInitiatedShutdownBehavior()));
AsyncRequests.sendSync(configuration, modifyInstanceAttributeType);
} else {
// AWS does not support resetInstanceAttribute( on "instanceInitiatedShutdownBehavior"
ModifyInstanceAttributeType modifyInstanceAttributeType = MessageHelper.createMessage(ModifyInstanceAttributeType.class, newAction.info.getEffectiveUserId());
modifyInstanceAttributeType.setInstanceId(newAction.info.getPhysicalResourceId());
modifyInstanceAttributeType.setInstanceInitiatedShutdownBehavior(convertToAttributeValueType("stop"));
AsyncRequests.sendSync(configuration, modifyInstanceAttributeType);
}
}
// check security group ids on vpc, but only if the network interfaces property is not set. If anything has changed in network interfaces,
// we run with replacement anyway
if (newAction.properties.getNetworkInterfaces() == null || newAction.properties.getNetworkInterfaces().isEmpty()) {
// Only update the 'groupSet' (i.e. security group ids) if: vpc is enabled -- otherwise needs replacement anyway
if (runningInstancesItemType.getVpcId() != null) {
List<String> newGroups = defaultSecurityGroupInVpcIfNullOrEmpty(configuration, runningInstancesItemType.getVpcId(), newAction.info.getEffectiveUserId(), newAction.properties.getSecurityGroupIds());
if (!groupIdsEquals(runningInstancesItemType.getGroupSet(), newGroups)) {
ModifyInstanceAttributeType modifyInstanceAttributeType = MessageHelper.createMessage(ModifyInstanceAttributeType.class, newAction.info.getEffectiveUserId());
modifyInstanceAttributeType.setInstanceId(newAction.info.getPhysicalResourceId());
modifyInstanceAttributeType.setGroupIdSet(convertToGroupIdSet(newGroups));
AsyncRequests.sendSync(configuration, modifyInstanceAttributeType);
}
}
}
if (!Objects.equals(runningInstancesItemType.getMonitoring(), BoolToString(newAction.properties.getMonitoring()))) {
if (!Boolean.TRUE.equals(newAction.properties.getMonitoring())) {
UnmonitorInstancesType unmonitorInstancesType = MessageHelper.createMessage(UnmonitorInstancesType.class, newAction.info.getEffectiveUserId());
unmonitorInstancesType.setInstancesSet(Lists.newArrayList(newAction.info.getPhysicalResourceId()));
AsyncRequests.sendSync(configuration, unmonitorInstancesType);
} else {
MonitorInstancesType monitorInstancesType = MessageHelper.createMessage(MonitorInstancesType.class, newAction.info.getEffectiveUserId());
monitorInstancesType.setInstancesSet(Lists.newArrayList(newAction.info.getPhysicalResourceId()));
AsyncRequests.sendSync(configuration, monitorInstancesType);
}
}
// EUCA-12124: regardless ofvalue runningInstancesItemType.getSourceDestCheck() is null so we get the value another way
DescribeInstanceAttributeType describeInstanceAttributeType = MessageHelper.createMessage(DescribeInstanceAttributeType.class, newAction.info.getEffectiveUserId());
describeInstanceAttributeType.setInstanceId(newAction.info.getPhysicalResourceId());
describeInstanceAttributeType.setAttribute("sourceDestCheck");
DescribeInstanceAttributeResponseType describeInstanceAttributeResponseType = AsyncRequests.sendSync(configuration, describeInstanceAttributeType);
if (!Objects.equals(describeInstanceAttributeResponseType.getSourceDestCheck(), newAction.properties.getSourceDestCheck())) {
if (newAction.properties.getSourceDestCheck() != null) {
ModifyInstanceAttributeType modifyInstanceAttributeType = MessageHelper.createMessage(ModifyInstanceAttributeType.class, newAction.info.getEffectiveUserId());
modifyInstanceAttributeType.setInstanceId(newAction.info.getPhysicalResourceId());
modifyInstanceAttributeType.setSourceDestCheck(convertToAttributeBooleanValueType(newAction.properties.getSourceDestCheck()));
AsyncRequests.sendSync(configuration, modifyInstanceAttributeType);
} else {
resetInstanceAttribute(configuration, newAction, "sourceDestCheck");
}
}
return newAction;
}
private String BoolToString(Boolean b) {
return b == null ? null : String.valueOf(b.booleanValue());
}
},
UPDATE_TAGS {
@Override
public ResourceAction perform(ResourceAction oldResourceAction, ResourceAction newResourceAction) throws Exception {
AWSEC2InstanceResourceAction oldAction = (AWSEC2InstanceResourceAction) oldResourceAction;
AWSEC2InstanceResourceAction newAction = (AWSEC2InstanceResourceAction) newResourceAction;
ServiceConfiguration configuration = Topology.lookup(Compute.class);
DescribeTagsType describeTagsType = MessageHelper.createMessage(DescribeTagsType.class, newAction.info.getEffectiveUserId());
describeTagsType.setFilterSet(Lists.newArrayList(Filter.filter("resource-id", newAction.info.getPhysicalResourceId())));
DescribeTagsResponseType describeTagsResponseType = AsyncRequests.sendSync(configuration, describeTagsType);
Set<EC2Tag> existingTags = Sets.newLinkedHashSet();
if (describeTagsResponseType != null && describeTagsResponseType.getTagSet() != null) {
for (TagInfo tagInfo: describeTagsResponseType.getTagSet()) {
EC2Tag tag = new EC2Tag();
tag.setKey(tagInfo.getKey());
tag.setValue(tagInfo.getValue());
existingTags.add(tag);
}
}
Set<EC2Tag> newTags = Sets.newLinkedHashSet();
if (newAction.properties.getTags() != null) {
newTags.addAll(newAction.properties.getTags());
}
List<EC2Tag> newStackTags = TagHelper.getEC2StackTags(newAction.getStackEntity());
if (newStackTags != null) {
newTags.addAll(newStackTags);
}
TagHelper.checkReservedEC2TemplateTags(newTags);
// add only 'new' tags
Set<EC2Tag> onlyNewTags = Sets.difference(newTags, existingTags);
if (!onlyNewTags.isEmpty()) {
CreateTagsType createTagsType = MessageHelper.createMessage(CreateTagsType.class, newAction.info.getEffectiveUserId());
createTagsType.setResourcesSet(Lists.newArrayList(newAction.info.getPhysicalResourceId()));
createTagsType.setTagSet(EC2Helper.createTagSet(onlyNewTags));
AsyncRequests.<CreateTagsType, CreateTagsResponseType>sendSync(configuration, createTagsType);
}
// Get old tags...
Set<EC2Tag> oldTags = Sets.newLinkedHashSet();
if (oldAction.properties.getTags() != null) {
oldTags.addAll(oldAction.properties.getTags());
}
List<EC2Tag> oldStackTags = TagHelper.getEC2StackTags(oldAction.getStackEntity());
if (oldStackTags != null) {
oldTags.addAll(oldStackTags);
}
// remove only the old tags that are not new and that exist
Set<EC2Tag> tagsToRemove = Sets.intersection(oldTags, Sets.difference(existingTags, newTags));
if (!tagsToRemove.isEmpty()) {
DeleteTagsType deleteTagsType = MessageHelper.createMessage(DeleteTagsType.class, newAction.info.getEffectiveUserId());
deleteTagsType.setResourcesSet(Lists.newArrayList(newAction.info.getPhysicalResourceId()));
deleteTagsType.setTagSet(EC2Helper.deleteTagSet(tagsToRemove));
AsyncRequests.<DeleteTagsType, DeleteTagsResponseType>sendSync(configuration, deleteTagsType);
}
return newAction;
}
},
DETACH_OLD_VOLUMES {
@Override
public ResourceAction perform(ResourceAction oldResourceAction, ResourceAction newResourceAction) throws Exception {
AWSEC2InstanceResourceAction oldAction = (AWSEC2InstanceResourceAction) oldResourceAction;
AWSEC2InstanceResourceAction newAction = (AWSEC2InstanceResourceAction) newResourceAction;
ServiceConfiguration configuration = Topology.lookup(Compute.class);
Map<String, String> oldVolumeDeviceMap = getVolumeDeviceMap(oldAction);
Map<String, String> newVolumeDeviceMap = getVolumeDeviceMap(newAction);
Map<String, String> detachingVolumeDeviceMap = getDetachingVolumeDeviceMap(oldVolumeDeviceMap, newVolumeDeviceMap);
DescribeVolumesType describeVolumesType = MessageHelper.createMessage(DescribeVolumesType.class, newAction.info.getEffectiveUserId());
describeVolumesType.getFilterSet( ).add( Filter.filter( "volume-id", detachingVolumeDeviceMap.keySet() ) );
DescribeVolumesResponseType describeVolumesResponseType = AsyncRequests.sendSync( configuration, describeVolumesType );
if (describeVolumesResponseType != null && describeVolumesResponseType.getVolumeSet() != null) {
for (Volume volume : describeVolumesResponseType.getVolumeSet()) {
if (!detachingVolumeDeviceMap.containsKey(volume.getVolumeId()))
continue; // shouldn't happen but unrelated volume. I don't care
if (volume.getAttachmentSet() != null) {
for (AttachedVolume attachedVolume : volume.getAttachmentSet()) {
// only detach volumes that match the old input record. Other things that might match (new device or
// instance id) means someone changed something outside cloudformation. We will not second guess that.
if (detachingVolumeDeviceMap.containsKey(attachedVolume.getVolumeId()) &&
newAction.info.getPhysicalResourceId().equals(attachedVolume.getInstanceId()) &&
detachingVolumeDeviceMap.get(attachedVolume.getVolumeId()).equals(attachedVolume.getDevice())) {
DetachVolumeType detachVolumeType = MessageHelper.createMessage(DetachVolumeType.class, newAction.info.getEffectiveUserId());
detachVolumeType.setVolumeId(attachedVolume.getVolumeId());
detachVolumeType.setInstanceId(newAction.info.getPhysicalResourceId());
AsyncRequests.sendSync(configuration, detachVolumeType);
}
}
}
}
}
return newAction;
}
},
WAIT_UNTIL_OLD_VOLUMES_NOT_ATTACHED {
@Override
public ResourceAction perform(ResourceAction oldResourceAction, ResourceAction newResourceAction) throws Exception {
AWSEC2InstanceResourceAction oldAction = (AWSEC2InstanceResourceAction) oldResourceAction;
AWSEC2InstanceResourceAction newAction = (AWSEC2InstanceResourceAction) newResourceAction;
ServiceConfiguration configuration = Topology.lookup(Compute.class);
Map<String, String> oldVolumeDeviceMap = getVolumeDeviceMap(oldAction);
Map<String, String> newVolumeDeviceMap = getVolumeDeviceMap(newAction);
Map<String, String> detachingVolumeDeviceMap = getDetachingVolumeDeviceMap(oldVolumeDeviceMap, newVolumeDeviceMap);
// a bit of a strange situation here. In the previous step we only detached old volumes that were in a sense
// 'correct'. (i.e. existed and had the old instance id and device associated with them. As such our test here
// is not so much that the volumes are 'available' as some of them may be attached to other instances or at
// other device ports. We just make sure we don't have any devices that still have an attachment set that contains
// the 'correct' information
boolean stillAttached = false;
DescribeVolumesType describeVolumesType = MessageHelper.createMessage(DescribeVolumesType.class, newAction.info.getEffectiveUserId());
if (!detachingVolumeDeviceMap.isEmpty()) {
describeVolumesType.getFilterSet().add(Filter.filter("volume-id", detachingVolumeDeviceMap.keySet()));
DescribeVolumesResponseType describeVolumesResponseType = AsyncRequests.sendSync(configuration, describeVolumesType);
if (describeVolumesResponseType != null && describeVolumesResponseType.getVolumeSet() != null) {
for (Volume volume : describeVolumesResponseType.getVolumeSet()) {
if (!detachingVolumeDeviceMap.containsKey(volume.getVolumeId()))
continue;
if (volume.getAttachmentSet() != null) {
for (AttachedVolume attachedVolume : volume.getAttachmentSet()) {
if (detachingVolumeDeviceMap.containsKey(attachedVolume.getVolumeId()) &&
newAction.info.getPhysicalResourceId().equals(attachedVolume.getInstanceId()) &&
detachingVolumeDeviceMap.get(attachedVolume.getVolumeId()).equals(attachedVolume.getDevice())) {
// might still be lingering in the detached state
if (!"detached".equals(attachedVolume.getStatus())) {
stillAttached = true;
break;
}
}
}
}
}
}
}
if (stillAttached) {
throw new RetryAfterConditionCheckFailedException("One or more volumes is not yet detached from the instance");
}
return newAction;
}
@Override
public Integer getTimeout() {
return INSTANCE_DETACH_VOLUME_MAX_UPDATE_RETRY_SECS;
}
},
ATTACH_NEW_VOLUMES {
public ResourceAction perform(ResourceAction oldResourceAction, ResourceAction newResourceAction) throws Exception {
AWSEC2InstanceResourceAction oldAction = (AWSEC2InstanceResourceAction) oldResourceAction;
AWSEC2InstanceResourceAction newAction = (AWSEC2InstanceResourceAction) newResourceAction;
ServiceConfiguration configuration = Topology.lookup(Compute.class);
Map<String, String> oldVolumeDeviceMap = getVolumeDeviceMap(oldAction);
Map<String, String> newVolumeDeviceMap = getVolumeDeviceMap(newAction);
Map<String, String> attachingVolumeDeviceMap = getAttachingVolumeDeviceMap(oldVolumeDeviceMap, newVolumeDeviceMap);
for (String volumeId: attachingVolumeDeviceMap.keySet()) {
AttachVolumeType attachVolumeType = MessageHelper.createMessage(AttachVolumeType.class, newAction.info.getEffectiveUserId());
attachVolumeType.setVolumeId(volumeId);
attachVolumeType.setInstanceId(newAction.info.getPhysicalResourceId());
attachVolumeType.setDevice(attachingVolumeDeviceMap.get(volumeId));
AsyncRequests.<AttachVolumeType, AttachVolumeResponseType>sendSync(configuration, attachVolumeType);
}
return newAction;
}
},
WAIT_UNTIL_NEW_VOLUMES_ATTACHED {
@Override
public ResourceAction perform(ResourceAction oldResourceAction, ResourceAction newResourceAction) throws Exception {
AWSEC2InstanceResourceAction oldAction = (AWSEC2InstanceResourceAction) oldResourceAction;
AWSEC2InstanceResourceAction newAction = (AWSEC2InstanceResourceAction) newResourceAction;
ServiceConfiguration configuration = Topology.lookup(Compute.class);
Map<String, String> oldVolumeDeviceMap = getVolumeDeviceMap(oldAction);
Map<String, String> newVolumeDeviceMap = getVolumeDeviceMap(newAction);
Map<String, String> attachingVolumeDeviceMap = getAttachingVolumeDeviceMap(oldVolumeDeviceMap, newVolumeDeviceMap);
if (!attachingVolumeDeviceMap.isEmpty()) {
Map<String, String> volumeStatusMap = Maps.newHashMap();
Collection<String> volumeIds = attachingVolumeDeviceMap.keySet();
DescribeVolumesType describeVolumesType = MessageHelper.createMessage(DescribeVolumesType.class, newAction.info.getEffectiveUserId());
describeVolumesType.getFilterSet( ).add( Filter.filter( "volume-id", volumeIds ) );
DescribeVolumesResponseType describeVolumesResponseType = AsyncRequests.sendSync( configuration, describeVolumesType );
for (Volume volume : describeVolumesResponseType.getVolumeSet()) {
for (AttachedVolume attachedVolume : volume.getAttachmentSet()) {
if (attachedVolume.getInstanceId().equals(newAction.info.getPhysicalResourceId()) && attachedVolume.getDevice().equals(attachingVolumeDeviceMap.get(volume.getVolumeId()))) {
volumeStatusMap.put(volume.getVolumeId(), attachedVolume.getStatus());
}
}
}
for (String volumeId : volumeIds) {
if (!"attached".equals(volumeStatusMap.get(volumeId))) {
throw new RetryAfterConditionCheckFailedException("One or more volumes is not yet attached to the instance");
}
}
}
return newAction;
}
@Override
public Integer getTimeout() {
return INSTANCE_ATTACH_VOLUME_MAX_UPDATE_RETRY_SECS;
}
};
@Nullable
@Override
public Integer getTimeout() {
return null;
}
}
private enum UpdateSomeInterruptionSteps implements UpdateStep {
UPDATE_NO_INTERRUPTION_ATTRIBUTES {
@Override
public ResourceAction perform(ResourceAction oldResourceAction, ResourceAction newResourceAction) throws Exception {
return UpdateNoInterruptionSteps.UPDATE_ATTRIBUTES.perform(oldResourceAction, newResourceAction);
}
@Nullable
@Override
public Integer getTimeout() {
return UpdateNoInterruptionSteps.UPDATE_ATTRIBUTES.getTimeout();
}
},
UPDATE_TAGS {
@Override
public ResourceAction perform(ResourceAction oldResourceAction, ResourceAction newResourceAction) throws Exception {
return UpdateNoInterruptionSteps.UPDATE_TAGS.perform(oldResourceAction, newResourceAction);
}
@Nullable
@Override
public Integer getTimeout() {
return UpdateNoInterruptionSteps.UPDATE_TAGS.getTimeout();
}
},
DETACH_OLD_VOLUMES {
@Override
public ResourceAction perform(ResourceAction oldResourceAction, ResourceAction newResourceAction) throws Exception {
return UpdateNoInterruptionSteps.DETACH_OLD_VOLUMES.perform(oldResourceAction, newResourceAction);
}
@Nullable
@Override
public Integer getTimeout() {
return UpdateNoInterruptionSteps.DETACH_OLD_VOLUMES.getTimeout();
}
},
WAIT_UNTIL_OLD_VOLUMES_NOT_ATTACHED {
@Override
public ResourceAction perform(ResourceAction oldResourceAction, ResourceAction newResourceAction) throws Exception {
return UpdateNoInterruptionSteps.WAIT_UNTIL_OLD_VOLUMES_NOT_ATTACHED.perform(oldResourceAction, newResourceAction);
}
@Nullable
@Override
public Integer getTimeout() {
return UpdateNoInterruptionSteps.WAIT_UNTIL_OLD_VOLUMES_NOT_ATTACHED.getTimeout();
}
},
ATTACH_NEW_VOLUMES {
@Override
public ResourceAction perform(ResourceAction oldResourceAction, ResourceAction newResourceAction) throws Exception {
return UpdateNoInterruptionSteps.ATTACH_NEW_VOLUMES.perform(oldResourceAction, newResourceAction);
}
@Nullable
@Override
public Integer getTimeout() {
return UpdateNoInterruptionSteps.ATTACH_NEW_VOLUMES.getTimeout();
}
},
WAIT_UNTIL_NEW_VOLUMES_ATTACHED {
@Override
public ResourceAction perform(ResourceAction oldResourceAction, ResourceAction newResourceAction) throws Exception {
return UpdateNoInterruptionSteps.WAIT_UNTIL_NEW_VOLUMES_ATTACHED.perform(oldResourceAction, newResourceAction);
}
@Nullable
@Override
public Integer getTimeout() {
return UpdateNoInterruptionSteps.WAIT_UNTIL_NEW_VOLUMES_ATTACHED.getTimeout();
}
},
STOP_INSTANCE {
@Override
public ResourceAction perform(ResourceAction oldResourceAction, ResourceAction newResourceAction) throws Exception {
AWSEC2InstanceResourceAction oldAction = (AWSEC2InstanceResourceAction) oldResourceAction;
AWSEC2InstanceResourceAction newAction = (AWSEC2InstanceResourceAction) newResourceAction;
ServiceConfiguration configuration = Topology.lookup(Compute.class);
StopInstancesType stopInstancesType = MessageHelper.createMessage(StopInstancesType.class, newAction.info.getEffectiveUserId());
stopInstancesType.setInstancesSet(Lists.newArrayList(newAction.info.getPhysicalResourceId()));
AsyncRequests.sendSync(configuration, stopInstancesType);
return newAction;
}
},
VERIFY_STOPPED {
@Override
public ResourceAction perform(ResourceAction oldResourceAction, ResourceAction newResourceAction) throws Exception {
AWSEC2InstanceResourceAction oldAction = (AWSEC2InstanceResourceAction) oldResourceAction;
AWSEC2InstanceResourceAction newAction = (AWSEC2InstanceResourceAction) newResourceAction;
ServiceConfiguration configuration = Topology.lookup(Compute.class);
DescribeInstancesType describeInstancesType = MessageHelper.createMessage(DescribeInstancesType.class, newAction.info.getEffectiveUserId());
describeInstancesType.getFilterSet().add(Filter.filter("instance-id", newAction.info.getPhysicalResourceId()));
DescribeInstancesResponseType describeInstancesResponseType = AsyncRequests.sendSync(configuration, describeInstancesType);
if (describeInstancesResponseType.getReservationSet().size() == 0) throw new ValidationErrorException("Instance " + newAction.info.getPhysicalResourceId() + " not found.");
if ("terminated".equals(describeInstancesResponseType.getReservationSet().get(0).getInstancesSet().get(0).getStateName())) {
throw new ValidationErrorException("Instance " + newAction.info.getPhysicalResourceId() + " terminated.");
}
if ("stopped".equals(describeInstancesResponseType.getReservationSet().get(0).getInstancesSet().get(0).getStateName())) {
return newAction;
}
throw new RetryAfterConditionCheckFailedException(("Instance " + newAction.info.getPhysicalResourceId() + " is not yet stopped, currently " + describeInstancesResponseType.getReservationSet().get(0).getInstancesSet().get(0).getStateName()));
}
public Integer getTimeout() {
return INSTANCE_STOPPED_MAX_UPDATE_RETRY_SECS;
}
},
UPDATE_SOME_INTERRUPTION_ATTRIBUTES {
@Override
public ResourceAction perform(ResourceAction oldResourceAction, ResourceAction newResourceAction) throws Exception {
AWSEC2InstanceResourceAction oldAction = (AWSEC2InstanceResourceAction) oldResourceAction;
AWSEC2InstanceResourceAction newAction = (AWSEC2InstanceResourceAction) newResourceAction;
ServiceConfiguration configuration = Topology.lookup(Compute.class);
// first lookup existing attributes
DescribeInstancesType describeInstancesType = MessageHelper.createMessage(DescribeInstancesType.class, newAction.info.getEffectiveUserId());
describeInstancesType.getFilterSet().add(Filter.filter("instance-id", newAction.info.getPhysicalResourceId()));
DescribeInstancesResponseType describeInstancesResponseType = AsyncRequests.sendSync(configuration, describeInstancesType);
if (describeInstancesResponseType.getReservationSet().size() == 0) {
throw new ValidationErrorException("Instance " + newAction.info.getPhysicalResourceId() + " does not exist");
}
RunningInstancesItemType runningInstancesItemType = describeInstancesResponseType.getReservationSet().get(0).getInstancesSet().get(0);
DescribeInstanceAttributeType describeInstanceAttributeType1 = MessageHelper.createMessage(DescribeInstanceAttributeType.class, newAction.info.getEffectiveUserId());
describeInstanceAttributeType1.setInstanceId(newAction.info.getPhysicalResourceId());
describeInstanceAttributeType1.setAttribute("ebsOptimized");
DescribeInstanceAttributeResponseType describeInstanceAttributeResponseType1 = AsyncRequests.sendSync(configuration, describeInstanceAttributeType1);
if (!Objects.equals(describeInstanceAttributeResponseType1.getEbsOptimized(), newAction.properties.getEbsOptimized())) {
if (newAction.properties.getEbsOptimized() != null) {
ModifyInstanceAttributeType modifyInstanceAttributeType = MessageHelper.createMessage(ModifyInstanceAttributeType.class, newAction.info.getEffectiveUserId());
modifyInstanceAttributeType.setInstanceId(newAction.info.getPhysicalResourceId());
modifyInstanceAttributeType.setEbsOptimized(convertToAttributeBooleanFlatValueType(newAction.properties.getEbsOptimized()));
AsyncRequests.sendSync(configuration, modifyInstanceAttributeType);
} else {
ModifyInstanceAttributeType modifyInstanceAttributeType = MessageHelper.createMessage(ModifyInstanceAttributeType.class, newAction.info.getEffectiveUserId());
modifyInstanceAttributeType.setInstanceId(newAction.info.getPhysicalResourceId());
modifyInstanceAttributeType.setEbsOptimized(convertToAttributeBooleanFlatValueType(false));
AsyncRequests.sendSync(configuration, modifyInstanceAttributeType);
}
}
if (!Objects.equals(runningInstancesItemType.getInstanceType(), newAction.properties.getInstanceType())) {
if (newAction.properties.getInstanceType() != null) {
ModifyInstanceAttributeType modifyInstanceAttributeType = MessageHelper.createMessage(ModifyInstanceAttributeType.class, newAction.info.getEffectiveUserId());
modifyInstanceAttributeType.setInstanceId(newAction.info.getPhysicalResourceId());
modifyInstanceAttributeType.setInstanceType(convertToAttributeValueType(newAction.properties.getInstanceType()));
AsyncRequests.sendSync(configuration, modifyInstanceAttributeType);
} else {
ModifyInstanceAttributeType modifyInstanceAttributeType = MessageHelper.createMessage(ModifyInstanceAttributeType.class, newAction.info.getEffectiveUserId());
modifyInstanceAttributeType.setInstanceId(newAction.info.getPhysicalResourceId());
modifyInstanceAttributeType.setInstanceType(convertToAttributeValueType("m1.small"));
AsyncRequests.sendSync(configuration, modifyInstanceAttributeType);
}
}
if (!Objects.equals(runningInstancesItemType.getKernel(), newAction.properties.getKernelId())) {
if (newAction.properties.getKernelId() != null) {
ModifyInstanceAttributeType modifyInstanceAttributeType = MessageHelper.createMessage(ModifyInstanceAttributeType.class, newAction.info.getEffectiveUserId());
modifyInstanceAttributeType.setInstanceId(newAction.info.getPhysicalResourceId());
modifyInstanceAttributeType.setKernel(convertToAttributeValueType(newAction.properties.getKernelId()));
AsyncRequests.sendSync(configuration, modifyInstanceAttributeType);
} else {
resetInstanceAttribute(configuration, newAction, "kernel");
}
}
if (!Objects.equals(runningInstancesItemType.getRamdisk(), newAction.properties.getRamdiskId())) {
if (newAction.properties.getRamdiskId() != null) {
ModifyInstanceAttributeType modifyInstanceAttributeType = MessageHelper.createMessage(ModifyInstanceAttributeType.class, newAction.info.getEffectiveUserId());
modifyInstanceAttributeType.setInstanceId(newAction.info.getPhysicalResourceId());
modifyInstanceAttributeType.setRamdisk(convertToAttributeValueType(newAction.properties.getRamdiskId()));
AsyncRequests.sendSync(configuration, modifyInstanceAttributeType);
} else {
resetInstanceAttribute(configuration, newAction, "ramdisk");
}
}
DescribeInstanceAttributeType describeInstanceAttributeType2 = MessageHelper.createMessage(DescribeInstanceAttributeType.class, newAction.info.getEffectiveUserId());
describeInstanceAttributeType2.setInstanceId(newAction.info.getPhysicalResourceId());
describeInstanceAttributeType2.setAttribute("userData");
DescribeInstanceAttributeResponseType describeInstanceAttributeResponseType2 = AsyncRequests.sendSync(configuration, describeInstanceAttributeType2);
// Null user data comes back as an empty string in describeInstanceAttributes.
if (!Objects.equals(Strings.nullToEmpty(describeInstanceAttributeResponseType2.getUserData()), Strings.nullToEmpty(newAction.properties.getUserData()))) {
if (newAction.properties.getUserData() != null) {
ModifyInstanceAttributeType modifyInstanceAttributeType = MessageHelper.createMessage(ModifyInstanceAttributeType.class, newAction.info.getEffectiveUserId());
modifyInstanceAttributeType.setInstanceId(newAction.info.getPhysicalResourceId());
modifyInstanceAttributeType.setUserData(convertToAttributeValueType(newAction.properties.getUserData()));
AsyncRequests.sendSync(configuration, modifyInstanceAttributeType);
} else {
ModifyInstanceAttributeType modifyInstanceAttributeType = MessageHelper.createMessage(ModifyInstanceAttributeType.class, newAction.info.getEffectiveUserId());
modifyInstanceAttributeType.setInstanceId(newAction.info.getPhysicalResourceId());
modifyInstanceAttributeType.setUserData(convertToAttributeValueType(""));
AsyncRequests.sendSync(configuration, modifyInstanceAttributeType);
}
}
return newAction;
}
},
START_INSTANCE {
@Override
public ResourceAction perform(ResourceAction oldResourceAction, ResourceAction newResourceAction) throws Exception {
AWSEC2InstanceResourceAction oldAction = (AWSEC2InstanceResourceAction) oldResourceAction;
AWSEC2InstanceResourceAction newAction = (AWSEC2InstanceResourceAction) newResourceAction;
ServiceConfiguration configuration = Topology.lookup(Compute.class);
StartInstancesType startInstancesType = MessageHelper.createMessage(StartInstancesType.class, newAction.info.getEffectiveUserId());
startInstancesType.setInstancesSet(Lists.newArrayList(newAction.info.getPhysicalResourceId()));
startInstancesType.setAdditionalInfo(newAction.properties.getAdditionalInfo());
AsyncRequests.sendSync(configuration, startInstancesType);
return newAction;
}
},
WAIT_UNTIL_RUNNING {
@Override
public ResourceAction perform(ResourceAction oldResourceAction, ResourceAction newResourceAction) throws Exception {
return CreateSteps.WAIT_UNTIL_RUNNING.perform(newResourceAction);
}
@Override
public Integer getTimeout() {
return INSTANCE_RUNNING_MAX_UPDATE_RETRY_SECS;
}
};
@Nullable
@Override
public Integer getTimeout() {
return null;
}
};
private static AttributeBooleanFlatValueType convertToAttributeBooleanFlatValueType(Boolean value) {
AttributeBooleanFlatValueType attributeBooleanFlatValueType = new AttributeBooleanFlatValueType();
attributeBooleanFlatValueType.setValue(value);
return attributeBooleanFlatValueType;
}
private static void resetInstanceAttribute(ServiceConfiguration configuration, AWSEC2InstanceResourceAction action, String attribute) throws Exception {
ResetInstanceAttributeType resetInstanceAttributeType = MessageHelper.createMessage(ResetInstanceAttributeType.class, action.info.getEffectiveUserId());
resetInstanceAttributeType.setInstanceId(action.info.getPhysicalResourceId());
resetInstanceAttributeType.setAttribute(attribute);
AsyncRequests.sendSync(configuration, resetInstanceAttributeType);
}
private static Map<String, String> getDetachingVolumeDeviceMap(Map<String, String> oldVolumeDeviceMap, Map<String, String> newVolumeDeviceMap) {
Map<String, String> detachingVolumeDeviceMap = Maps.newHashMap();
for (String volumeId: oldVolumeDeviceMap.keySet()) {
if (!newVolumeDeviceMap.containsKey(volumeId) || !Objects.equals(newVolumeDeviceMap.get(volumeId), oldVolumeDeviceMap.get(volumeId))) {
detachingVolumeDeviceMap.put(volumeId, oldVolumeDeviceMap.get(volumeId));
}
}
return detachingVolumeDeviceMap;
}
private static Map<String,String> getAttachingVolumeDeviceMap(Map<String, String> oldVolumeDeviceMap, Map<String, String> newVolumeDeviceMap) {
Map<String, String> attachingVolumeDeviceMap = Maps.newHashMap();
for (String volumeId: newVolumeDeviceMap.keySet()) {
if (!oldVolumeDeviceMap.containsKey(volumeId) || !Objects.equals(newVolumeDeviceMap.get(volumeId), oldVolumeDeviceMap.get(volumeId))) {
attachingVolumeDeviceMap.put(volumeId, newVolumeDeviceMap.get(volumeId));
}
}
return attachingVolumeDeviceMap;
}
private static Map<String,String> getVolumeDeviceMap(AWSEC2InstanceResourceAction oldAction) {
Map<String, String> volumeDeviceMap = Maps.newHashMap();
if (oldAction.properties.getVolumes() != null) {
for (EC2MountPoint volume : oldAction.properties.getVolumes()) {
volumeDeviceMap.put(volume.getVolumeId(), volume.getDevice());
}
}
return volumeDeviceMap;
}
private static boolean groupIdsEquals(ArrayList<GroupItemType> groupSet, List<String> securityGroupIds) {
Set<String> set1 = Sets.newHashSet();
if (groupSet != null) {
for (GroupItemType groupItemType: groupSet) {
set1.add(groupItemType.getGroupId());
}
}
Set<String> set2 = Sets.newHashSet();
if (securityGroupIds != null) {
set2.addAll(securityGroupIds);
}
return Objects.equals(set1, set2);
}
private static GroupIdSetType convertToGroupIdSet(List<String> securityGroupIds) {
GroupIdSetType groupIdSetType = new GroupIdSetType();
for (String securityGroupId: securityGroupIds) {
SecurityGroupIdSetItemType securityGroupIdSetItemType = new SecurityGroupIdSetItemType();
securityGroupIdSetItemType.setGroupId(securityGroupId);
groupIdSetType.getItem().add(securityGroupIdSetItemType);
}
return groupIdSetType;
}
private static InstanceBlockDeviceMappingSetType convertToBlockMappingWithDeleteOnTerminateValues(List<EC2BlockDeviceMapping> blockDeviceMappings) {
InstanceBlockDeviceMappingSetType instanceBlockDeviceMappingSetType = new InstanceBlockDeviceMappingSetType();
for (EC2BlockDeviceMapping blockDeviceMapping: blockDeviceMappings) {
if (blockDeviceMapping.getEbs() == null) continue;
InstanceBlockDeviceMappingItemType instanceBlockDeviceMappingItemType = new InstanceBlockDeviceMappingItemType();
instanceBlockDeviceMappingItemType.setDeviceName(blockDeviceMapping.getDeviceName());
InstanceEbsBlockDeviceType ebs = new InstanceEbsBlockDeviceType();
ebs.setDeleteOnTermination(blockDeviceMapping.getEbs().getDeleteOnTermination() == null ? Boolean.TRUE : blockDeviceMapping.getEbs().getDeleteOnTermination());
instanceBlockDeviceMappingItemType.setEbs(ebs);
instanceBlockDeviceMappingSetType.getItem().add(instanceBlockDeviceMappingItemType);
}
return instanceBlockDeviceMappingSetType;
}
private ArrayList<BlockDeviceMappingItemType> convertBlockDeviceMappings(List<EC2BlockDeviceMapping> blockDeviceMappings) {
if (blockDeviceMappings == null) return null;
ArrayList<BlockDeviceMappingItemType> blockDeviceMappingItemTypes = Lists.newArrayList();
for (EC2BlockDeviceMapping ec2BlockDeviceMapping : blockDeviceMappings) {
BlockDeviceMappingItemType itemType = new BlockDeviceMappingItemType();
itemType.setDeviceName(ec2BlockDeviceMapping.getDeviceName());
itemType.setNoDevice(ec2BlockDeviceMapping.getNoDevice() != null ? Boolean.TRUE : null);
itemType.setVirtualName(ec2BlockDeviceMapping.getVirtualName());
if (ec2BlockDeviceMapping.getEbs() != null) {
EbsDeviceMapping ebs = new EbsDeviceMapping();
ebs.setDeleteOnTermination(ec2BlockDeviceMapping.getEbs().getDeleteOnTermination() == null ? Boolean.TRUE : ec2BlockDeviceMapping.getEbs().getDeleteOnTermination());
ebs.setIops(ec2BlockDeviceMapping.getEbs().getIops());
ebs.setSnapshotId(ec2BlockDeviceMapping.getEbs().getSnapshotId());
ebs.setVolumeSize(ec2BlockDeviceMapping.getEbs().getVolumeSize() == null ? null : Integer.valueOf(ec2BlockDeviceMapping.getEbs().getVolumeSize()));
ebs.setVolumeType(ec2BlockDeviceMapping.getEbs().getVolumeType() != null ? ec2BlockDeviceMapping.getEbs().getVolumeType() : "standard");
itemType.setEbs(ebs);
}
blockDeviceMappingItemTypes.add(itemType);
}
return blockDeviceMappingItemTypes;
}
private static boolean blockDeviceMappingsDeleteOnTerminateEquals(ArrayList<InstanceBlockDeviceMapping> blockDevices, List<EC2BlockDeviceMapping> blockDeviceMappings) {
Map<String, Boolean> map1 = Maps.newHashMap();
if (blockDevices != null) {
for (InstanceBlockDeviceMapping blockDevice: blockDevices) {
if (blockDevice.getEbs() != null) {
map1.put(blockDevice.getDeviceName(), blockDevice.getEbs().getDeleteOnTermination() == null ? Boolean.TRUE : blockDevice.getEbs().getDeleteOnTermination());
}
}
}
Map<String, Boolean> map2 = Maps.newHashMap();
if (blockDeviceMappings != null) {
for (EC2BlockDeviceMapping blockDeviceMapping: blockDeviceMappings) {
if (blockDeviceMapping.getEbs() != null) {
map2.put(blockDeviceMapping.getDeviceName(), blockDeviceMapping.getEbs().getDeleteOnTermination() == null ? Boolean.TRUE : blockDeviceMapping.getEbs().getDeleteOnTermination());
}
}
}
return Objects.equals(map1, map2);
}
@Override
public void refreshAttributes() throws Exception {
// This assumes everything is set, propertywise and attributewise
ServiceConfiguration configuration = Topology.lookup(Compute.class);
DescribeInstancesType describeInstancesType = MessageHelper.createMessage(DescribeInstancesType.class, info.getEffectiveUserId());
describeInstancesType.getFilterSet( ).add( Filter.filter( "instance-id", info.getPhysicalResourceId( ) ) );
DescribeInstancesResponseType describeInstancesResponseType = AsyncRequests.sendSync( configuration, describeInstancesType );
if (describeInstancesResponseType.getReservationSet().size() == 0) {
return;
}
RunningInstancesItemType runningInstancesItemType = describeInstancesResponseType.getReservationSet().get(0).getInstancesSet().get(0);
info.setPrivateIp(JsonHelper.getStringFromJsonNode(new TextNode(runningInstancesItemType.getPrivateIpAddress())));
info.setPublicIp(JsonHelper.getStringFromJsonNode(new TextNode(runningInstancesItemType.getIpAddress())));
info.setAvailabilityZone(JsonHelper.getStringFromJsonNode(new TextNode(runningInstancesItemType.getPlacement())));
info.setPrivateDnsName(JsonHelper.getStringFromJsonNode(new TextNode(runningInstancesItemType.getPrivateDnsName())));
info.setPublicDnsName(JsonHelper.getStringFromJsonNode(new TextNode(runningInstancesItemType.getDnsName())));
info.setReferenceValueJson(JsonHelper.getStringFromJsonNode(new TextNode(info.getPhysicalResourceId())));
}
private static AttributeValueType convertToAttributeValueType(String value) {
AttributeValueType attributeValueType = new AttributeValueType();
attributeValueType.setValue(value);
return attributeValueType;
}
private static boolean onlyChangedDeleteOnTerminate(List<EC2BlockDeviceMapping> blockDeviceMappings1, List<EC2BlockDeviceMapping> blockDeviceMappings2) {
if (blockDeviceMappings1 == null || blockDeviceMappings2 == null) return false;
Map<String, EC2BlockDeviceMapping> device1Map = Maps.newHashMap();
for (EC2BlockDeviceMapping ec2BlockDeviceMapping1: blockDeviceMappings1) {
device1Map.put(ec2BlockDeviceMapping1.getDeviceName(), ec2BlockDeviceMapping1);
}
for (EC2BlockDeviceMapping ec2BlockDeviceMapping2: blockDeviceMappings2) {
if (!device1Map.containsKey(ec2BlockDeviceMapping2.getDeviceName())) {
return false;
} else {
// only delete on terminate can be different
EC2BlockDeviceMapping ec2BlockDeviceMapping1 = device1Map.get(ec2BlockDeviceMapping2.getDeviceName());
// already checked device names
if (!Objects.equals(ec2BlockDeviceMapping1.getNoDevice(), ec2BlockDeviceMapping2.getNoDevice())) {
return false;
}
if (!Objects.equals(ec2BlockDeviceMapping1.getVirtualName(), ec2BlockDeviceMapping2.getVirtualName())) {
return false;
}
EC2EBSBlockDevice ebs1 = ec2BlockDeviceMapping1.getEbs();
EC2EBSBlockDevice ebs2 = ec2BlockDeviceMapping2.getEbs();
if (ebs1 != null && ebs2 == null) {
return false;
}
if (ebs1 == null && ebs2 != null) {
return false;
}
if (ebs1 == null && ebs2 == null) {
continue;
}
if (!Objects.equals(ebs1.getIops(), ebs2.getIops())) {
return false;
}
if (!Objects.equals(ebs1.getSnapshotId(), ebs2.getSnapshotId())) {
return false;
}
if (!Objects.equals(ebs1.getVolumeSize(), ebs2.getVolumeSize())) {
return false;
}
if (!Objects.equals(ebs1.getVolumeType(), ebs2.getVolumeType())) {
return false;
}
}
device1Map.remove(ec2BlockDeviceMapping2.getDeviceName());
}
if (!device1Map.isEmpty()) {
return false;
}
return true;
}
private static AttributeBooleanValueType convertToAttributeBooleanValueType(Boolean value) {
AttributeBooleanValueType attributeBooleanValueType = new AttributeBooleanValueType();
attributeBooleanValueType.setValue(value);
return attributeBooleanValueType;
}
private InstanceNetworkInterfaceSetRequestType convertNetworkInterfaceSet(List<EC2NetworkInterface> networkInterfaces) {
InstanceNetworkInterfaceSetRequestType instanceNetworkInterfaceSetRequestType = new InstanceNetworkInterfaceSetRequestType();
ArrayList<InstanceNetworkInterfaceSetItemRequestType> item = Lists.newArrayList();
for (EC2NetworkInterface networkInterface: networkInterfaces) {
InstanceNetworkInterfaceSetItemRequestType instanceNetworkInterfaceSetItemRequestType = new InstanceNetworkInterfaceSetItemRequestType();
if (networkInterface.getNetworkInterfaceId() != null) {
instanceNetworkInterfaceSetItemRequestType.setNetworkInterfaceId(networkInterface.getNetworkInterfaceId());
}
if (networkInterface.getDeviceIndex() != null) {
instanceNetworkInterfaceSetItemRequestType.setDeviceIndex(networkInterface.getDeviceIndex());
}
if (networkInterface.getDeleteOnTermination() != null) {
instanceNetworkInterfaceSetItemRequestType.setDeleteOnTermination(networkInterface.getDeleteOnTermination());
}
if (networkInterface.getAssociatePublicIpAddress() != null) {
instanceNetworkInterfaceSetItemRequestType.setAssociatePublicIpAddress(networkInterface.getAssociatePublicIpAddress());
}
if (networkInterface.getDescription() != null) {
instanceNetworkInterfaceSetItemRequestType.setDescription(networkInterface.getDescription());
}
if (networkInterface.getGroupSet() != null) {
instanceNetworkInterfaceSetItemRequestType.setGroupSet(convertGroupSet(networkInterface.getGroupSet()));
}
if (networkInterface.getPrivateIpAddress() != null) {
instanceNetworkInterfaceSetItemRequestType.setPrivateIpAddress(networkInterface.getPrivateIpAddress());
}
if (networkInterface.getPrivateIpAddresses() != null) {
instanceNetworkInterfaceSetItemRequestType.setPrivateIpAddressesSet(convertPrivateIpAddressSet(networkInterface.getPrivateIpAddresses()));
}
if (networkInterface.getSecondaryPrivateIpAddressCount() != null) {
instanceNetworkInterfaceSetItemRequestType.setSecondaryPrivateIpAddressCount(networkInterface.getSecondaryPrivateIpAddressCount());
}
if (networkInterface.getSubnetId() != null) {
instanceNetworkInterfaceSetItemRequestType.setSubnetId(networkInterface.getSubnetId());
}
item.add(instanceNetworkInterfaceSetItemRequestType);
}
instanceNetworkInterfaceSetRequestType.setItem(item);
return instanceNetworkInterfaceSetRequestType;
}
private PrivateIpAddressesSetRequestType convertPrivateIpAddressSet(List<EC2NetworkInterfacePrivateIPSpecification> privateIpAddresses) {
if (privateIpAddresses == null) return null;
PrivateIpAddressesSetRequestType privateIpAddressesSetRequestType = new PrivateIpAddressesSetRequestType();
ArrayList<PrivateIpAddressesSetItemRequestType> item = Lists.newArrayList();
for (EC2NetworkInterfacePrivateIPSpecification ec2NetworkInterfacePrivateIPSpecification: privateIpAddresses) {
PrivateIpAddressesSetItemRequestType privateIpAddressesSetItemRequestType = new PrivateIpAddressesSetItemRequestType();
if (ec2NetworkInterfacePrivateIPSpecification.getPrimary() != null) {
privateIpAddressesSetItemRequestType.setPrimary(ec2NetworkInterfacePrivateIPSpecification.getPrimary());
}
if (ec2NetworkInterfacePrivateIPSpecification.getPrivateIpAddress() != null){
privateIpAddressesSetItemRequestType.setPrivateIpAddress(ec2NetworkInterfacePrivateIPSpecification.getPrivateIpAddress());
}
item.add(privateIpAddressesSetItemRequestType);
}
privateIpAddressesSetRequestType.setItem(item);
return privateIpAddressesSetRequestType;
}
private SecurityGroupIdSetType convertGroupSet(List<String> groupSet) {
if (groupSet == null) return null;
SecurityGroupIdSetType securityGroupIdSetType = new SecurityGroupIdSetType();
ArrayList<SecurityGroupIdSetItemType> item = Lists.newArrayList();
for (String groupId: groupSet) {
SecurityGroupIdSetItemType securityGroupIdSetItemType = new SecurityGroupIdSetItemType();
securityGroupIdSetItemType.setGroupId(groupId);
item.add(securityGroupIdSetItemType);
}
securityGroupIdSetType.setItem(item);
return securityGroupIdSetType;
}
private static List<String> defaultSecurityGroupInVpcIfNullOrEmpty(ServiceConfiguration configuration, String vpcId, String effectiveUserId, List<String> groupIds) throws Exception {
if (groupIds != null && !groupIds.isEmpty()) return groupIds;
DescribeSecurityGroupsType describeSecurityGroupsType = MessageHelper.createMessage(DescribeSecurityGroupsType.class, effectiveUserId);
describeSecurityGroupsType.getFilterSet().add(Filter.filter("vpc-id", vpcId));
describeSecurityGroupsType.getFilterSet().add(Filter.filter("group-name", "default"));
DescribeSecurityGroupsResponseType describeSecurityGroupsResponseType = AsyncRequests.sendSync(configuration, describeSecurityGroupsType);
if (describeSecurityGroupsResponseType == null || describeSecurityGroupsResponseType.getSecurityGroupInfo() == null ||
describeSecurityGroupsResponseType.getSecurityGroupInfo().size() != 1) {
throw new ValidationErrorException("Could not find unique default security group for vpc " + vpcId);
}
return Lists.newArrayList(describeSecurityGroupsResponseType.getSecurityGroupInfo().get(0).getGroupId());
}
@Override
public ResourceProperties getResourceProperties() {
return properties;
}
@Override
public void setResourceProperties(ResourceProperties resourceProperties) {
properties = (AWSEC2InstanceProperties) resourceProperties;
}
@Override
public ResourceInfo getResourceInfo() {
return info;
}
@Override
public void setResourceInfo(ResourceInfo resourceInfo) {
info = (AWSEC2InstanceResourceInfo) resourceInfo;
}
}