package tw.com; import com.amazonaws.services.cloudformation.AmazonCloudFormationClient; import com.amazonaws.services.cloudformation.model.DeleteStackRequest; import com.amazonaws.services.cloudformation.model.DescribeStacksResult; import com.amazonaws.services.cloudformation.model.Stack; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import tw.com.entity.StackNameAndId; import java.util.LinkedList; import java.util.List; public class DeletesStacks { private static final int TIMEOUT_INCREMENT_MS = 500; private static final int INITIAL_TIMEOUT_MS = 1000; private static final Logger logger = LoggerFactory.getLogger(DeletesStacks.class); private AmazonCloudFormationClient cfnClient; private LinkedList<String> deleteIfPresent; private LinkedList<String> deleteIfPresentNonBlocking; public DeletesStacks(AmazonCloudFormationClient cfnClient) { this.cfnClient = cfnClient; deleteIfPresent = new LinkedList<>(); deleteIfPresentNonBlocking = new LinkedList<>(); } public DeletesStacks ifPresent(String stackName) { deleteIfPresent.add(stackName); return this; } public DeletesStacks ifPresent(StackNameAndId stackId) { return ifPresent(stackId.getStackName()); } public void act() { List<String> currentStacks = fetchCurrentStacks(); List<String> deletionList = new LinkedList<>(); List<String> deletionListNonBlocking = new LinkedList<>(); for(String stackName : currentStacks) { if (deleteIfPresent.contains(stackName)) { deletionList.add(stackName); } else if (deleteIfPresentNonBlocking.contains(stackName)) { deletionListNonBlocking.add(stackName); } } try { if (deletionList.isEmpty() && deletionListNonBlocking.isEmpty()) { logger.info("No stacks need deleting"); } else { requestDeletion(deletionListNonBlocking); deleteStacks(deletionList); } } catch (InterruptedException e) { logger.error(e.getMessage()); logger.error(e.getStackTrace().toString()); } } private List<String> fetchCurrentStacks() { List<String> current = new LinkedList<>(); DescribeStacksResult result = cfnClient.describeStacks(); for(Stack stack : result.getStacks()) { current.add(stack.getStackName()); } return current; } private void deleteStacks(List<String> deletionList) throws InterruptedException { if (deletionList.isEmpty()) { return; } requestDeletion(deletionList); List<String> present = fetchCurrentStacks(); int count = EnvironmentSetupForTests.DELETE_RETRY_LIMIT; long timeout = INITIAL_TIMEOUT_MS; while (containsAnyOf(present,deletionList) && (count>0)) { logger.info(String.format("Waiting for stack deletion (check %s/%s). Present: %s Deleting: %s", (EnvironmentSetupForTests.DELETE_RETRY_LIMIT-count), EnvironmentSetupForTests.DELETE_RETRY_LIMIT, present.size(), deletionList.size())); Thread.sleep(timeout); count--; if (timeout<EnvironmentSetupForTests.DELETE_RETRY_MAX_TIMEOUT_MS) { timeout = timeout + TIMEOUT_INCREMENT_MS; } present = fetchCurrentStacks(); } if (count==0) { logger.warn("Timed out waiting for deletions"); } else { logger.info("Deleted stacks"); } for(String unwanted : deletionList) { if (present.contains(unwanted)) { logger.error("Stack was not deleted yet: " + unwanted); } } } private void requestDeletion(List<String> deletionList) { for(String stackName : deletionList) { DeleteStackRequest request = new DeleteStackRequest(); request.setStackName(stackName); cfnClient.deleteStack(request); logger.info("Requested deletion of " + stackName); } } private boolean containsAnyOf(List<String> present, List<String> deletionList) { for(String unwanted : deletionList) { if (present.contains(unwanted)) { return true; } } return false; } }