package com.sequenceiq.cloudbreak.core.flow2.stack.downscale; 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.HashSet; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import javax.inject.Inject; import org.springframework.statemachine.StateContext; import com.sequenceiq.cloudbreak.cloud.context.CloudContext; import com.sequenceiq.cloudbreak.cloud.event.Payload; import com.sequenceiq.cloudbreak.cloud.model.CloudCredential; import com.sequenceiq.cloudbreak.cloud.model.CloudStack; import com.sequenceiq.cloudbreak.cloud.model.Location; import com.sequenceiq.cloudbreak.converter.spi.CredentialToCloudCredentialConverter; 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.event.StackDownscaleTriggerEvent; import com.sequenceiq.cloudbreak.core.flow2.event.StackScaleTriggerEvent; 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.StackFailureEvent; import com.sequenceiq.cloudbreak.service.stack.StackService; import com.sequenceiq.cloudbreak.service.stack.flow.StackScalingService; public abstract class AbstractStackDownscaleAction<P extends Payload> extends AbstractAction<StackDownscaleState, StackDownscaleEvent, StackScalingFlowContext, P> { protected static final String INSTANCEGROUPNAME = "INSTANCEGROUPNAME"; protected static final String INSTANCEIDS = "INSTANCEIDS"; private static final String ADJUSTMENT = "ADJUSTMENT"; @Inject private StackService stackService; @Inject private StackToCloudStackConverter cloudStackConverter; @Inject private CredentialToCloudCredentialConverter credentialConverter; @Inject private ResourceToCloudResourceConverter cloudResourceConverter; @Inject private StackScalingService stackScalingService; protected AbstractStackDownscaleAction(Class<P> payloadClass) { super(payloadClass); } @Override protected StackScalingFlowContext createFlowContext(String flowId, StateContext<StackDownscaleState, StackDownscaleEvent> stateContext, P payload) { Map<Object, Object> variables = stateContext.getExtendedState().getVariables(); Stack stack = stackService.getById(payload.getStackId()); MDCBuilder.buildMdcContext(stack); 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()); String instanceGroupName = extractInstanceGroupName(payload, variables); Set<String> instanceIds = extractInstanceIds(payload, variables, stack); Integer adjustment = extractAdjustment(payload, variables); CloudStack cloudStack = cloudStackConverter.convertForDownscale(stack, instanceIds); return new StackScalingFlowContext(flowId, stack, cloudContext, cloudCredential, cloudStack, instanceGroupName, instanceIds, adjustment); } private Integer extractAdjustment(P payload, Map<Object, Object> variables) { if (payload instanceof StackDownscaleTriggerEvent) { StackDownscaleTriggerEvent ssc = (StackDownscaleTriggerEvent) payload; Integer adjustment = ssc.getHostNames() == null ? ssc.getAdjustment() : -ssc.getHostNames().size(); variables.put(ADJUSTMENT, adjustment); return adjustment; } return getAdjustment(variables); } private Set<String> extractInstanceIds(P payload, Map<Object, Object> variables, Stack stack) { if (payload instanceof StackDownscaleTriggerEvent) { StackDownscaleTriggerEvent ssc = (StackDownscaleTriggerEvent) payload; Set<String> instanceIds; if (ssc.getHostNames() == null) { Map<String, String> unusedInstanceIds = stackScalingService.getUnusedInstanceIds(ssc.getInstanceGroup(), ssc.getAdjustment(), stack); instanceIds = new HashSet<>(unusedInstanceIds.keySet()); } else { Set<InstanceMetaData> imds = stack.getInstanceGroupByInstanceGroupName(ssc.getInstanceGroup()).getInstanceMetaData(); instanceIds = imds.stream().filter(imd -> ssc.getHostNames().contains(imd.getDiscoveryFQDN())).map(InstanceMetaData::getInstanceId) .collect(Collectors.toSet()); } variables.put(INSTANCEIDS, instanceIds); return instanceIds; } return getInstanceIds(variables); } private String extractInstanceGroupName(P payload, Map<Object, Object> variables) { if (payload instanceof StackScaleTriggerEvent) { StackScaleTriggerEvent ssc = (StackScaleTriggerEvent) payload; variables.put(INSTANCEGROUPNAME, ssc.getInstanceGroup()); return ssc.getInstanceGroup(); } return getInstanceGroupName(variables); } @Override protected Object getFailurePayload(P payload, Optional<StackScalingFlowContext> flowContext, Exception ex) { return new StackFailureEvent(payload.getStackId(), ex); } protected String getInstanceGroupName(Map<Object, Object> variables) { return (String) variables.get(INSTANCEGROUPNAME); } protected Set<String> getInstanceIds(Map<Object, Object> variables) { return (Set<String>) variables.get(INSTANCEIDS); } protected Integer getAdjustment(Map<Object, Object> variables) { return (Integer) variables.get(ADJUSTMENT); } }