package tw.com;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import tw.com.entity.DeletionPending;
import tw.com.entity.DeletionsPending;
import tw.com.entity.StackNameAndId;
import tw.com.exceptions.CfnAssistException;
import tw.com.exceptions.NotReadyException;
import tw.com.exceptions.WrongNumberOfStacksException;
import tw.com.exceptions.WrongStackStatus;
import tw.com.repository.StackRepository;
import com.amazonaws.services.cloudformation.model.CreateStackRequest;
import com.amazonaws.services.cloudformation.model.StackEvent;
import com.amazonaws.services.cloudformation.model.StackStatus;
import com.amazonaws.services.cloudformation.model.UpdateStackRequest;
public class PollingStackMonitor extends StackMonitor {
private static final Logger logger = LoggerFactory.getLogger(PollingStackMonitor.class);
private StackRepository cfnRepository;
public PollingStackMonitor(StackRepository cfnRepository) {
this.cfnRepository = cfnRepository;
}
@Override
public String waitForCreateFinished(StackNameAndId stackId) throws WrongNumberOfStacksException, InterruptedException, WrongStackStatus {
String stackName = stackId.getStackName();
String result = cfnRepository.waitForStatusToChangeFrom(stackName, StackStatus.CREATE_IN_PROGRESS, Arrays.asList(CREATE_ABORTS));
String expected = StackStatus.CREATE_COMPLETE.toString();
if (!result.equals(expected)) {
logger.error(String.format("Failed to create stack %s, status is %s", stackId, result));
logStackEvents(cfnRepository.getStackEvents(stackName));
throw new WrongStackStatus(stackId, expected,result);
}
return result;
}
@Override
public String waitForRollbackComplete(StackNameAndId id) throws NotReadyException,
WrongNumberOfStacksException, WrongStackStatus, InterruptedException {
String stackName = id.getStackName();
String result = cfnRepository.waitForStatusToChangeFrom(stackName, StackStatus.ROLLBACK_IN_PROGRESS, Arrays.asList(ROLLBACK_ABORTS));
String complete = StackStatus.ROLLBACK_COMPLETE.toString();
if (!result.equals(complete)) {
logger.error("Expected " + complete);
logStackEvents(cfnRepository.getStackEvents(stackName));
throw new WrongStackStatus(id, complete, result);
}
return result;
}
public String waitForDeleteFinished(StackNameAndId stackId) throws WrongNumberOfStacksException, InterruptedException {
StackStatus initialStatus = StackStatus.DELETE_IN_PROGRESS;
String result = StackStatus.DELETE_FAILED.toString();
try {
result = cfnRepository.waitForStatusToChangeFrom(stackId.getStackName(), initialStatus, Arrays.asList(DELETE_ABORTS));
}
catch(com.amazonaws.AmazonServiceException awsException) {
logger.warn("Caught exception during status check", awsException);
String errorCode = awsException.getErrorCode();
// TODO should this be using statuscode=400?
if (errorCode.equals("ValidationError")) {
result = StackStatus.DELETE_COMPLETE.toString();
} else {
result = StackStatus.DELETE_FAILED.toString();
}
}
if (!result.equals(StackStatus.DELETE_COMPLETE.toString())) {
logger.error("Failed to delete stack, status is " + result);
logStackEvents(cfnRepository.getStackEvents(stackId.getStackName()));
}
return result;
}
private void logStackEvents(List<StackEvent> stackEvents) {
for(StackEvent event : stackEvents) {
logger.info(event.toString());
}
}
@Override
public void init() {
// no op for polling monitor
}
@Override
public String waitForUpdateFinished(StackNameAndId id) throws WrongNumberOfStacksException, InterruptedException, WrongStackStatus {
String stackName = id.getStackName();
String result = cfnRepository.waitForStatusToChangeFrom(stackName, StackStatus.UPDATE_IN_PROGRESS, Arrays.asList(UPDATE_ABORTS));
if (result.equals(StackStatus.UPDATE_COMPLETE_CLEANUP_IN_PROGRESS.toString())) {
logger.info("Update now in cleanup status");
result = cfnRepository.waitForStatusToChangeFrom(stackName, StackStatus.UPDATE_COMPLETE_CLEANUP_IN_PROGRESS, Arrays.asList(UPDATE_ABORTS));
}
String complete = StackStatus.UPDATE_COMPLETE.toString();
if (!result.equals(complete)) {
logger.error("Expected " + complete);
logStackEvents(cfnRepository.getStackEvents(id.getStackName()));
throw new WrongStackStatus(id, complete, result);
}
return result;
}
@Override
public List<String> waitForDeleteFinished(DeletionsPending pending, SetsDeltaIndex setDeltaIndex) {
return monitorDeletions(pending, setDeltaIndex);
}
private List<String> monitorDeletions(DeletionsPending allPending, SetsDeltaIndex setDeltaIndex) {
List<String> deletedOk = new LinkedList<String>();
try {
for(DeletionPending pending : allPending) {
StackNameAndId id = pending.getStackId();
logger.info("Now waiting for deletion of " + id);
String status = waitForDeleteFinished(id );
if (StackStatus.DELETE_COMPLETE.toString().equals(status)) {
deletedOk.add(id.getStackName());
int newDelta = pending.getDelta()-1;
if (newDelta>=0) {
logger.info("Resetting delta to " + newDelta);
setDeltaIndex.setDeltaIndex(newDelta);
}
} else {
break;
}
}
}
catch(CfnAssistException exception) {
reportDeletionIssue(exception);
} catch (InterruptedException exception) {
reportDeletionIssue(exception);
}
return deletedOk;
}
private void reportDeletionIssue(Exception exception) {
logger.error("Unable to wait for stack deletion ",exception);
logger.error("Please manually check stack deletion and delta index values");
}
@Override
public void addMonitoringTo(CreateStackRequest createStackRequest) {
// does nothing in this implementation
}
@Override
public void addMonitoringTo(UpdateStackRequest updateStackRequest)
throws NotReadyException {
// does nothing in this implementation
}
}