ca.roussil.ec2instancestarter.SimpleEc2Service.java Source code

Java tutorial

Introduction

Here is the source code for ca.roussil.ec2instancestarter.SimpleEc2Service.java

Source

/**
 * Copyright (c) 2015 Marc-Andr Roussil
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all 
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package ca.roussil.ec2instancestarter;

import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;

import com.amazonaws.AmazonServiceException;
import com.amazonaws.services.ec2.model.DescribeInstancesRequest;
import com.amazonaws.services.ec2.model.DescribeInstancesResult;
import com.amazonaws.services.ec2.model.Instance;
import com.amazonaws.services.ec2.model.InstanceState;
import com.amazonaws.services.ec2.model.InstanceStateChange;
import com.amazonaws.services.ec2.model.Reservation;
import com.amazonaws.services.ec2.model.StartInstancesRequest;
import com.amazonaws.services.ec2.model.StartInstancesResult;
import com.renatodelgaudio.awsupdate.Configuration;

public class SimpleEc2Service implements AWSEc2Service {

    private static final int WAIT_FOR_TRANSITION_INTERVAL = 5000;

    private final static Logger log = LoggerFactory.getLogger(SimpleEc2Service.class);

    @Autowired
    Configuration config;

    @Override
    public String getInstanceIp(String ec2InstanceId) {

        return getSingleEc2InstanceById(ec2InstanceId).getPublicIpAddress();
    }

    @Override
    public void startInstance(String ec2InstanceId) throws InterruptedException {

        Instance instance = getSingleEc2InstanceById(ec2InstanceId);
        InstanceState state = instance.getState();

        // different possible states: pending, running, shutting-down,
        // terminated, stopping, stopped
        String stateName = state.getName();
        if (stateName.equalsIgnoreCase("pending")) {
            log.info("startInstance: instance with id= " + ec2InstanceId
                    + " state is pending, no action was taken.");
        } else if (stateName.equalsIgnoreCase("running")) {
            log.info("startInstance: instance with id= " + ec2InstanceId
                    + " state is running, no action was taken.");
        } else if (stateName.equalsIgnoreCase("shutting-down")) {
            log.info("startInstance: instance with id= " + ec2InstanceId
                    + " state is shutting-down, no action was taken.");

            // TODO maybe we should wait for the instance to shutdown before
            // starting it again.. ?
        } else if (stateName.equalsIgnoreCase("terminated")) {
            log.info("startInstance: instance with id= " + ec2InstanceId
                    + " state is terminated, no action was taken.");

            // TODO throw error ?
        } else if (stateName.equalsIgnoreCase("stopping")) {
            log.info("startInstance: instance with id= " + ec2InstanceId
                    + " state is stopping, no action was taken.");

            // TODO maybe we should wait for the instance to stop before
            // starting it again.. ? what is the difference between
            // shutting-down and stopping ??
        } else if (stateName.equalsIgnoreCase("stopped")) {
            log.info("startInstance: instance with id= " + ec2InstanceId
                    + " state is stopped, the instance has been asked to start...");

            StartInstancesRequest startRequest = new StartInstancesRequest().withInstanceIds(ec2InstanceId);
            StartInstancesResult startResult = config.getAmazonEC2Client().startInstances(startRequest);
            List<InstanceStateChange> stateChangeList = startResult.getStartingInstances();

            waitForTransitionCompletion(stateChangeList, "running", ec2InstanceId);
        }
    }

    private Instance getSingleEc2InstanceById(String ec2InstanceId) {
        DescribeInstancesRequest describeRequest = new DescribeInstancesRequest();
        describeRequest.withInstanceIds(ec2InstanceId);
        DescribeInstancesResult result = config.getAmazonEC2Client().describeInstances(describeRequest);

        List<Reservation> reservations = result.getReservations();
        List<Instance> instances = reservations.get(0).getInstances();
        if (instances.size() == 0)
            throw new RuntimeException(
                    "There is a problem, could not find an ec2 instance for id=" + ec2InstanceId);
        return instances.get(0);
    }

    /**
     * Wait for a instance to complete transitioning (i.e. status not being in
     * INSTANCE_STATE_IN_PROGRESS_SET or the instance no longer existing).
     * 
     * @param stateChangeList
     * @param instancebuilder
     * @param instanceId
     * @param BuildLogger
     * @throws InterruptedException
     * @throws Exception
     */
    private final String waitForTransitionCompletion(List<InstanceStateChange> stateChangeList,
            final String desiredState, String instanceId) throws InterruptedException {

        Boolean transitionCompleted = false;
        InstanceStateChange stateChange = stateChangeList.get(0);
        String previousState = stateChange.getPreviousState().getName();
        String currentState = stateChange.getCurrentState().getName();
        String transitionReason = "";

        while (!transitionCompleted) {
            try {
                Instance instance = getSingleEc2InstanceById(instanceId);
                currentState = instance.getState().getName();
                if (previousState.equals(currentState)) {
                    log.info("... '" + instanceId + "' is still in state " + currentState + " ...");
                } else {
                    log.info("... '" + instanceId + "' entered state " + currentState + " ...");
                    transitionReason = instance.getStateTransitionReason();
                }
                previousState = currentState;

                if (currentState.equals(desiredState)) {
                    transitionCompleted = true;
                }
            } catch (AmazonServiceException ase) {
                log.error("Failed to describe instance '" + instanceId + "'!", ase);
                throw ase;
            }

            // Sleep for WAIT_FOR_TRANSITION_INTERVAL seconds until transition
            // has completed.
            if (!transitionCompleted) {
                Thread.sleep(WAIT_FOR_TRANSITION_INTERVAL);
            }
        }

        log.info("Transition of instance '" + instanceId + "' completed with state " + currentState + " ("
                + (StringUtils.isEmpty(transitionReason) ? "Unknown transition reason" : transitionReason) + ").");

        return currentState;
    }

}