package com.sequenceiq.cloudbreak.core.flow2.stack.start; import static com.sequenceiq.cloudbreak.cloud.model.AvailabilityZone.availabilityZone; import static com.sequenceiq.cloudbreak.cloud.model.Location.location; import static com.sequenceiq.cloudbreak.cloud.model.Region.region; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Optional; import javax.inject.Inject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.statemachine.StateContext; import org.springframework.statemachine.action.Action; import com.sequenceiq.cloudbreak.cloud.context.CloudContext; import com.sequenceiq.cloudbreak.cloud.event.Payload; import com.sequenceiq.cloudbreak.cloud.event.Selectable; import com.sequenceiq.cloudbreak.cloud.event.instance.CollectMetadataRequest; import com.sequenceiq.cloudbreak.cloud.event.instance.CollectMetadataResult; import com.sequenceiq.cloudbreak.cloud.event.instance.StartInstancesRequest; import com.sequenceiq.cloudbreak.cloud.event.instance.StartInstancesResult; import com.sequenceiq.cloudbreak.cloud.model.CloudCredential; import com.sequenceiq.cloudbreak.cloud.model.CloudInstance; import com.sequenceiq.cloudbreak.cloud.model.CloudResource; import com.sequenceiq.cloudbreak.cloud.model.Location; import com.sequenceiq.cloudbreak.converter.spi.CredentialToCloudCredentialConverter; import com.sequenceiq.cloudbreak.converter.spi.InstanceMetaDataToCloudInstanceConverter; import com.sequenceiq.cloudbreak.converter.spi.ResourceToCloudResourceConverter; import com.sequenceiq.cloudbreak.converter.spi.StackToCloudStackConverter; import com.sequenceiq.cloudbreak.core.flow2.AbstractAction; import com.sequenceiq.cloudbreak.core.flow2.stack.AbstractStackFailureAction; import com.sequenceiq.cloudbreak.core.flow2.stack.StackFailureContext; import com.sequenceiq.cloudbreak.domain.InstanceMetaData; import com.sequenceiq.cloudbreak.domain.Stack; import com.sequenceiq.cloudbreak.logger.MDCBuilder; import com.sequenceiq.cloudbreak.reactor.api.event.StackEvent; import com.sequenceiq.cloudbreak.reactor.api.event.StackFailureEvent; import com.sequenceiq.cloudbreak.repository.InstanceMetaDataRepository; import com.sequenceiq.cloudbreak.service.events.CloudbreakEventService; import com.sequenceiq.cloudbreak.service.messages.CloudbreakMessagesService; import com.sequenceiq.cloudbreak.service.stack.StackService; @Configuration public class StackStartActions { private static final Logger LOGGER = LoggerFactory.getLogger(StackStartActions.class); @Inject private StackToCloudStackConverter cloudStackConverter; @Inject private InstanceMetaDataToCloudInstanceConverter cloudInstanceConverter; @Inject private ResourceToCloudResourceConverter cloudResourceConverter; @Inject private StackStartStopService stackStartStopService; @Bean(name = "START_STATE") public Action stackStartAction() { return new AbstractStackStartAction<StackEvent>(StackEvent.class) { @Override protected void doExecute(StackStartStopContext context, StackEvent payload, Map<Object, Object> variables) throws Exception { stackStartStopService.startStackStart(context); sendEvent(context); } @Override protected Selectable createRequest(StackStartStopContext context) { LOGGER.info("Assembling start request for stack: {}", context.getStack()); List<CloudInstance> instances = cloudInstanceConverter.convert(context.getStack().getInstanceMetaDataAsList()); List<CloudResource> resources = cloudResourceConverter.convert(context.getStack().getResources()); return new StartInstancesRequest(context.getCloudContext(), context.getCloudCredential(), resources, instances); } }; } @Bean(name = "COLLECTING_METADATA") public Action collectingMetadataAction() { return new AbstractStackStartAction<StartInstancesResult>(StartInstancesResult.class) { @Override protected void doExecute(StackStartStopContext context, StartInstancesResult payload, Map<Object, Object> variables) throws Exception { sendEvent(context); } @Override protected Selectable createRequest(StackStartStopContext context) { List<CloudInstance> cloudInstances = cloudStackConverter.buildInstances(context.getStack()); List<CloudResource> cloudResources = cloudResourceConverter.convert(context.getStack().getResources()); return new CollectMetadataRequest(context.getCloudContext(), context.getCloudCredential(), cloudResources, cloudInstances); } }; } @Bean(name = "START_FINISHED_STATE") public Action startFinishedAction() { return new AbstractStackStartAction<CollectMetadataResult>(CollectMetadataResult.class) { @Override protected void doExecute(StackStartStopContext context, CollectMetadataResult payload, Map<Object, Object> variables) throws Exception { stackStartStopService.finishStackStart(context.getStack(), payload.getResults()); sendEvent(context); } @Override protected Selectable createRequest(StackStartStopContext context) { return new StackEvent(StackStartEvent.START_FINALIZED_EVENT.event(), context.getStack().getId()); } }; } @Bean(name = "START_FAILED_STATE") public Action stackStartFailedAction() { return new AbstractStackFailureAction<StackStartState, StackStartEvent>() { @Override protected void doExecute(StackFailureContext context, StackFailureEvent payload, Map<Object, Object> variables) throws Exception { stackStartStopService.handleStackStartError(context.getStack(), payload); sendEvent(context); } @Override protected Selectable createRequest(StackFailureContext context) { return new StackEvent(StackStartEvent.START_FAIL_HANDLED_EVENT.event(), context.getStack().getId()); } }; } private abstract static class AbstractStackStartAction<P extends Payload> extends AbstractAction<StackStartState, StackStartEvent, StackStartStopContext, P> { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractStackStartAction.class); @Inject private StackService stackService; @Inject private InstanceMetaDataRepository instanceMetaDataRepository; @Inject private CredentialToCloudCredentialConverter credentialConverter; @Inject private InstanceMetaDataToCloudInstanceConverter cloudInstanceConverter; @Inject private ResourceToCloudResourceConverter cloudResourceConverter; @Inject private CloudbreakMessagesService messagesService; @Inject private CloudbreakEventService cloudbreakEventService; protected AbstractStackStartAction(Class<P> payloadClass) { super(payloadClass); } @Override protected StackStartStopContext createFlowContext(String flowId, StateContext<StackStartState, StackStartEvent> stateContext, P payload) { Long stackId = payload.getStackId(); Stack stack = stackService.getById(stackId); MDCBuilder.buildMdcContext(stack); List<InstanceMetaData> instances = new ArrayList<>(instanceMetaDataRepository.findNotTerminatedForStack(stackId)); Location location = location(region(stack.getRegion()), availabilityZone(stack.getAvailabilityZone())); CloudContext cloudContext = new CloudContext(stack.getId(), stack.getName(), stack.cloudPlatform(), stack.getOwner(), stack.getPlatformVariant(), location); CloudCredential cloudCredential = credentialConverter.convert(stack.getCredential()); return new StackStartStopContext(flowId, stack, instances, cloudContext, cloudCredential); } @Override protected Object getFailurePayload(P payload, Optional<StackStartStopContext> flowContext, Exception ex) { return new StackFailureEvent(payload.getStackId(), ex); } } }