All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.carrotgarden.maven.aws.cfn.CarrotCloudForm Maven / Gradle / Ivy

There is a newer version: 1.2.6
Show newest version
/**
 * Copyright (C) 2010-2012 Andrei Pozolotin 
 *
 * All rights reserved. Licensed under the OSI BSD License.
 *
 * http://www.opensource.org/licenses/bsd-license.php
 */
package com.carrotgarden.maven.aws.cfn;

import java.util.Collections;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;

import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.services.cloudformation.AmazonCloudFormation;
import com.amazonaws.services.cloudformation.AmazonCloudFormationAsyncClient;
import com.amazonaws.services.cloudformation.model.CreateStackRequest;
import com.amazonaws.services.cloudformation.model.DeleteStackRequest;
import com.amazonaws.services.cloudformation.model.DescribeStackEventsRequest;
import com.amazonaws.services.cloudformation.model.DescribeStackEventsResult;
import com.amazonaws.services.cloudformation.model.DescribeStacksRequest;
import com.amazonaws.services.cloudformation.model.DescribeStacksResult;
import com.amazonaws.services.cloudformation.model.Parameter;
import com.amazonaws.services.cloudformation.model.Stack;
import com.amazonaws.services.cloudformation.model.StackEvent;
import com.amazonaws.services.cloudformation.model.StackStatus;
import com.amazonaws.services.cloudformation.model.UpdateStackRequest;
import com.google.common.collect.Lists;

/**
 * 
 * @author Andrei Pozolotin
 * 
 * @author Erick Dovale
 *         https://github.com/Syncapse/jenkins-cloudformation-plugin
 */
public class CarrotCloudForm {

	private final AmazonCloudFormation amazonClient;

	private final AWSCredentials credentials;
	private final String endpoint;
	private final Logger logger;

	private final String name;

	private final List paramList;

	private final String template;

	private final long timeout;

	private final long waitBetweenAttempts;

	public CarrotCloudForm(final Logger logger, final String stackName,
			final String stackTemplate, final Map stackParams,
			final long timeout, final AWSCredentials credentials,
			final String endpoint) {

		this.logger = logger;

		this.name = stackName;
		this.template = stackTemplate;
		this.paramList = convert(stackParams);

		this.credentials = credentials;

		this.timeout = timeout;

		this.endpoint = endpoint;

		this.waitBetweenAttempts = 10; // query every 10s

		this.amazonClient = newClient(); // keep last

	}

	private List convert(final Map paraMap) {

		final List list = Lists.newArrayList();

		if (paraMap == null || paraMap.values().size() == 0) {
			return list;
		}

		for (final String name : paraMap.keySet()) {

			final Parameter parameter = new Parameter();
			parameter.setParameterKey(name);
			parameter.setParameterValue(paraMap.get(name));

			list.add(parameter);

		}

		return list;

	}

	public Stack findStack() throws Exception {

		final DescribeStacksRequest request = new DescribeStacksRequest();

		final DescribeStacksResult result = amazonClient
				.describeStacks(request);

		for (final Stack stack : result.getStacks()) {
			if (name.equals(stack.getStackName())) {
				return stack;
			}
		}

		return null;

	}

	private boolean isStackValid(final Stack stack) {
		return stack != null;
	}

	private boolean isTimeoutPending(final long startTime) {
		return (System.currentTimeMillis() - startTime) > (timeout * 1000);
	}

	public void logParamList() {

		for (final Parameter param : paramList) {

			logger.info(//
			param.getParameterKey() + "=" + param.getParameterValue());

		}

	}

	private AmazonCloudFormation newClient() {

		final AmazonCloudFormation amazonClient = new AmazonCloudFormationAsyncClient(
				credentials);

		logger.info("stack endpoint : {}", endpoint);

		amazonClient.setEndpoint(endpoint);

		return amazonClient;

	}

	private Stack newStackWithStatus(final StackStatus status,
			final String reason) {

		final Stack stack = new Stack();

		stack.setStackName(name);
		stack.setStackStatus(status);
		stack.setStackStatusReason(reason);

		return stack;

	}

	private void printStackEvents() {

		final DescribeStackEventsRequest request = new DescribeStackEventsRequest();

		request.withStackName(name);

		final DescribeStackEventsResult describeStackEvents = amazonClient
				.describeStackEvents(request);

		final List stackEvents = describeStackEvents
				.getStackEvents();

		Collections.reverse(stackEvents);

		logger.info("stack events:");

		for (final StackEvent event : stackEvents) {

			final StringBuilder text = new StringBuilder(128);

			text.append("\n\t");
			text.append("time=");
			text.append(event.getTimestamp());

			text.append("\n\t");
			text.append("id=");
			text.append(event.getEventId());

			text.append("\n\t");
			text.append("type=");
			text.append(event.getResourceType());

			text.append("\n\t");
			text.append("status=");
			text.append(event.getResourceStatus());

			text.append("\n\t");
			text.append("reason=");
			text.append(event.getResourceStatusReason());

			logger.info("event {}", text);

		}

	}

