/*************************************************************************
* Copyright 2009-2013 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.euare.AddRoleToInstanceProfileResponseType;
import com.eucalyptus.auth.euare.AddRoleToInstanceProfileType;
import com.eucalyptus.auth.euare.CreateInstanceProfileResponseType;
import com.eucalyptus.auth.euare.CreateInstanceProfileType;
import com.eucalyptus.auth.euare.DeleteInstanceProfileResponseType;
import com.eucalyptus.auth.euare.DeleteInstanceProfileType;
import com.eucalyptus.auth.euare.GetInstanceProfileResponseType;
import com.eucalyptus.auth.euare.GetInstanceProfileType;
import com.eucalyptus.auth.euare.RemoveRoleFromInstanceProfileResponseType;
import com.eucalyptus.auth.euare.RemoveRoleFromInstanceProfileType;
import com.eucalyptus.auth.euare.RoleType;
import com.eucalyptus.cloudformation.ValidationErrorException;
import com.eucalyptus.cloudformation.resources.IAMHelper;
import com.eucalyptus.cloudformation.resources.ResourceAction;
import com.eucalyptus.cloudformation.resources.ResourceInfo;
import com.eucalyptus.cloudformation.resources.ResourceProperties;
import com.eucalyptus.cloudformation.resources.standard.info.AWSIAMInstanceProfileResourceInfo;
import com.eucalyptus.cloudformation.resources.standard.propertytypes.AWSIAMInstanceProfileProperties;
import com.eucalyptus.cloudformation.template.JsonHelper;
import com.eucalyptus.cloudformation.util.MessageHelper;
import com.eucalyptus.cloudformation.workflow.steps.Step;
import com.eucalyptus.cloudformation.workflow.steps.StepBasedResourceAction;
import com.eucalyptus.cloudformation.workflow.steps.UpdateStep;
import com.eucalyptus.cloudformation.workflow.updateinfo.UpdateType;
import com.eucalyptus.component.ServiceConfiguration;
import com.eucalyptus.component.Topology;
import com.eucalyptus.component.id.Euare;
import com.eucalyptus.util.async.AsyncRequests;
import com.fasterxml.jackson.databind.node.TextNode;
import com.google.common.collect.Lists;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Objects;
/**
* Created by ethomas on 2/3/14.
*/
public class AWSIAMInstanceProfileResourceAction extends StepBasedResourceAction {
private AWSIAMInstanceProfileProperties properties = new AWSIAMInstanceProfileProperties();
private AWSIAMInstanceProfileResourceInfo info = new AWSIAMInstanceProfileResourceInfo();
public AWSIAMInstanceProfileResourceAction() {
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;
AWSIAMInstanceProfileResourceAction otherAction = (AWSIAMInstanceProfileResourceAction) resourceAction;
if (!Objects.equals(properties.getPath(), otherAction.properties.getPath())) {
updateType = UpdateType.max(updateType, UpdateType.NEEDS_REPLACEMENT);
}
if (!Objects.equals(properties.getRoles(), otherAction.properties.getRoles())) {
updateType = UpdateType.max(updateType, UpdateType.NO_INTERRUPTION);
}
return updateType;
}
private enum CreateSteps implements Step {
CREATE_INSTANCE_PROFILE {
@Override
public ResourceAction perform(ResourceAction resourceAction) throws Exception {
AWSIAMInstanceProfileResourceAction action = (AWSIAMInstanceProfileResourceAction) resourceAction;
ServiceConfiguration configuration = Topology.lookup(Euare.class);
String instanceProfileName = action.getDefaultPhysicalResourceId();
CreateInstanceProfileType createInstanceProfileType = MessageHelper.createMessage(CreateInstanceProfileType.class, action.info.getEffectiveUserId());
createInstanceProfileType.setPath(action.properties.getPath());
createInstanceProfileType.setInstanceProfileName(instanceProfileName);
CreateInstanceProfileResponseType createInstanceProfileResponseType = AsyncRequests.<CreateInstanceProfileType,CreateInstanceProfileResponseType> sendSync(configuration, createInstanceProfileType);
String arn = createInstanceProfileResponseType.getCreateInstanceProfileResult().getInstanceProfile().getArn();
action.info.setPhysicalResourceId(instanceProfileName);
action.info.setCreatedEnoughToDelete(true);
action.info.setArn(JsonHelper.getStringFromJsonNode(new TextNode(arn)));
action.info.setReferenceValueJson(JsonHelper.getStringFromJsonNode(new TextNode(action.info.getPhysicalResourceId())));
return action;
}
},
ADD_ROLES {
@Override
public ResourceAction perform(ResourceAction resourceAction) throws Exception {
AWSIAMInstanceProfileResourceAction action = (AWSIAMInstanceProfileResourceAction) resourceAction;
ServiceConfiguration configuration = Topology.lookup(Euare.class);
if (action.properties.getRoles() != null) {
if (action.properties.getRoles().size() > 1) throw new ValidationErrorException("Roles has too many elements. The limit is 1.");
if (action.properties.getRoles().size() == 0) throw new ValidationErrorException("Property Roles can not be empty.");
for (String roleName: action.properties.getRoles()) {
AddRoleToInstanceProfileType addRoleToInstanceProfileType = MessageHelper.createMessage(AddRoleToInstanceProfileType.class, action.info.getEffectiveUserId());
addRoleToInstanceProfileType.setInstanceProfileName(action.info.getPhysicalResourceId());
addRoleToInstanceProfileType.setRoleName(roleName);
AsyncRequests.<AddRoleToInstanceProfileType,AddRoleToInstanceProfileResponseType> sendSync(configuration, addRoleToInstanceProfileType);
}
}
return action;
}
};
@Nullable
@Override
public Integer getTimeout() {
return null;
}
}
private enum DeleteSteps implements Step {
DELETE_INSTANCE_PROFILE {
@Override
public ResourceAction perform(ResourceAction resourceAction) throws Exception {
AWSIAMInstanceProfileResourceAction action = (AWSIAMInstanceProfileResourceAction) resourceAction;
ServiceConfiguration configuration = Topology.lookup(Euare.class);
if (!Boolean.TRUE.equals(action.info.getCreatedEnoughToDelete())) return action;
if (!IAMHelper.instanceProfileExists(configuration, action.info.getPhysicalResourceId(), action.info.getEffectiveUserId())) return action;
// we can delete the instance profile without detaching the role
DeleteInstanceProfileType deleteInstanceProfileType = MessageHelper.createMessage(DeleteInstanceProfileType.class, action.info.getEffectiveUserId());
deleteInstanceProfileType.setInstanceProfileName(action.info.getPhysicalResourceId());
AsyncRequests.<DeleteInstanceProfileType,DeleteInstanceProfileResponseType> sendSync(configuration, deleteInstanceProfileType);
return action;
}
};
@Nullable
@Override
public Integer getTimeout() {
return null;
}
}
@Override
public ResourceProperties getResourceProperties() {
return properties;
}
@Override
public void setResourceProperties(ResourceProperties resourceProperties) {
properties = (AWSIAMInstanceProfileProperties) resourceProperties;
}
@Override
public ResourceInfo getResourceInfo() {
return info;
}
@Override
public void setResourceInfo(ResourceInfo resourceInfo) {
info = (AWSIAMInstanceProfileResourceInfo) resourceInfo;
}
private static List<String> getRoleNames(GetInstanceProfileResponseType getInstanceProfileResponseType) {
List<String> returnValue = Lists.newArrayList();
if (getInstanceProfileResponseType != null && getInstanceProfileResponseType.getGetInstanceProfileResult() != null &&
getInstanceProfileResponseType.getGetInstanceProfileResult().getInstanceProfile() != null &&
getInstanceProfileResponseType.getGetInstanceProfileResult().getInstanceProfile().getRoles() != null &&
getInstanceProfileResponseType.getGetInstanceProfileResult().getInstanceProfile().getRoles().getMember() != null) {
for (RoleType roleType : getInstanceProfileResponseType.getGetInstanceProfileResult().getInstanceProfile().getRoles().getMember()) {
if (roleType != null && roleType.getRoleName() != null) returnValue.add(roleType.getRoleName());
}
}
return returnValue;
}
private enum UpdateNoInterruptionSteps implements UpdateStep {
UPDATE_ROLES {
@Override
public ResourceAction perform(ResourceAction oldResourceAction, ResourceAction newResourceAction) throws Exception {
AWSIAMInstanceProfileResourceAction oldAction = (AWSIAMInstanceProfileResourceAction) oldResourceAction;
AWSIAMInstanceProfileResourceAction newAction = (AWSIAMInstanceProfileResourceAction) newResourceAction;
ServiceConfiguration configuration = Topology.lookup(Euare.class);
// This is a weird case. There can be only 1 role per instance profile, but the API might allow more.
// As such, we delete the current role if it exists.
GetInstanceProfileType getInstanceProfileType = MessageHelper.createMessage(GetInstanceProfileType.class, newAction.info.getEffectiveUserId());
getInstanceProfileType.setInstanceProfileName(newAction.info.getPhysicalResourceId());
GetInstanceProfileResponseType getInstanceProfileResponseType = AsyncRequests.<GetInstanceProfileType, GetInstanceProfileResponseType>sendSync(configuration, getInstanceProfileType);
for (String roleName: getRoleNames(getInstanceProfileResponseType)) {
RemoveRoleFromInstanceProfileType removeRoleFromInstanceProfileType = MessageHelper.createMessage(RemoveRoleFromInstanceProfileType.class, newAction.info.getEffectiveUserId());
removeRoleFromInstanceProfileType.setInstanceProfileName(newAction.info.getPhysicalResourceId());
removeRoleFromInstanceProfileType.setRoleName(roleName);
AsyncRequests.<RemoveRoleFromInstanceProfileType,RemoveRoleFromInstanceProfileResponseType> sendSync(configuration, removeRoleFromInstanceProfileType);
}
if (newAction.properties.getRoles() != null) {
if (newAction.properties.getRoles().size() == 0) throw new ValidationErrorException("Property Roles can not be empty.");
if (newAction.properties.getRoles().size() > 1) throw new ValidationErrorException("Roles has too many elements. The limit is 1.");
for (String roleName: newAction.properties.getRoles()) {
AddRoleToInstanceProfileType addRoleToInstanceProfileType = MessageHelper.createMessage(AddRoleToInstanceProfileType.class, newAction.info.getEffectiveUserId());
addRoleToInstanceProfileType.setInstanceProfileName(newAction.info.getPhysicalResourceId());
addRoleToInstanceProfileType.setRoleName(roleName);
AsyncRequests.<AddRoleToInstanceProfileType,AddRoleToInstanceProfileResponseType> sendSync(configuration, addRoleToInstanceProfileType);
}
}
return newAction;
}
};
@Nullable
@Override
public Integer getTimeout() {
return null;
}
}
}