package com.sequenceiq.cloudbreak.core.flow2.service; import static com.sequenceiq.cloudbreak.common.type.CloudConstants.BYOS; import static com.sequenceiq.cloudbreak.core.flow2.cluster.provision.ClusterCreationEvent.CLUSTER_CREATION_EVENT; import static com.sequenceiq.cloudbreak.core.flow2.cluster.start.ClusterStartEvent.CLUSTER_START_EVENT; import static com.sequenceiq.cloudbreak.core.flow2.cluster.sync.ClusterSyncEvent.CLUSTER_SYNC_EVENT; import static com.sequenceiq.cloudbreak.core.flow2.cluster.upscale.ClusterUpscaleEvent.CLUSTER_UPSCALE_TRIGGER_EVENT; import static com.sequenceiq.cloudbreak.core.flow2.cluster.userpasswd.ClusterCredentialChangeEvent.CLUSTER_CREDENTIALCHANGE_EVENT; import static com.sequenceiq.cloudbreak.core.flow2.stack.downscale.StackDownscaleEvent.STACK_DOWNSCALE_EVENT; import static com.sequenceiq.cloudbreak.core.flow2.stack.repair.ManualStackRepairTriggerEvent.MANUAL_STACK_REPAIR_TRIGGER_EVENT; import static com.sequenceiq.cloudbreak.core.flow2.stack.stop.StackStopEvent.STACK_STOP_EVENT; import static com.sequenceiq.cloudbreak.core.flow2.stack.sync.StackSyncEvent.STACK_SYNC_EVENT; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import javax.inject.Inject; import org.springframework.stereotype.Service; import com.sequenceiq.cloudbreak.api.model.HostGroupAdjustmentJson; import com.sequenceiq.cloudbreak.api.model.InstanceGroupAdjustmentJson; import com.sequenceiq.cloudbreak.cloud.Acceptable; import com.sequenceiq.cloudbreak.common.type.ScalingType; import com.sequenceiq.cloudbreak.controller.CloudbreakApiException; import com.sequenceiq.cloudbreak.controller.FlowsAlreadyRunningException; import com.sequenceiq.cloudbreak.core.flow2.Flow2Handler; import com.sequenceiq.cloudbreak.core.flow2.chain.FlowChainTriggers; import com.sequenceiq.cloudbreak.core.flow2.cluster.termination.ClusterTerminationEvent; import com.sequenceiq.cloudbreak.core.flow2.event.ClusterAndStackDownscaleTriggerEvent; import com.sequenceiq.cloudbreak.core.flow2.event.ClusterCredentialChangeTriggerEvent; import com.sequenceiq.cloudbreak.core.flow2.event.ClusterScaleTriggerEvent; import com.sequenceiq.cloudbreak.core.flow2.event.InstanceTerminationTriggerEvent; import com.sequenceiq.cloudbreak.core.flow2.event.StackAndClusterUpscaleTriggerEvent; import com.sequenceiq.cloudbreak.core.flow2.event.StackDownscaleTriggerEvent; import com.sequenceiq.cloudbreak.core.flow2.event.StackScaleTriggerEvent; import com.sequenceiq.cloudbreak.core.flow2.event.StackSyncTriggerEvent; import com.sequenceiq.cloudbreak.core.flow2.stack.instance.termination.InstanceTerminationEvent; import com.sequenceiq.cloudbreak.core.flow2.stack.termination.StackTerminationEvent; import com.sequenceiq.cloudbreak.domain.Stack; import com.sequenceiq.cloudbreak.reactor.api.event.StackEvent; import com.sequenceiq.cloudbreak.reactor.api.event.orchestration.ClusterRepairTriggerEvent; import com.sequenceiq.cloudbreak.reactor.api.event.orchestration.StackRepairTriggerEvent; import com.sequenceiq.cloudbreak.reactor.api.event.stack.TerminationEvent; import com.sequenceiq.cloudbreak.service.stack.StackService; import com.sequenceiq.cloudbreak.service.stack.repair.UnhealthyInstances; import reactor.bus.Event; import reactor.bus.EventBus; /** * Flow manager implementation backed by Reactor. * This class is the flow state machine and mediates between the states and reactor events */ @Service public class ReactorFlowManager { private static final Integer WAIT_FOR_ACCEPT = 5; @Inject private EventBus reactor; @Inject private ErrorHandlerAwareFlowEventFactory eventFactory; @Inject private StackService stackService; public void triggerProvisioning(Long stackId) { String selector = FlowChainTriggers.FULL_PROVISION_TRIGGER_EVENT; notify(selector, new StackEvent(selector, stackId)); } public void triggerStackStart(Long stackId) { String selector = FlowChainTriggers.FULL_START_TRIGGER_EVENT; StackEvent startTriggerEvent = new StackEvent(selector, stackId); notify(selector, startTriggerEvent); } public void triggerStackStop(Long stackId) { String selector = STACK_STOP_EVENT.event(); notify(selector, new StackEvent(selector, stackId)); } public void triggerStackUpscale(Long stackId, InstanceGroupAdjustmentJson instanceGroupAdjustment) { String selector = FlowChainTriggers.FULL_UPSCALE_TRIGGER_EVENT; StackAndClusterUpscaleTriggerEvent stackAndClusterUpscaleTriggerEvent = new StackAndClusterUpscaleTriggerEvent(selector, stackId, instanceGroupAdjustment.getInstanceGroup(), instanceGroupAdjustment.getScalingAdjustment(), instanceGroupAdjustment.getWithClusterEvent() ? ScalingType.UPSCALE_TOGETHER : ScalingType.UPSCALE_ONLY_STACK); notify(selector, stackAndClusterUpscaleTriggerEvent); } public void triggerStackDownscale(Long stackId, InstanceGroupAdjustmentJson instanceGroupAdjustment) { String selector = STACK_DOWNSCALE_EVENT.event(); StackScaleTriggerEvent stackScaleTriggerEvent = new StackDownscaleTriggerEvent(selector, stackId, instanceGroupAdjustment.getInstanceGroup(), instanceGroupAdjustment.getScalingAdjustment()); notify(selector, stackScaleTriggerEvent); } public void triggerStackSync(Long stackId) { String selector = STACK_SYNC_EVENT.event(); notify(selector, new StackSyncTriggerEvent(selector, stackId, true)); } public void triggerStackRemoveInstance(Long stackId, String instanceId) { String selector = InstanceTerminationEvent.TERMINATION_EVENT.event(); InstanceTerminationTriggerEvent event = new InstanceTerminationTriggerEvent(selector, stackId, Collections.singleton(instanceId)); notify(selector, event); } public void triggerTermination(Long stackId, Boolean deleteDependencies) { Stack stack = stackService.getById(stackId); if (BYOS.equals(stack.cloudPlatform())) { String selector = FlowChainTriggers.BYOS_CLUSTER_TERMINATION_TRIGGER_EVENT; notify(selector, new TerminationEvent(selector, stackId, null, deleteDependencies)); } else { String selector = StackTerminationEvent.TERMINATION_EVENT.event(); notify(selector, new TerminationEvent(selector, stackId, deleteDependencies)); } cancelRunningFlows(stackId); } public void triggerForcedTermination(Long stackId, Boolean deleteDependencies) { Stack stack = stackService.get(stackId); if (BYOS.equals(stack.cloudPlatform())) { String selector = FlowChainTriggers.BYOS_CLUSTER_TERMINATION_TRIGGER_EVENT; notify(selector, new TerminationEvent(selector, stackId, null, deleteDependencies)); } else { String selector = StackTerminationEvent.FORCE_TERMINATION_EVENT.event(); notify(selector, new TerminationEvent(selector, stackId, deleteDependencies)); } cancelRunningFlows(stackId); } public void triggerClusterInstall(Long stackId) { String selector = CLUSTER_CREATION_EVENT.event(); notify(selector, new StackEvent(selector, stackId)); } public void triggerClusterReInstall(Long stackId) { String selector = FlowChainTriggers.CLUSTER_RESET_CHAIN_TRIGGER_EVENT; notify(selector, new StackEvent(selector, stackId)); } public void triggerClusterUpgrade(Long stackId) { String selector = FlowChainTriggers.CLUSTER_UPGRADE_CHAIN_TRIGGER_EVENT; reactor.notify(selector, eventFactory.createEvent(new StackEvent(selector, stackId), selector)); } public void triggerClusterCredentialReplace(Long stackId, String userName, String password) { String selector = CLUSTER_CREDENTIALCHANGE_EVENT.event(); ClusterCredentialChangeTriggerEvent event = ClusterCredentialChangeTriggerEvent.replaceUserEvent(selector, stackId, userName, password); notify(selector, event); } public void triggerClusterCredentialUpdate(Long stackId, String password) { String selector = CLUSTER_CREDENTIALCHANGE_EVENT.event(); ClusterCredentialChangeTriggerEvent event = ClusterCredentialChangeTriggerEvent.changePasswordEvent(selector, stackId, password); notify(selector, event); } public void triggerClusterUpscale(Long stackId, HostGroupAdjustmentJson hostGroupAdjustment) { String selector = CLUSTER_UPSCALE_TRIGGER_EVENT.event(); ClusterScaleTriggerEvent event = new ClusterScaleTriggerEvent(selector, stackId, hostGroupAdjustment.getHostGroup(), hostGroupAdjustment.getScalingAdjustment()); notify(selector, event); } public void triggerClusterDownscale(Long stackId, HostGroupAdjustmentJson hostGroupAdjustment) { String selector = FlowChainTriggers.FULL_DOWNSCALE_TRIGGER_EVENT; ScalingType scalingType = hostGroupAdjustment.getWithStackUpdate() ? ScalingType.DOWNSCALE_TOGETHER : ScalingType.DOWNSCALE_ONLY_CLUSTER; ClusterAndStackDownscaleTriggerEvent event = new ClusterAndStackDownscaleTriggerEvent(selector, stackId, hostGroupAdjustment.getHostGroup(), hostGroupAdjustment.getScalingAdjustment(), scalingType); notify(selector, event); } public void triggerClusterStart(Long stackId) { String selector = CLUSTER_START_EVENT.event(); notify(selector, new StackEvent(selector, stackId)); } public void triggerClusterStop(Long stackId) { String selector = FlowChainTriggers.FULL_STOP_TRIGGER_EVENT; notify(selector, new StackEvent(selector, stackId)); } public void triggerClusterSync(Long stackId) { String selector = CLUSTER_SYNC_EVENT.event(); notify(selector, new StackEvent(selector, stackId)); } public void triggerFullSync(Long stackId) { String selector = FlowChainTriggers.FULL_SYNC_TRIGGER_EVENT; notify(selector, new StackEvent(selector, stackId)); } public void triggerClusterTermination(Long stackId) { Stack stack = stackService.get(stackId); if (BYOS.equals(stack.cloudPlatform())) { String selector = FlowChainTriggers.BYOS_CLUSTER_TERMINATION_TRIGGER_EVENT; notify(selector, new StackEvent(selector, stackId, null)); } else { String selector = ClusterTerminationEvent.TERMINATION_EVENT.event(); notify(selector, new StackEvent(selector, stackId)); } cancelRunningFlows(stackId); } public void triggerManualRepairFlow(Long stackId) { String selector = MANUAL_STACK_REPAIR_TRIGGER_EVENT.event(); notify(selector, new StackEvent(selector, stackId)); } public void triggerStackRepairFlow(Long stackId, UnhealthyInstances unhealthyInstances) { String selector = FlowChainTriggers.STACK_REPAIR_TRIGGER_EVENT; notify(selector, new StackRepairTriggerEvent(stackId, unhealthyInstances)); } public void triggerClusterRepairFlow(Long stackId, Map<String, List<String>> failedNodesMap, boolean removeOnly) { notify(FlowChainTriggers.CLUSTER_REPAIR_TRIGGER_EVENT, new ClusterRepairTriggerEvent(stackId, failedNodesMap, removeOnly)); } private void cancelRunningFlows(Long stackId) { StackEvent cancelEvent = new StackEvent(Flow2Handler.FLOW_CANCEL, stackId); reactor.notify(Flow2Handler.FLOW_CANCEL, eventFactory.createEvent(cancelEvent, Flow2Handler.FLOW_CANCEL)); } private void notify(String selector, Acceptable acceptable) { Event<Acceptable> event = eventFactory.createEvent(acceptable); reactor.notify(selector, event); try { Boolean accepted = true; if (event.getData().accepted() != null) { accepted = event.getData().accepted().await(WAIT_FOR_ACCEPT, TimeUnit.SECONDS); } if (accepted == null || !accepted) { throw new FlowsAlreadyRunningException(String.format("Stack %d has flows under operation, request not allowed.", event.getData().getStackId())); } } catch (InterruptedException e) { throw new CloudbreakApiException(e.getMessage()); } } }