package com.sequenceiq.cloudbreak.core.flow2.stack.upscale;
import java.util.List;
import java.util.Map;
import java.util.Set;
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.action.Action;
import com.sequenceiq.cloudbreak.api.model.InstanceGroupType;
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.GetSSHFingerprintsRequest;
import com.sequenceiq.cloudbreak.cloud.event.instance.GetSSHFingerprintsResult;
import com.sequenceiq.cloudbreak.cloud.event.resource.UpscaleStackRequest;
import com.sequenceiq.cloudbreak.cloud.event.resource.UpscaleStackResult;
import com.sequenceiq.cloudbreak.cloud.model.CloudInstance;
import com.sequenceiq.cloudbreak.cloud.model.CloudResource;
import com.sequenceiq.cloudbreak.cloud.model.CloudStack;
import com.sequenceiq.cloudbreak.converter.spi.CloudResourceToResourceConverter;
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.event.StackScaleTriggerEvent;
import com.sequenceiq.cloudbreak.core.flow2.stack.AbstractStackFailureAction;
import com.sequenceiq.cloudbreak.core.flow2.stack.StackFailureContext;
import com.sequenceiq.cloudbreak.core.flow2.stack.downscale.StackScalingFlowContext;
import com.sequenceiq.cloudbreak.core.flow2.stack.provision.StackCreationEvent;
import com.sequenceiq.cloudbreak.domain.InstanceGroup;
import com.sequenceiq.cloudbreak.domain.InstanceMetaData;
import com.sequenceiq.cloudbreak.reactor.api.event.StackEvent;
import com.sequenceiq.cloudbreak.reactor.api.event.StackFailureEvent;
import com.sequenceiq.cloudbreak.reactor.api.event.resource.BootstrapNewNodesRequest;
import com.sequenceiq.cloudbreak.reactor.api.event.resource.BootstrapNewNodesResult;
import com.sequenceiq.cloudbreak.reactor.api.event.resource.ExtendHostMetadataRequest;
import com.sequenceiq.cloudbreak.reactor.api.event.resource.ExtendHostMetadataResult;
import com.sequenceiq.cloudbreak.repository.InstanceGroupRepository;
import com.sequenceiq.cloudbreak.service.stack.InstanceMetadataService;
@Configuration
public class StackUpscaleActions {
private static final Logger LOGGER = LoggerFactory.getLogger(StackUpscaleActions.class);
@Inject
private ResourceToCloudResourceConverter cloudResourceConverter;
@Inject
private CloudResourceToResourceConverter resourceConverter;
@Inject
private InstanceMetadataService instanceMetadataService;
@Inject
private StackUpscaleService stackUpscaleService;
@Inject
private StackToCloudStackConverter cloudStackConverter;
@Inject
private InstanceGroupRepository instanceGroupRepository;
@Inject
private InstanceMetaDataToCloudInstanceConverter metadataConverter;
@Bean(name = "ADD_INSTANCES_STATE")
public Action addInstances() {
return new AbstractStackUpscaleAction<StackScaleTriggerEvent>(StackScaleTriggerEvent.class) {
@Override
protected void prepareExecution(StackScaleTriggerEvent payload, Map<Object, Object> variables) {
variables.put(INSTANCEGROUPNAME, payload.getInstanceGroup());
variables.put(ADJUSTMENT, payload.getAdjustment());
}
@Override
protected void doExecute(StackScalingFlowContext context, StackScaleTriggerEvent payload, Map<Object, Object> variables) throws Exception {
stackUpscaleService.startAddInstances(context.getStack(), payload.getAdjustment());
sendEvent(context);
}
@Override
protected Selectable createRequest(StackScalingFlowContext context) {
LOGGER.debug("Assembling upscale stack event for stack: {}", context.getStack());
InstanceGroup group = context.getStack().getInstanceGroupByInstanceGroupName(context.getInstanceGroupName());
group.setNodeCount(group.getNodeCount() + context.getAdjustment());
CloudStack cloudStack = cloudStackConverter.convert(context.getStack());
instanceMetadataService.saveInstanceRequests(context.getStack(), cloudStack.getGroups());
List<CloudResource> resources = cloudResourceConverter.convert(context.getStack().getResources());
return new UpscaleStackRequest<UpscaleStackResult>(context.getCloudContext(), context.getCloudCredential(), cloudStack, resources);
}
};
}
@Bean(name = "ADD_INSTANCES_FINISHED_STATE")
public Action finishAddInstances() {
return new AbstractStackUpscaleAction<UpscaleStackResult>(UpscaleStackResult.class) {
@Override
protected void doExecute(StackScalingFlowContext context, UpscaleStackResult payload, Map<Object, Object> variables) throws Exception {
stackUpscaleService.finishAddInstances(context, payload);
sendEvent(context);
}
@Override
protected Selectable createRequest(StackScalingFlowContext context) {
return new StackEvent(StackUpscaleEvent.EXTEND_METADATA_EVENT.event(), context.getStack().getId());
}
};
}
@Bean(name = "EXTEND_METADATA_STATE")
public Action extendMetadata() {
return new AbstractStackUpscaleAction<StackEvent>(StackEvent.class) {
@Override
protected void doExecute(StackScalingFlowContext context, StackEvent payload, Map<Object, Object> variables) throws Exception {
stackUpscaleService.extendingMetadata(context.getStack());
sendEvent(context);
}
@Override
protected Selectable createRequest(StackScalingFlowContext context) {
List<CloudResource> cloudResources = cloudResourceConverter.convert(context.getStack().getResources());
List<CloudInstance> cloudInstances = stackUpscaleService.getNewInstances(context.getStack());
return new CollectMetadataRequest(context.getCloudContext(), context.getCloudCredential(), cloudResources, cloudInstances);
}
};
}
@Bean(name = "EXTEND_METADATA_FINISHED_STATE")
public Action finishExtendMetadata() {
return new AbstractStackUpscaleAction<CollectMetadataResult>(CollectMetadataResult.class) {
@Override
protected void doExecute(StackScalingFlowContext context, CollectMetadataResult payload, Map<Object, Object> variables) throws Exception {
Set<String> upscaleCandidateAddresses = stackUpscaleService.finishExtendMetadata(context.getStack(), context.getInstanceGroupName(), payload);
variables.put(UPSCALE_CANDIDATE_ADDRESSES, upscaleCandidateAddresses);
InstanceGroup ig = instanceGroupRepository.findOneByGroupNameInStack(payload.getStackId(), context.getInstanceGroupName());
if (InstanceGroupType.GATEWAY == ig.getInstanceGroupType()) {
InstanceMetaData gatewayMetaData = context.getStack().getPrimaryGatewayInstance();
CloudInstance gatewayInstance = metadataConverter.convert(gatewayMetaData);
GetSSHFingerprintsRequest sshFingerPrintReq = new GetSSHFingerprintsRequest<GetSSHFingerprintsResult>(context.getCloudContext(),
context.getCloudCredential(), gatewayInstance);
sendEvent(context.getFlowId(), sshFingerPrintReq);
} else {
BootstrapNewNodesEvent bootstrapPayload = new BootstrapNewNodesEvent(context.getStack().getId(), upscaleCandidateAddresses);
sendEvent(context.getFlowId(), StackUpscaleEvent.BOOTSTRAP_NEW_NODES_EVENT.event(), bootstrapPayload);
}
}
};
}
@Bean(name = "GATEWAY_TLS_SETUP_STATE")
public Action tlsSetupAction() {
return new AbstractStackUpscaleAction<GetSSHFingerprintsResult>(GetSSHFingerprintsResult.class) {
@Override
protected void doExecute(StackScalingFlowContext context, GetSSHFingerprintsResult payload, Map<Object, Object> variables) throws Exception {
stackUpscaleService.setupTls(context, payload);
stackUpscaleService.removeTemporarySShKey(context, payload.getSshFingerprints());
BootstrapNewNodesEvent bootstrapNewNodesEvent =
new BootstrapNewNodesEvent(payload.getStackId(), (Set<String>) variables.get(UPSCALE_CANDIDATE_ADDRESSES));
sendEvent(context.getFlowId(), StackCreationEvent.TLS_SETUP_FINISHED_EVENT.event(), bootstrapNewNodesEvent);
}
};
}
@Bean(name = "BOOTSTRAP_NEW_NODES_STATE")
public Action bootstrapNewNodes() {
return new AbstractStackUpscaleAction<BootstrapNewNodesEvent>(BootstrapNewNodesEvent.class) {
@Override
protected void doExecute(StackScalingFlowContext context, BootstrapNewNodesEvent payload, Map<Object, Object> variables) throws Exception {
stackUpscaleService.bootstrappingNewNodes(context.getStack());
BootstrapNewNodesRequest request = new BootstrapNewNodesRequest(context.getStack().getId(), payload.getUpscaleCandidateAddresses());
sendEvent(context.getFlowId(), request);
}
};
}
@Bean(name = "EXTEND_HOST_METADATA_STATE")
public Action extendHostMetadata() {
return new AbstractStackUpscaleAction<BootstrapNewNodesResult>(BootstrapNewNodesResult.class) {
@Override
protected void doExecute(StackScalingFlowContext context, BootstrapNewNodesResult payload, Map<Object, Object> variables) throws Exception {
stackUpscaleService.extendingHostMetadata(context.getStack());
ExtendHostMetadataRequest request = new ExtendHostMetadataRequest(context.getStack().getId(),
payload.getRequest().getUpscaleCandidateAddresses());
sendEvent(context.getFlowId(), request);
}
};
}
@Bean(name = "EXTEND_HOST_METADATA_FINISHED_STATE")
public Action finishExtendHostMetadata() {
return new AbstractStackUpscaleAction<ExtendHostMetadataResult>(ExtendHostMetadataResult.class) {
@Override
protected void doExecute(StackScalingFlowContext context, ExtendHostMetadataResult payload, Map<Object, Object> variables) throws Exception {
stackUpscaleService.finishExtendHostMetadata(context.getStack());
sendEvent(context);
}
@Override
protected Selectable createRequest(StackScalingFlowContext context) {
return new StackEvent(StackUpscaleEvent.UPSCALE_FINALIZED_EVENT.event(), context.getStack().getId());
}
};
}
@Bean(name = "UPSCALE_FAILED_STATE")
public Action stackStartFailedAction() {
return new AbstractStackFailureAction<StackUpscaleState, StackUpscaleEvent>() {
@Override
protected void doExecute(StackFailureContext context, StackFailureEvent payload, Map<Object, Object> variables) throws Exception {
stackUpscaleService.handleStackUpscaleFailure(context.getStack(), payload);
sendEvent(context);
}
@Override
protected Selectable createRequest(StackFailureContext context) {
return new StackEvent(StackUpscaleEvent.UPSCALE_FAIL_HANDLED_EVENT.event(), context.getStack().getId());
}
};
}
}