package com.eucalyptus.cloudformation.workflow.steps; import com.amazonaws.services.simpleworkflow.flow.core.Promise; import com.eucalyptus.cloudformation.resources.ResourceAction; import com.eucalyptus.cloudformation.workflow.StackActivityClient; import com.eucalyptus.cloudformation.workflow.updateinfo.UpdateTypeAndDirection; import com.google.common.base.Function; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.netflix.glisten.WorkflowOperations; import javax.annotation.Nullable; import java.util.EnumMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; /** * Created by ethomas on 12/4/15. */ public abstract class StepBasedResourceAction extends ResourceAction { public Integer getMultiUpdateStepTimeoutPollMaximumInterval() { return (int) TimeUnit.SECONDS.toSeconds(30); } public Integer getMultiStepTimeoutPollMaximumInterval() { return (int) TimeUnit.SECONDS.toSeconds(30); } protected Map<String, Step> createSteps = Maps.newLinkedHashMap(); public final Step getCreateStep(String stepId) { return createSteps.get(stepId); } protected Map<String, Step> deleteSteps = Maps.newLinkedHashMap(); public final Step getDeleteStep(String stepId) { return deleteSteps.get(stepId); } protected EnumMap<UpdateTypeAndDirection, Map<String, UpdateStep>> updateStepEnumMap = Maps.newEnumMap(UpdateTypeAndDirection.class); public final UpdateStep getUpdateStep(UpdateTypeAndDirection updateTypeAndDirection, String stepId) { return updateStepEnumMap.get(updateTypeAndDirection).get(stepId); } public StepBasedResourceAction(Map<String, Step> addedCreateSteps, Map<String, Step> addedDeleteSteps, Map<String, UpdateStep> addedUpdateNoInterruptionSteps, Map<String, UpdateStep> addedUpdateSomeInterruptionSteps) { for (UpdateTypeAndDirection updateTypeAndDirection : UpdateTypeAndDirection.values()) { updateStepEnumMap.put(updateTypeAndDirection, Maps.<String, UpdateStep>newLinkedHashMap()); } clearAndPutIfNotNull(createSteps, addedCreateSteps); clearAndPutIfNotNull(deleteSteps, addedDeleteSteps); clearAndPutIfNotNull(updateStepEnumMap.get(UpdateTypeAndDirection.UPDATE_NO_INTERRUPTION), addedUpdateNoInterruptionSteps); clearAndPutIfNotNull(updateStepEnumMap.get(UpdateTypeAndDirection.UPDATE_SOME_INTERRUPTION), addedUpdateSomeInterruptionSteps); // defaults for the rest clearAndPutIfNotNull(updateStepEnumMap.get(UpdateTypeAndDirection.UPDATE_WITH_REPLACEMENT), createStepsToUpdateWithReplacementSteps(addedCreateSteps)); clearAndPutIfNotNull(updateStepEnumMap.get(UpdateTypeAndDirection.UPDATE_ROLLBACK_NO_INTERRUPTION), addedUpdateNoInterruptionSteps); clearAndPutIfNotNull(updateStepEnumMap.get(UpdateTypeAndDirection.UPDATE_ROLLBACK_SOME_INTERRUPTION), addedUpdateSomeInterruptionSteps); clearAndPutIfNotNull(updateStepEnumMap.get(UpdateTypeAndDirection.UPDATE_ROLLBACK_WITH_REPLACEMENT), Maps.<String, UpdateStep>newHashMap()); // by default, nothing to do. (Cleanup will take care of it) } public void setCreateSteps(Map<String, Step> addedCreateSteps) { clearAndPutIfNotNull(createSteps, addedCreateSteps); } public void setDeleteSteps(Map<String, Step> addedDeleteSteps) { clearAndPutIfNotNull(deleteSteps, addedDeleteSteps); } public void setUpdateSteps(UpdateTypeAndDirection updateTypeAndDirection, Map<String, UpdateStep> addedUpdateSteps) { clearAndPutIfNotNull(updateStepEnumMap.get(updateTypeAndDirection), addedUpdateSteps); } protected static <T> void clearAndPutIfNotNull(Map<String, T> map, Map<String, T> addedMap){ map.clear(); if (addedMap != null) map.putAll(addedMap); } public static final Map<String, Step> fromEnum(Class<? extends Step> stepClass) { Map<String, Step> stepMap = Maps.newLinkedHashMap(); Step[] steps = stepClass.getEnumConstants(); if (steps != null) { for (Step step: steps) { stepMap.put(step.name(), step); } } return stepMap; } public static final Map<String, UpdateStep> fromUpdateEnum(Class<? extends UpdateStep> stepClass) { Map<String, UpdateStep> stepMap = Maps.newLinkedHashMap(); UpdateStep[] steps = stepClass.getEnumConstants(); if (steps != null) { for (UpdateStep step: steps) { stepMap.put(step.name(), step); } } return stepMap; } public static final Map<String, UpdateStep> createStepsToUpdateWithReplacementSteps(Map<String, Step> createStepsMap) { return Maps.transformValues(createStepsMap, new Function<Step, UpdateStep>() { @Nullable @Override public UpdateStep apply(final @Nullable Step step) { return new UpdateStep() { @Override public ResourceAction perform(ResourceAction oldResourceAction, ResourceAction newResourceAction) throws Exception { return step.perform(newResourceAction); } @Nullable @Override public Integer getTimeout() { return step.getTimeout(); } @Override public String name() { return step.name(); } }; } }); } @Override public Promise<String> getCreatePromise(WorkflowOperations<StackActivityClient> workflowOperations, String resourceId, String stackId, String accountId, String effectiveUserId, int createdResourceVersion) { List<String> stepIds = Lists.newArrayList(createSteps.keySet()); return new CreateMultiStepPromise(workflowOperations, stepIds, this).getCreatePromise(resourceId, stackId, accountId, effectiveUserId, createdResourceVersion); } @Override public Promise<String> getDeletePromise(WorkflowOperations<StackActivityClient> workflowOperations, String resourceId, String stackId, String accountId, String effectiveUserId, int updatedResourceVersion) { List<String> stepIds = Lists.newArrayList(deleteSteps.keySet()); return new DeleteMultiStepPromise(workflowOperations, stepIds, this).getDeletePromise(resourceId, stackId, accountId, effectiveUserId, updatedResourceVersion); } @Override public Promise<String> getUpdateCleanupPromise(WorkflowOperations<StackActivityClient> workflowOperations, String resourceId, String stackId, String accountId, String effectiveUserId, int updatedResourceVersion) { List<String> stepIds = Lists.newArrayList(deleteSteps.keySet()); return new CleanupMultiStepPromise(workflowOperations, stepIds, this).getCleanupPromise(resourceId, stackId, accountId, effectiveUserId, updatedResourceVersion); } @Override public Promise<String> getUpdateRollbackCleanupPromise(WorkflowOperations<StackActivityClient> workflowOperations, String resourceId, String stackId, String accountId, String effectiveUserId, int rolledBackResourceVersion) { List<String> stepIds = Lists.newArrayList(deleteSteps.keySet()); return new CleanupMultiStepPromise(workflowOperations, stepIds, this).getCleanupPromise(resourceId, stackId, accountId, effectiveUserId, rolledBackResourceVersion); } @Override public Promise<String> getUpdatePromise(UpdateTypeAndDirection updateTypeAndDirection, WorkflowOperations<StackActivityClient> workflowOperations, String resourceId, String stackId, String accountId, String effectiveUserId, int updatedResourceVersion) { List<String> stepIds = Lists.newArrayList(updateStepEnumMap.get(updateTypeAndDirection).keySet()); return new UpdateMultiStepPromise(workflowOperations, stepIds, this, updateTypeAndDirection).getUpdatePromise(resourceId, stackId, accountId, effectiveUserId, updatedResourceVersion); } }