	private void sleep() throws Exception {
		try {
			Thread.sleep(waitBetweenAttempts * 1000);
		} catch (final InterruptedException ie) {
			throw new IllegalStateException("operation interrupted; "
					+ "resources are left in inconsistent state; "
					+ "requires manual intervention");
		}
	}

	/**
	 */
	public Stack stackCreate() throws Exception {

		final CreateStackRequest request = new CreateStackRequest();

		request.withStackName(name);
		request.withParameters(paramList);
		request.withTemplateBody(template);

		amazonClient.createStack(request);

		final Stack stack = waitForStackCreate();

		return stack;

	}

	/**
	 */
	public Stack stackDelete() throws Exception {

		final DeleteStackRequest request = new DeleteStackRequest();

		request.withStackName(name);

		amazonClient.deleteStack(request);

		final Stack stack = waitForStackDelete();

		return stack;

	}

	public Stack stackUpdate() throws Exception {

		final UpdateStackRequest request = new UpdateStackRequest();

		request.withStackName(name);
		request.withParameters(paramList);
		request.withTemplateBody(template);

		amazonClient.updateStack(request);

		final Stack stack = waitForStackUpdate();

		return stack;

	}

	private Stack waitForStackCreate() throws Exception {

		final long timeStart = System.currentTimeMillis();

		while (true) {

			if (isTimeoutPending(timeStart)) {
				return newStackWithStatus(StackStatus.CREATE_FAILED,
						"stack create timeout");
			}

			Stack stack = null;
			try {
				stack = findStack();
			} catch (final Exception e) {
				return newStackWithStatus(StackStatus.CREATE_FAILED,
						e.toString());
			}

			if (!isStackValid(stack)) {
				return newStackWithStatus(StackStatus.CREATE_FAILED,
						"stack create invalid/missing");
			}

			final StackStatus status = StackStatus.fromValue(stack
					.getStackStatus());

			switch (status) {
			case CREATE_IN_PROGRESS:
				final long timeCurrent = System.currentTimeMillis();
				final long timeDiff = timeCurrent - timeStart;
				logger.info("stack create in progress; time=" + timeDiff / 1000);
				sleep();
				continue;
			case CREATE_COMPLETE:
				logger.info("stack create success");
				printStackEvents();
				return stack;
			default:
				logger.error("stack create failure");
				return stack;
			}

		}

	}

	private Stack waitForStackDelete() throws Exception {

		final long timeStart = System.currentTimeMillis();

		while (true) {

			if (isTimeoutPending(timeStart)) {
				return newStackWithStatus(StackStatus.DELETE_FAILED,
						"stack delete timeout");
			}

			Stack stack = null;
			try {
				stack = findStack();
			} catch (final Exception e) {
				return newStackWithStatus(StackStatus.DELETE_FAILED,
						e.toString());
			}

			if (!isStackValid(stack)) {
				return newStackWithStatus(StackStatus.DELETE_COMPLETE,
						"stack delete invalid/missing");
			}

			final StackStatus status = StackStatus.fromValue(stack
					.getStackStatus());

			switch (status) {
			case DELETE_IN_PROGRESS:
				final long timeCurrent = System.currentTimeMillis();
				final long timeDiff = timeCurrent - timeStart;
				logger.info("stack delete in progress; time=" + timeDiff / 1000);
				sleep();
				continue;
			case DELETE_COMPLETE:
				logger.info("stack delete complete");
				printStackEvents();
				return stack;
			default:
				logger.error("stack delete failed");
				return stack;
			}

		}

	}

	private Stack waitForStackUpdate() throws Exception {

		final long timeStart = System.currentTimeMillis();

		while (true) {

			if (isTimeoutPending(timeStart)) {
				return newStackWithStatus(StackStatus.UPDATE_ROLLBACK_FAILED,
						"stack update timeout");
			}

			Stack stack = null;
			try {
				stack = findStack();
			} catch (final Exception e) {
				return newStackWithStatus(StackStatus.UPDATE_ROLLBACK_FAILED,
						e.toString());
			}

			if (!isStackValid(stack)) {
				return newStackWithStatus(StackStatus.UPDATE_ROLLBACK_FAILED,
						"stack update invalid/missing");
			}

			final StackStatus status = StackStatus.fromValue(stack
					.getStackStatus());

			switch (status) {
			case UPDATE_IN_PROGRESS:
				final long timeCurrent = System.currentTimeMillis();
				final long timeDiff = timeCurrent - timeStart;
				logger.info("stack update in progress; time=" + timeDiff / 1000);
				sleep();
				continue;
			case UPDATE_COMPLETE:
				logger.info("stack update complete");
				printStackEvents();
				return stack;
			default:
				logger.error("stack updtae failed");
				return stack;
			}

		}

	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy