package com.sequenceiq.cloudbreak.service.stack.connector.adapter;
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 static java.lang.String.format;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import javax.inject.Inject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import com.sequenceiq.cloudbreak.cloud.PlatformParameters;
import com.sequenceiq.cloudbreak.cloud.context.CloudContext;
import com.sequenceiq.cloudbreak.cloud.event.model.EventStatus;
import com.sequenceiq.cloudbreak.cloud.event.platform.CheckPlatformVariantRequest;
import com.sequenceiq.cloudbreak.cloud.event.platform.CheckPlatformVariantResult;
import com.sequenceiq.cloudbreak.cloud.event.platform.GetPlatformTemplateRequest;
import com.sequenceiq.cloudbreak.cloud.event.platform.GetPlatformTemplateResult;
import com.sequenceiq.cloudbreak.cloud.event.platform.PlatformParameterRequest;
import com.sequenceiq.cloudbreak.cloud.event.platform.PlatformParameterResult;
import com.sequenceiq.cloudbreak.cloud.event.resource.DownscaleStackRequest;
import com.sequenceiq.cloudbreak.cloud.event.resource.DownscaleStackResult;
import com.sequenceiq.cloudbreak.cloud.event.resource.TerminateStackRequest;
import com.sequenceiq.cloudbreak.cloud.event.resource.TerminateStackResult;
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.CloudStack;
import com.sequenceiq.cloudbreak.cloud.model.Location;
import com.sequenceiq.cloudbreak.cloud.model.Variant;
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.domain.Credential;
import com.sequenceiq.cloudbreak.domain.InstanceGroup;
import com.sequenceiq.cloudbreak.domain.InstanceMetaData;
import com.sequenceiq.cloudbreak.domain.Resource;
import com.sequenceiq.cloudbreak.domain.Stack;
import com.sequenceiq.cloudbreak.service.stack.connector.OperationException;
import reactor.bus.Event;
import reactor.bus.EventBus;
@Component
public class ServiceProviderConnectorAdapter {
private static final Logger LOGGER = LoggerFactory.getLogger(ServiceProviderConnectorAdapter.class);
@Inject
private EventBus eventBus;
@Inject
private StackToCloudStackConverter cloudStackConverter;
@Inject
private InstanceMetaDataToCloudInstanceConverter metadataConverter;
@Inject
private CredentialToCloudCredentialConverter credentialConverter;
@Inject
private ResourceToCloudResourceConverter cloudResourceConverter;
public Set<String> removeInstances(Stack stack, Set<String> instanceIds, String instanceGroup) {
LOGGER.debug("Assembling downscale stack event for stack: {}", 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());
List<CloudResource> resources = cloudResourceConverter.convert(stack.getResources());
List<CloudInstance> instances = new ArrayList<>();
InstanceGroup group = stack.getInstanceGroupByInstanceGroupName(instanceGroup);
for (InstanceMetaData metaData : group.getAllInstanceMetaData()) {
if (instanceIds.contains(metaData.getInstanceId())) {
CloudInstance cloudInstance = metadataConverter.convert(metaData);
instances.add(cloudInstance);
}
}
CloudStack cloudStack = cloudStackConverter.convertForDownscale(stack, instanceIds);
DownscaleStackRequest downscaleRequest = new DownscaleStackRequest(cloudContext,
cloudCredential, cloudStack, resources, instances);
LOGGER.info("Triggering downscale stack event: {}", downscaleRequest);
eventBus.notify(downscaleRequest.selector(), Event.wrap(downscaleRequest));
try {
DownscaleStackResult res = downscaleRequest.await();
LOGGER.info("Downscale stack result: {}", res);
if (res.getStatus().equals(EventStatus.FAILED)) {
LOGGER.error("Failed to downscale the stack", res.getErrorDetails());
throw new OperationException(res.getErrorDetails());
}
return instanceIds;
} catch (InterruptedException e) {
LOGGER.error("Error while downscaling the stack", e);
throw new OperationException(e);
}
}
public void deleteStack(Stack stack, Credential credential) {
LOGGER.debug("Assembling terminate stack event for stack: {}", 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());
List<CloudResource> resources = cloudResourceConverter.convert(stack.getResources());
CloudStack cloudStack = cloudStackConverter.convert(stack);
TerminateStackRequest<TerminateStackResult> terminateRequest = new TerminateStackRequest<>(cloudContext, cloudStack, cloudCredential, resources);
LOGGER.info("Triggering terminate stack event: {}", terminateRequest);
eventBus.notify(terminateRequest.selector(), Event.wrap(terminateRequest));
try {
TerminateStackResult res = terminateRequest.await();
LOGGER.info("Terminate stack result: {}", res);
if (res.getStatus().equals(EventStatus.FAILED)) {
if (res.getErrorDetails() != null) {
LOGGER.error("Failed to terminate the stack", res.getErrorDetails());
throw new OperationException(res.getErrorDetails());
}
throw new OperationException(format("Failed to terminate the stack: %s due to %s", cloudContext, res.getStatusReason()));
}
} catch (InterruptedException e) {
LOGGER.error("Error while terminating the stack", e);
throw new OperationException(e);
}
}
public void rollback(Stack stack, Set<Resource> resourceSet) {
LOGGER.info("Rollback the whole stack for {}", stack.getId());
deleteStack(stack, stack.getCredential());
}
public String getTemplate(Stack stack) {
LOGGER.debug("Get template for: {}");
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());
GetPlatformTemplateRequest getPlatformTemplateRequest = new GetPlatformTemplateRequest(cloudContext, cloudCredential);
eventBus.notify(getPlatformTemplateRequest.selector(), Event.wrap(getPlatformTemplateRequest));
try {
GetPlatformTemplateResult res = getPlatformTemplateRequest.await();
LOGGER.info("Get template result: {}", res);
if (res.getStatus().equals(EventStatus.FAILED)) {
LOGGER.error("Failed to get template", res.getErrorDetails());
throw new OperationException(res.getErrorDetails());
}
return res.getTemplate();
} catch (InterruptedException e) {
LOGGER.error("Error while getting template: " + cloudContext, e);
throw new OperationException(e);
}
}
public PlatformParameters getPlatformParameters(Stack stack) {
LOGGER.debug("Get platform parameters for: {}", 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());
PlatformParameterRequest parameterRequest = new PlatformParameterRequest(cloudContext, cloudCredential);
eventBus.notify(parameterRequest.selector(), Event.wrap(parameterRequest));
try {
PlatformParameterResult res = parameterRequest.await();
LOGGER.info("Platform parameter result: {}", res);
if (res.getStatus().equals(EventStatus.FAILED)) {
LOGGER.error("Failed to get platform parameters", res.getErrorDetails());
throw new OperationException(res.getErrorDetails());
}
return res.getPlatformParameters();
} catch (InterruptedException e) {
LOGGER.error("Error while getting platform parameters: " + cloudContext, e);
throw new OperationException(e);
}
}
public Variant checkAndGetPlatformVariant(Stack stack) {
LOGGER.debug("Get platform variant for: {}", 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());
CheckPlatformVariantRequest checkPlatformVariantRequest = new CheckPlatformVariantRequest(cloudContext, cloudCredential);
eventBus.notify(checkPlatformVariantRequest.selector(), Event.wrap(checkPlatformVariantRequest));
try {
CheckPlatformVariantResult res = checkPlatformVariantRequest.await();
LOGGER.info("Platform variant result: {}", res);
if (res.getStatus().equals(EventStatus.FAILED)) {
LOGGER.error("Failed to get platform variant", res.getErrorDetails());
throw new OperationException(res.getErrorDetails());
}
return res.getDefaultPlatformVariant();
} catch (InterruptedException e) {
LOGGER.error("Error while getting the platform variant: " + cloudContext, e);
throw new OperationException(e);
}
}
}