com.carrotgarden.maven.aws.cfn.CarrotCloudForm.java Source code

Java tutorial

Introduction

Here is the source code for com.carrotgarden.maven.aws.cfn.CarrotCloudForm.java

Source

/**
 * Copyright (C) 2010-2012 Andrei Pozolotin <Andrei.Pozolotin@gmail.com>
 *
 * 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<Parameter> 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<String, String> 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<Parameter> convert(final Map<String, String> paraMap) {

        final List<Parameter> 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<StackEvent> 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;
            }

        }

    }

}