/*************************************************************************
* 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.resources.EC2Helper;
import com.eucalyptus.cloudformation.resources.IpPermissionTypeWithEquals;
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.AWSEC2SecurityGroupResourceInfo;
import com.eucalyptus.cloudformation.resources.standard.propertytypes.AWSEC2SecurityGroupProperties;
import com.eucalyptus.cloudformation.resources.standard.propertytypes.EC2SecurityGroupRule;
import com.eucalyptus.cloudformation.resources.standard.propertytypes.EC2Tag;
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.steps.UpdateStep;
import com.eucalyptus.cloudformation.workflow.updateinfo.UpdateType;
import com.eucalyptus.component.ServiceConfiguration;
import com.eucalyptus.component.Topology;
import com.eucalyptus.compute.common.AuthorizeSecurityGroupEgressResponseType;
import com.eucalyptus.compute.common.AuthorizeSecurityGroupEgressType;
import com.eucalyptus.compute.common.AuthorizeSecurityGroupIngressResponseType;
import com.eucalyptus.compute.common.AuthorizeSecurityGroupIngressType;
import com.eucalyptus.compute.common.Compute;
import com.eucalyptus.compute.common.CreateSecurityGroupResponseType;
import com.eucalyptus.compute.common.CreateSecurityGroupType;
import com.eucalyptus.compute.common.CreateTagsResponseType;
import com.eucalyptus.compute.common.CreateTagsType;
import com.eucalyptus.compute.common.DeleteSecurityGroupResponseType;
import com.eucalyptus.compute.common.DeleteSecurityGroupType;
import com.eucalyptus.compute.common.DeleteTagsResponseType;
import com.eucalyptus.compute.common.DeleteTagsType;
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.Filter;
import com.eucalyptus.compute.common.IpPermissionType;
import com.eucalyptus.compute.common.RevokeSecurityGroupEgressResponseType;
import com.eucalyptus.compute.common.RevokeSecurityGroupEgressType;
import com.eucalyptus.compute.common.RevokeSecurityGroupIngressResponseType;
import com.eucalyptus.compute.common.RevokeSecurityGroupIngressType;
import com.eucalyptus.compute.common.SecurityGroupItemType;
import com.eucalyptus.compute.common.TagInfo;
import com.eucalyptus.compute.common.UserIdGroupPairType;
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.base.Throwables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import org.apache.log4j.Logger;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
/**
* Created by ethomas on 2/3/14.
*/
@ConfigurableClass( root = "cloudformation", description = "Parameters controlling cloud formation")
public class AWSEC2SecurityGroupResourceAction extends StepBasedResourceAction {
@ConfigurableField(initial = "300", description = "The amount of time (in seconds) to retry security group deletes (may fail if instances from autoscaling group)")
public static volatile Integer SECURITY_GROUP_MAX_DELETE_RETRY_SECS = 300;
private static final Logger LOG = Logger.getLogger(AWSEC2SecurityGroupResourceAction.class);
private AWSEC2SecurityGroupProperties properties = new AWSEC2SecurityGroupProperties();
private AWSEC2SecurityGroupResourceInfo info = new AWSEC2SecurityGroupResourceInfo();
public AWSEC2SecurityGroupResourceAction() {
super(fromEnum(CreateSteps.class), fromEnum(DeleteSteps.class), fromUpdateEnum(UpdateNoInterruptionSteps.class), null);
}
@Override
public UpdateType getUpdateType(ResourceAction resourceAction, boolean stackTagsChanged) {
UpdateType updateType = info.supportsTags() && stackTagsChanged ? UpdateType.NO_INTERRUPTION : UpdateType.NONE;
AWSEC2SecurityGroupResourceAction otherAction = (AWSEC2SecurityGroupResourceAction) resourceAction;
if (!Objects.equals(properties.getGroupDescription(), otherAction.properties.getGroupDescription())) {
updateType = UpdateType.max(updateType, UpdateType.NEEDS_REPLACEMENT);
}
if (!Objects.equals(properties.getSecurityGroupEgress(), otherAction.properties.getSecurityGroupEgress())) {
updateType = UpdateType.max(updateType, UpdateType.NO_INTERRUPTION);
}
if (!Objects.equals(properties.getSecurityGroupIngress(), otherAction.properties.getSecurityGroupIngress())) {
updateType = UpdateType.max(updateType, UpdateType.NO_INTERRUPTION);
}
if (!Objects.equals(properties.getTags(), otherAction.properties.getTags())) {
updateType = UpdateType.max(updateType, UpdateType.NO_INTERRUPTION);
}
if (!Objects.equals(properties.getVpcId(), otherAction.properties.getVpcId())) {
updateType = UpdateType.max(updateType, UpdateType.NEEDS_REPLACEMENT);
}
return updateType;
}
@Override
public ResourceProperties getResourceProperties() {
return properties;
}
@Override
public void setResourceProperties(ResourceProperties resourceProperties) {
properties = (AWSEC2SecurityGroupProperties) resourceProperties;
}
@Override
public ResourceInfo getResourceInfo() {
return info;
}
@Override
public void setResourceInfo(ResourceInfo resourceInfo) {
info = (AWSEC2SecurityGroupResourceInfo) resourceInfo;
}
private enum CreateSteps implements Step {
CREATE_GROUP {
@Override
public ResourceAction perform(ResourceAction resourceAction) throws Exception {
AWSEC2SecurityGroupResourceAction action = (AWSEC2SecurityGroupResourceAction) resourceAction;
ServiceConfiguration configuration = Topology.lookup(Compute.class);
CreateSecurityGroupType createSecurityGroupType = MessageHelper.createMessage(CreateSecurityGroupType.class, action.info.getEffectiveUserId());
if (!Strings.isNullOrEmpty(action.properties.getGroupDescription())) {
createSecurityGroupType.setGroupDescription(action.properties.getGroupDescription());
}
if (!Strings.isNullOrEmpty(action.properties.getVpcId())) {
createSecurityGroupType.setVpcId(action.properties.getVpcId());
}
String groupName = action.getDefaultPhysicalResourceId();
createSecurityGroupType.setGroupName(groupName);
CreateSecurityGroupResponseType createSecurityGroupResponseType = AsyncRequests.<CreateSecurityGroupType,CreateSecurityGroupResponseType> sendSync(configuration, createSecurityGroupType);
String groupId = createSecurityGroupResponseType.getGroupId();
if (!Strings.isNullOrEmpty(action.properties.getVpcId())) {
action.info.setPhysicalResourceId(groupId);
} else {
action.info.setPhysicalResourceId(groupName);
}
action.info.setCreatedEnoughToDelete(true);
action.info.setReferenceValueJson(JsonHelper.getStringFromJsonNode(new TextNode(action.info.getPhysicalResourceId())));
action.info.setGroupId(JsonHelper.getStringFromJsonNode(new TextNode(groupId)));
return action;
}
},
CREATE_TAGS {
@Override
public ResourceAction perform(ResourceAction resourceAction) throws Exception {
AWSEC2SecurityGroupResourceAction action = (AWSEC2SecurityGroupResourceAction) 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(JsonHelper.getJsonNodeFromString(action.info.getGroupId()).asText()));
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(JsonHelper.getJsonNodeFromString(action.info.getGroupId()).asText()));
createTagsType.setTagSet(EC2Helper.createTagSet(tags));
AsyncRequests.<CreateTagsType, CreateTagsResponseType>sendSync(configuration, createTagsType);
}
return action;
}
},
CREATE_INGRESS_RULES {
@Override
public ResourceAction perform(ResourceAction resourceAction) throws Exception {
AWSEC2SecurityGroupResourceAction action = (AWSEC2SecurityGroupResourceAction) resourceAction;
ServiceConfiguration configuration = Topology.lookup(Compute.class);
if (action.properties.getSecurityGroupIngress() != null && !action.properties.getSecurityGroupIngress().isEmpty()) {
for (EC2SecurityGroupRule ec2SecurityGroupRule : action.properties.getSecurityGroupIngress()) {
AuthorizeSecurityGroupIngressType authorizeSecurityGroupIngressType = MessageHelper.createMessage(AuthorizeSecurityGroupIngressType.class, action.info.getEffectiveUserId());
authorizeSecurityGroupIngressType.setGroupId(JsonHelper.getJsonNodeFromString(action.info.getGroupId()).asText());
IpPermissionType ipPermissionType = getIpPermissionTypeForIngress(action, ec2SecurityGroupRule);
authorizeSecurityGroupIngressType.setIpPermissions(Lists.newArrayList(ipPermissionType));
AuthorizeSecurityGroupIngressResponseType authorizeSecurityGroupIngressResponseType = AsyncRequests.<AuthorizeSecurityGroupIngressType, AuthorizeSecurityGroupIngressResponseType> sendSync(configuration, authorizeSecurityGroupIngressType);
}
}
return action;
}
},
CREATE_EGRESS_RULES {
@Override
public ResourceAction perform(ResourceAction resourceAction) throws Exception {
AWSEC2SecurityGroupResourceAction action = (AWSEC2SecurityGroupResourceAction) resourceAction;
ServiceConfiguration configuration = Topology.lookup(Compute.class);
if (action.properties.getSecurityGroupEgress() != null && !action.properties.getSecurityGroupEgress().isEmpty()) {
// revoke default
RevokeSecurityGroupEgressType revokeSecurityGroupEgressType = MessageHelper.createMessage(RevokeSecurityGroupEgressType.class, action.info.getEffectiveUserId());
revokeSecurityGroupEgressType.setGroupId(JsonHelper.getJsonNodeFromString(action.info.getGroupId()).asText());
revokeSecurityGroupEgressType.setIpPermissions(Lists.newArrayList(DEFAULT_EGRESS_RULE()));
RevokeSecurityGroupEgressResponseType revokeSecurityGroupEgressResponseType = AsyncRequests.<RevokeSecurityGroupEgressType, RevokeSecurityGroupEgressResponseType> sendSync(configuration, revokeSecurityGroupEgressType);
for (EC2SecurityGroupRule ec2SecurityGroupRule : action.properties.getSecurityGroupEgress()) {
AuthorizeSecurityGroupEgressType authorizeSecurityGroupEgressType = MessageHelper.createMessage(AuthorizeSecurityGroupEgressType.class, action.info.getEffectiveUserId());
authorizeSecurityGroupEgressType.setGroupId(JsonHelper.getJsonNodeFromString(action.info.getGroupId()).asText());
IpPermissionType ipPermissionType = getIpPermissionTypeForEgress(ec2SecurityGroupRule);
authorizeSecurityGroupEgressType.setIpPermissions(Lists.newArrayList(ipPermissionType));
AuthorizeSecurityGroupEgressResponseType authorizeSecurityGroupEgressResponseType = AsyncRequests.<AuthorizeSecurityGroupEgressType, AuthorizeSecurityGroupEgressResponseType> sendSync(configuration, authorizeSecurityGroupEgressType);
}
}
return action;
}
};
@Nullable
@Override
public Integer getTimeout() {
return null;
}
}
private static IpPermissionType getIpPermissionTypeForEgress(EC2SecurityGroupRule ec2SecurityGroupRule) throws ValidationErrorException {
// Can't specify cidr and Destination security group
if (!Strings.isNullOrEmpty(ec2SecurityGroupRule.getCidrIp()) && !Strings.isNullOrEmpty(ec2SecurityGroupRule.getDestinationSecurityGroupId())) {
throw new ValidationErrorException("Both CidrIp and DestinationSecurityGroup cannot be specified in SecurityGroupEgress");
}
IpPermissionType ipPermissionType = new IpPermissionType(
ec2SecurityGroupRule.getIpProtocol(),
ec2SecurityGroupRule.getFromPort(),
ec2SecurityGroupRule.getToPort()
);
if (!Strings.isNullOrEmpty(ec2SecurityGroupRule.getCidrIp())) {
ipPermissionType.setCidrIpRanges(Lists.newArrayList(ec2SecurityGroupRule.getCidrIp()));
}
if (!Strings.isNullOrEmpty(ec2SecurityGroupRule.getDestinationSecurityGroupId())) {
ipPermissionType.setGroups(Lists.newArrayList(new UserIdGroupPairType(null, null, ec2SecurityGroupRule.getDestinationSecurityGroupId())));
}
return ipPermissionType;
}
private static IpPermissionType getIpPermissionTypeForIngress(AWSEC2SecurityGroupResourceAction action, EC2SecurityGroupRule ec2SecurityGroupRule) throws ValidationErrorException {
// Can't specify cidr and source security group
if (!Strings.isNullOrEmpty(ec2SecurityGroupRule.getCidrIp()) &&
(!Strings.isNullOrEmpty(ec2SecurityGroupRule.getSourceSecurityGroupId())
|| !Strings.isNullOrEmpty(ec2SecurityGroupRule.getSourceSecurityGroupName())
|| !Strings.isNullOrEmpty(ec2SecurityGroupRule.getSourceSecurityGroupOwnerId()))) {
throw new ValidationErrorException("Both CidrIp and SourceSecurityGroup cannot be specified in SecurityGroupIngress");
}
// Can't specify both source security group name and id
if (!Strings.isNullOrEmpty(ec2SecurityGroupRule.getSourceSecurityGroupId()) &&
!Strings.isNullOrEmpty(ec2SecurityGroupRule.getSourceSecurityGroupName())) {
throw new ValidationErrorException("Both SourceSecurityGroupName and SourceSecurityGroupId cannot be specified in SecurityGroupIngress");
}
IpPermissionType ipPermissionType = new IpPermissionType(
ec2SecurityGroupRule.getIpProtocol(),
ec2SecurityGroupRule.getFromPort(),
ec2SecurityGroupRule.getToPort()
);
if (!Strings.isNullOrEmpty(ec2SecurityGroupRule.getCidrIp())) {
ipPermissionType.setCidrIpRanges(Lists.newArrayList(ec2SecurityGroupRule.getCidrIp()));
}
if (!Strings.isNullOrEmpty(ec2SecurityGroupRule.getSourceSecurityGroupId())) {
// Generally no need for SourceSecurityGroupOwnerId if SourceSecurityGroupId is set, but pass it along if set
ipPermissionType.setGroups(Lists.newArrayList(new UserIdGroupPairType(ec2SecurityGroupRule.getSourceSecurityGroupOwnerId(), null, ec2SecurityGroupRule.getSourceSecurityGroupId())));
}
if (!Strings.isNullOrEmpty(ec2SecurityGroupRule.getSourceSecurityGroupName())) {
// I think SourceSecurityGroupOwnerId is needed here. If not provided, use the local account id
String sourceSecurityGroupOwnerId = ec2SecurityGroupRule.getSourceSecurityGroupOwnerId();
if (Strings.isNullOrEmpty(sourceSecurityGroupOwnerId)) {
sourceSecurityGroupOwnerId = action.getStackEntity().getAccountId();
}
ipPermissionType.setGroups(Lists.newArrayList(new UserIdGroupPairType(sourceSecurityGroupOwnerId, ec2SecurityGroupRule.getSourceSecurityGroupName(), null)));
}
return ipPermissionType;
}
private enum DeleteSteps implements Step {
DELETE_GROUP {
@Override
public ResourceAction perform(ResourceAction resourceAction) throws Exception {
AWSEC2SecurityGroupResourceAction action = (AWSEC2SecurityGroupResourceAction) resourceAction;
ServiceConfiguration configuration = Topology.lookup(Compute.class);
// See if group was ever populated
if (!Boolean.TRUE.equals(action.info.getCreatedEnoughToDelete())) return action;
// See if group exists now
String groupId = JsonHelper.getJsonNodeFromString(action.info.getGroupId()).asText();
DescribeSecurityGroupsType describeSecurityGroupsType = MessageHelper.createMessage(DescribeSecurityGroupsType.class, action.info.getEffectiveUserId());
describeSecurityGroupsType.setFilterSet( Lists.newArrayList( Filter.filter( "group-id", groupId ) ) );
DescribeSecurityGroupsResponseType describeSecurityGroupsResponseType = AsyncRequests.sendSync(configuration, describeSecurityGroupsType);
ArrayList<SecurityGroupItemType> securityGroupItemTypeArrayList = describeSecurityGroupsResponseType.getSecurityGroupInfo();
if (securityGroupItemTypeArrayList == null || securityGroupItemTypeArrayList.isEmpty()) {
return action;
}
// Delete the group (may fail)
DeleteSecurityGroupType deleteSecurityGroupType = MessageHelper.createMessage(DeleteSecurityGroupType.class, action.info.getEffectiveUserId());
deleteSecurityGroupType.setGroupId(groupId);
try {
AsyncRequests.<DeleteSecurityGroupType, DeleteSecurityGroupResponseType>sendSync(configuration, deleteSecurityGroupType);
return action;
} catch (Exception ex) {
Throwable cause = Throwables.getRootCause(ex);
throw new RetryAfterConditionCheckFailedException(ex.getMessage());
}
}
@Override
public Integer getTimeout() {
return SECURITY_GROUP_MAX_DELETE_RETRY_SECS;
}
}
}
private enum UpdateNoInterruptionSteps implements UpdateStep {
UPDATE_INGRESS_AND_EGRESS_RULES {
@Override
public ResourceAction perform(ResourceAction oldResourceAction, ResourceAction newResourceAction) throws Exception {
AWSEC2SecurityGroupResourceAction oldAction = (AWSEC2SecurityGroupResourceAction) oldResourceAction;
AWSEC2SecurityGroupResourceAction newAction = (AWSEC2SecurityGroupResourceAction) newResourceAction;
ServiceConfiguration configuration = Topology.lookup(Compute.class);
// first get ingress and egress rules...
String groupId = JsonHelper.getJsonNodeFromString(newAction.info.getGroupId()).asText();
DescribeSecurityGroupsType describeSecurityGroupsType = MessageHelper.createMessage(DescribeSecurityGroupsType.class, newAction.info.getEffectiveUserId());
describeSecurityGroupsType.setFilterSet( Lists.newArrayList( Filter.filter( "group-id", groupId ) ) );
DescribeSecurityGroupsResponseType describeSecurityGroupsResponseType = AsyncRequests.sendSync(configuration, describeSecurityGroupsType);
Set<IpPermissionTypeWithEquals> existingIngressPermissionTypes = Sets.newLinkedHashSet();
Set<IpPermissionTypeWithEquals> existingEgressPermissionTypes = Sets.newLinkedHashSet();
if (describeSecurityGroupsResponseType != null && describeSecurityGroupsResponseType.getSecurityGroupInfo() != null &&
!describeSecurityGroupsResponseType.getSecurityGroupInfo().isEmpty()) {
if (describeSecurityGroupsResponseType.getSecurityGroupInfo().get(0).getIpPermissions() != null) {
existingIngressPermissionTypes.addAll(IpPermissionTypeWithEquals.getNonNullCollection(describeSecurityGroupsResponseType.getSecurityGroupInfo().get(0).getIpPermissions()));
}
if (describeSecurityGroupsResponseType.getSecurityGroupInfo().get(0).getIpPermissionsEgress() != null) {
existingEgressPermissionTypes.addAll(IpPermissionTypeWithEquals.getNonNullCollection(describeSecurityGroupsResponseType.getSecurityGroupInfo().get(0).getIpPermissionsEgress()));
}
}
Set<IpPermissionTypeWithEquals> newIngressPermissionTypes = Sets.newLinkedHashSet();
Set<IpPermissionTypeWithEquals> newEgressPermissionTypes = Sets.newLinkedHashSet();
if (newAction.properties.getSecurityGroupIngress() != null && !newAction.properties.getSecurityGroupIngress().isEmpty()) {
for (EC2SecurityGroupRule ec2SecurityGroupRule : newAction.properties.getSecurityGroupIngress()) {
IpPermissionType ipPermissionType = getIpPermissionTypeForIngress(newAction, ec2SecurityGroupRule);
newIngressPermissionTypes.add(new IpPermissionTypeWithEquals(ipPermissionType));
}
}
if (newAction.properties.getSecurityGroupEgress() != null && !newAction.properties.getSecurityGroupEgress().isEmpty()) {
for (EC2SecurityGroupRule ec2SecurityGroupRule : newAction.properties.getSecurityGroupEgress()) {
IpPermissionType ipPermissionType = getIpPermissionTypeForEgress(ec2SecurityGroupRule);
newEgressPermissionTypes.add(new IpPermissionTypeWithEquals(ipPermissionType));
}
}
Set<IpPermissionTypeWithEquals> oldIngressPermissionTypes = Sets.newLinkedHashSet();
Set<IpPermissionTypeWithEquals> oldEgressPermissionTypes = Sets.newLinkedHashSet();
if (oldAction.properties.getSecurityGroupIngress() != null && !oldAction.properties.getSecurityGroupIngress().isEmpty()) {
for (EC2SecurityGroupRule ec2SecurityGroupRule : oldAction.properties.getSecurityGroupIngress()) {
IpPermissionType ipPermissionType = getIpPermissionTypeForIngress(oldAction, ec2SecurityGroupRule);
oldIngressPermissionTypes.add(new IpPermissionTypeWithEquals(ipPermissionType));
}
}
if (oldAction.properties.getSecurityGroupEgress() != null && !oldAction.properties.getSecurityGroupEgress().isEmpty()) {
for (EC2SecurityGroupRule ec2SecurityGroupRule : oldAction.properties.getSecurityGroupEgress()) {
IpPermissionType ipPermissionType = getIpPermissionTypeForEgress(ec2SecurityGroupRule);
oldEgressPermissionTypes.add(new IpPermissionTypeWithEquals(ipPermissionType));
}
}
// add all new rules that are not in the existing set
for (IpPermissionTypeWithEquals ipPermissionTypeWithEquals : Sets.difference(newIngressPermissionTypes, existingIngressPermissionTypes)) {
AuthorizeSecurityGroupIngressType authorizeSecurityGroupIngressType = MessageHelper.createMessage(AuthorizeSecurityGroupIngressType.class, newAction.info.getEffectiveUserId());
authorizeSecurityGroupIngressType.setGroupId(groupId);
authorizeSecurityGroupIngressType.setIpPermissions(Lists.newArrayList(ipPermissionTypeWithEquals.getIpPermissionType()));
AsyncRequests.<AuthorizeSecurityGroupIngressType, AuthorizeSecurityGroupIngressResponseType> sendSync(configuration, authorizeSecurityGroupIngressType);
}
for (IpPermissionTypeWithEquals ipPermissionTypeWithEquals : Sets.difference(newEgressPermissionTypes, existingEgressPermissionTypes)) {
AuthorizeSecurityGroupEgressType authorizeSecurityGroupEgressType = MessageHelper.createMessage(AuthorizeSecurityGroupEgressType.class, newAction.info.getEffectiveUserId());
authorizeSecurityGroupEgressType.setGroupId(groupId);
authorizeSecurityGroupEgressType.setIpPermissions(Lists.newArrayList(ipPermissionTypeWithEquals.getIpPermissionType()));
AsyncRequests.<AuthorizeSecurityGroupEgressType, AuthorizeSecurityGroupEgressResponseType> sendSync(configuration, authorizeSecurityGroupEgressType);
}
// revoke all rules from the old set that exist and are not new
for (IpPermissionTypeWithEquals ipPermissionTypeWithEquals : Sets.intersection(oldIngressPermissionTypes, Sets.difference(existingIngressPermissionTypes, newIngressPermissionTypes))) {
RevokeSecurityGroupIngressType revokeSecurityGroupIngressType = MessageHelper.createMessage(RevokeSecurityGroupIngressType.class, newAction.info.getEffectiveUserId());
revokeSecurityGroupIngressType.setGroupId(groupId);
revokeSecurityGroupIngressType.setIpPermissions(Lists.newArrayList(ipPermissionTypeWithEquals.getIpPermissionType()));
AsyncRequests.<RevokeSecurityGroupIngressType, RevokeSecurityGroupIngressResponseType> sendSync(configuration, revokeSecurityGroupIngressType);
}
for (IpPermissionTypeWithEquals ipPermissionTypeWithEquals : Sets.intersection(oldEgressPermissionTypes, Sets.difference(existingEgressPermissionTypes, newEgressPermissionTypes))) {
RevokeSecurityGroupEgressType revokeSecurityGroupEgressType = MessageHelper.createMessage(RevokeSecurityGroupEgressType.class, newAction.info.getEffectiveUserId());
revokeSecurityGroupEgressType.setGroupId(groupId);
revokeSecurityGroupEgressType.setIpPermissions(Lists.newArrayList(ipPermissionTypeWithEquals.getIpPermissionType()));
AsyncRequests.<RevokeSecurityGroupEgressType, RevokeSecurityGroupEgressResponseType> sendSync(configuration, revokeSecurityGroupEgressType);
}
return newAction;
}
},
UPDATE_TAGS {
@Override
public ResourceAction perform(ResourceAction oldResourceAction, ResourceAction newResourceAction) throws Exception {
AWSEC2SecurityGroupResourceAction oldAction = (AWSEC2SecurityGroupResourceAction) oldResourceAction;
AWSEC2SecurityGroupResourceAction newAction = (AWSEC2SecurityGroupResourceAction) newResourceAction;
ServiceConfiguration configuration = Topology.lookup(Compute.class);
String groupId = JsonHelper.getJsonNodeFromString(newAction.info.getGroupId()).asText();
DescribeTagsType describeTagsType = MessageHelper.createMessage(DescribeTagsType.class, newAction.info.getEffectiveUserId());
describeTagsType.setFilterSet(Lists.newArrayList(Filter.filter("resource-id", groupId)));
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(groupId));
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(groupId));
deleteTagsType.setTagSet(EC2Helper.deleteTagSet(tagsToRemove));
AsyncRequests.<DeleteTagsType, DeleteTagsResponseType>sendSync(configuration, deleteTagsType);
}
return newAction;
}
};
@Nullable
@Override
public Integer getTimeout() {
return null;
}
}
private static final IpPermissionType DEFAULT_EGRESS_RULE() {
IpPermissionType ipPermissionType = new IpPermissionType();
ipPermissionType.setIpProtocol("-1");
ipPermissionType.setCidrIpRanges(Lists.newArrayList("0.0.0.0/0"));
return ipPermissionType;
}
}