package com.sequenceiq.cloudbreak.cloud.aws.task;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.services.cloudformation.AmazonCloudFormationClient;
import com.amazonaws.services.cloudformation.model.DescribeStackEventsRequest;
import com.amazonaws.services.cloudformation.model.DescribeStacksRequest;
import com.amazonaws.services.cloudformation.model.Stack;
import com.amazonaws.services.cloudformation.model.StackEvent;
import com.amazonaws.services.cloudformation.model.StackStatus;
import com.sequenceiq.cloudbreak.cloud.context.AuthenticatedContext;
import com.sequenceiq.cloudbreak.cloud.exception.CloudConnectorException;
import com.sequenceiq.cloudbreak.cloud.task.PollBooleanStateTask;
abstract class AbstractAwsStackStatusCheckerTask extends PollBooleanStateTask {
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractAwsStackStatusCheckerTask.class);
private final AmazonCloudFormationClient cfClient;
private final StackStatus successStatus;
private final StackStatus errorStatus;
private final List<StackStatus> stackErrorStatuses;
private final String cloudFormationStackName;
private final DescribeStacksRequest describeStacksRequest;
private final DescribeStackEventsRequest stackEventsRequest;
AbstractAwsStackStatusCheckerTask(AuthenticatedContext authenticatedContext, AmazonCloudFormationClient cfClient, StackStatus successStatus,
StackStatus errorStatus, List<StackStatus> stackErrorStatuses, String cloudFormationStackName, boolean cancellable) {
super(authenticatedContext, cancellable);
this.cfClient = cfClient;
this.successStatus = successStatus;
this.errorStatus = errorStatus;
this.stackErrorStatuses = stackErrorStatuses;
this.cloudFormationStackName = cloudFormationStackName;
describeStacksRequest = new DescribeStacksRequest().withStackName(cloudFormationStackName);
stackEventsRequest = new DescribeStackEventsRequest().withStackName(cloudFormationStackName);
}
@Override
public Boolean call() {
LOGGER.info("Checking if AWS CloudFormation stack '{}' reached status '{}'", cloudFormationStackName, successStatus);
try {
com.amazonaws.services.cloudformation.model.Stack cfStack = cfClient.describeStacks(describeStacksRequest).getStacks().get(0);
List<StackEvent> stackEvents = cfClient.describeStackEvents(stackEventsRequest).getStackEvents();
return doCheck(cfStack, stackEvents);
} catch (AmazonServiceException e) {
return handleError(e);
}
}
protected boolean isSuccess(Stack cfStack, List<StackEvent> stackEvents) {
if (!stackEvents.isEmpty() && cfStack != null) {
StackStatus cfStackStatus = StackStatus.valueOf(cfStack.getStackStatus());
if (stackErrorStatuses.contains(cfStackStatus)) {
throw new CloudConnectorException(getErrorMessage(errorStatus.toString(), getErrorCauseStatusReason(stackEvents, errorStatus)));
} else if (cfStackStatus.equals(successStatus)) {
return true;
}
}
return false;
}
protected String getErrorMessage(String state, String reason) {
return String.format("AWS CloudFormation stack reached an error state: %s reason: %s", state, reason);
}
private String getErrorCauseStatusReason(List<StackEvent> stackEvents, StackStatus errorStatus) {
StackEvent cause = null;
for (StackEvent event : stackEvents) {
if (event.getResourceStatus().equals(errorStatus.toString())) {
if (cause == null) {
cause = event;
} else if (cause.getTimestamp().getTime() > event.getTimestamp().getTime()) {
cause = event;
}
}
}
return cause == null ? "unknown" : cause.getResourceStatusReason();
}
abstract boolean doCheck(com.amazonaws.services.cloudformation.model.Stack cfStack, List<StackEvent> stackEvents);
abstract boolean handleError(AmazonServiceException e);
}