fr.xebia.training.troubleshooting.TroubleshootingTrainingInfrastructureCreator.java Source code

Java tutorial

Introduction

Here is the source code for fr.xebia.training.troubleshooting.TroubleshootingTrainingInfrastructureCreator.java

Source

/*
 * Copyright 2008-2010 Xebia and the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package fr.xebia.training.troubleshooting;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.annotation.Nonnull;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.PropertiesCredentials;
import com.amazonaws.services.ec2.AmazonEC2;
import com.amazonaws.services.ec2.AmazonEC2Client;
import com.amazonaws.services.ec2.model.CreateTagsRequest;
import com.amazonaws.services.ec2.model.DescribeInstancesRequest;
import com.amazonaws.services.ec2.model.DescribeTagsRequest;
import com.amazonaws.services.ec2.model.Filter;
import com.amazonaws.services.ec2.model.Instance;
import com.amazonaws.services.ec2.model.InstanceType;
import com.amazonaws.services.ec2.model.Reservation;
import com.amazonaws.services.ec2.model.RunInstancesRequest;
import com.amazonaws.services.ec2.model.RunInstancesResult;
import com.amazonaws.services.ec2.model.Tag;
import com.amazonaws.services.ec2.model.TagDescription;
import com.amazonaws.services.rds.AmazonRDS;
import com.amazonaws.services.rds.AmazonRDSClient;
import com.amazonaws.services.rds.model.CreateDBInstanceRequest;
import com.amazonaws.services.rds.model.DBInstance;
import com.amazonaws.services.rds.model.DBInstanceNotFoundException;
import com.amazonaws.services.rds.model.DescribeDBInstancesRequest;
import com.amazonaws.services.rds.model.DescribeDBInstancesResult;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.MapMaker;
import com.google.common.collect.Maps;

import fr.xebia.cloud.amazon.aws.tools.AmazonAwsFunctions;
import fr.xebia.cloud.amazon.aws.tools.AmazonAwsUtils;
import fr.xebia.cloud.cloudinit.CloudInitUserDataBuilder;
import fr.xebia.cloud.cloudinit.FreemarkerUtils;

public class TroubleshootingTrainingInfrastructureCreator {

    AmazonEC2 ec2;
    AmazonRDS rds;

    public static void main(String[] args) {
        TroubleshootingTrainingInfrastructureCreator creator = new TroubleshootingTrainingInfrastructureCreator();
        creator.createTrainingInfrastructure();
    }

    public TroubleshootingTrainingInfrastructureCreator() {
        try {
            InputStream credentialsAsStream = Thread.currentThread().getContextClassLoader()
                    .getResourceAsStream("AwsCredentials.properties");
            Preconditions.checkNotNull(credentialsAsStream,
                    "File 'AwsCredentials.properties' NOT found in the classpath");
            AWSCredentials credentials = new PropertiesCredentials(credentialsAsStream);
            ec2 = new AmazonEC2Client(credentials);
            ec2.setEndpoint("ec2.eu-west-1.amazonaws.com");
            rds = new AmazonRDSClient(credentials);
            rds.setEndpoint("rds.eu-west-1.amazonaws.com");
        } catch (IOException e) {
            throw Throwables.propagate(e);
        }
    }

    private final Logger logger = LoggerFactory.getLogger(getClass());

    public void generateDocs() {
        Filter filter = new Filter("tag:TrainingSession", Lists.newArrayList("Troubleshooting"));
        List<Reservation> reservations = ec2.describeInstances(new DescribeInstancesRequest().withFilters(filter))
                .getReservations();
        Iterable<Instance> instances = AmazonAwsUtils.toEc2Instances(reservations);

        Iterable<Instance> runningInstances = Iterables.filter(instances,
                AmazonAwsUtils.PREDICATE_RUNNING_OR_PENDING_INSTANCE);
        runningInstances = AmazonAwsUtils.awaitForEc2Instances(runningInstances, ec2);

        Map<String, Instance> runningInstancesByInstanceId = Maps.uniqueIndex(runningInstances,
                AmazonAwsFunctions.EC2_INSTANCE_TO_INSTANCE_ID);

        List<String> runningInstanceIds = Lists.newArrayList(
                Iterables.transform(runningInstances, AmazonAwsFunctions.EC2_INSTANCE_TO_INSTANCE_ID));

        List<TagDescription> tags = ec2
                .describeTags(new DescribeTagsRequest().withFilters(new Filter("resource-id", runningInstanceIds)))
                .getTags();
        Map<String, Map<String, String>> tagsByInstanceId = new MapMaker()
                .makeComputingMap(new Function<String, Map<String, String>>() {
                    @Override
                    public Map<String, String> apply(String instanceId) {
                        return Maps.newHashMap();
                    }
                });

        for (TagDescription tag : tags) {
            tagsByInstanceId.get(tag.getResourceId()).put(tag.getKey(), tag.getValue());
        }

        Map<String, Map<String, Object>> tomcatTagsPerTeamIdentifier = Maps.newHashMap();

        for (Map.Entry<String, Map<String, String>> entry : tagsByInstanceId.entrySet()) {
            String instanceId = entry.getKey();
            Map<String, String> instanceTags = entry.getValue();

            String teamIdentifier = instanceTags.get("TeamIdentifier");
            Instance tomcatInstance = runningInstancesByInstanceId.get(instanceId);

            Map<String, Object> tomcatTags = Maps.newHashMap();
            tomcatTags.putAll(instanceTags);
            tomcatTags.put("PublicDnsName", tomcatInstance.getPublicDnsName());
            tomcatTags.put("Instance", tomcatInstance);

            tomcatTagsPerTeamIdentifier.put(teamIdentifier, tomcatTags);
        }

        Map<String, Object> rootMap = Maps.newHashMap();
        rootMap.put("infrastructures", tomcatTagsPerTeamIdentifier);
        String wikiPage = FreemarkerUtils.generate(rootMap, "/fr/xebia/training/troubleshooting/wiki-page.ftl");
        System.out.println(wikiPage);
    }

    public void createTrainingInfrastructure() {
        String applicationId = "petclinic";
        String dbInstanceIdentifier = applicationId;
        String dbName = applicationId;
        String jdbcUsername = applicationId;
        String jdbcPassword = applicationId;

        // CREATE DATABASE
        DBInstance petclinicDatabase = createMySqlDatabaseInstanceIfNotExists(dbInstanceIdentifier, dbName,
                jdbcUsername, jdbcPassword);
        petclinicDatabase = AmazonAwsUtils.awaitForDbInstanceCreation(petclinicDatabase, rds);

        logger.info("MySQL instance: {}", petclinicDatabase);

        Collection<String> teamIdentifiers = Lists.newArrayList("clc", "plo");

        // CREATE TOMCAT SERVERS
        List<Instance> tomcatInstances = launchTomcatServers(petclinicDatabase, applicationId, jdbcUsername,
                jdbcPassword, teamIdentifiers);

        logger.info("Created {}", tomcatInstances);

        // GENERATE WIKI DOCUMENTATION PAGE
        generateDocs();

    }

    /**
     * Returns a base-64 version of the mime-multi-part cloud-init file to put
     * in the user-data attribute of the ec2 instance.
     * 
     * @param distribution
     * @param dbInstance
     * @param jdbcUsername
     * @param jdbcPassword
     * @param warUrl
     * @return
     */
    @Nonnull
    protected String buildCloudInitUserData(DBInstance dbInstance, String jdbcUsername, String jdbcPassword) {

        dbInstance = AmazonAwsUtils.awaitForDbInstanceCreation(dbInstance, rds);

        // SHELL SCRIPT
        Map<String, Object> rootMap = Maps.newHashMap();

        Map<String, String> systemProperties = Maps.newHashMap();

        String warUrl = "http://xebia-france.googlecode.com/svn/repository/maven2/fr/xebia/demo/xebia-petclinic/1.0.2/xebia-petclinic-1.0.2.war";
        String rootContext = "/petclinic";

        rootMap.put("warUrl", warUrl);
        rootMap.put("warName", rootContext + ".war");

        rootMap.put("systemProperties", systemProperties);
        String jdbcUrl = "jdbc:mysql://" + dbInstance.getEndpoint().getAddress() + ":"
                + dbInstance.getEndpoint().getPort() + "/" + dbInstance.getDBName();
        systemProperties.put("jdbc.url", jdbcUrl);
        systemProperties.put("jdbc.username", jdbcUsername);
        systemProperties.put("jdbc.password", jdbcPassword);

        String shellScript = FreemarkerUtils.generate(rootMap,
                "/fr/xebia/training/troubleshooting/provision_tomcat.py.ftl");

        // CLOUD CONFIG
        InputStream cloudConfigAsStream = Thread.currentThread().getContextClassLoader()
                .getResourceAsStream("fr/xebia/training/troubleshooting/cloud-config-amzn-linux-tomcat.txt");
        Preconditions.checkNotNull(cloudConfigAsStream,
                "'fr/xebia/training/troubleshooting/cloud-config-amzn-linux-tomcat.txt' not found in path");
        Readable cloudConfig = new InputStreamReader(cloudConfigAsStream);

        return CloudInitUserDataBuilder.start() //
                .addCloudConfig(cloudConfig) //
                .addShellScript(shellScript) //
                .buildBase64UserData();

    }

    public List<Instance> launchTomcatServers(DBInstance petclinicDatabase, String applicationId,
            String jdbcUsername, String jdbcPassword, Collection<String> teamIdentifiers) {

        AmazonAwsUtils.terminateInstancesByRole("training,troubleshooting,tomcat", ec2);

        String userData = buildCloudInitUserData(petclinicDatabase, jdbcUsername, jdbcPassword);

        // FIXME : use M1SMALL
        logger.warn("FIXME : use M1SMALL");

        // CREATE EC2 INSTANCES
        RunInstancesRequest runInstancesRequest = new RunInstancesRequest() //
                .withInstanceType(InstanceType.T1Micro.toString())//
                .withImageId("ami-6c3f0d18") //
                .withMinCount(teamIdentifiers.size()) //
                .withMaxCount(teamIdentifiers.size()) //
                .withSecurityGroupIds("accept-all") //
                .withKeyName("training-troubleshooting") //
                .withUserData(userData) //

        ;
        RunInstancesResult runInstances = ec2.runInstances(runInstancesRequest);
        List<Instance> instances = runInstances.getReservation().getInstances();

        // TAG EC2 INSTANCES
        Iterator<String> teamIdentifierIterator = teamIdentifiers.iterator();

        for (Instance instance : instances) {
            String teamIdentifier = teamIdentifierIterator.next();

            CreateTagsRequest createTagsRequest = new CreateTagsRequest();
            createTagsRequest.withResources(instance.getInstanceId()) //
                    .withTags(//
                            new Tag("Name", applicationId + "-" + teamIdentifier), //
                            new Tag("Role", "training,troubleshooting,tomcat"), //
                            new Tag("TeamIdentifier", teamIdentifier), //
                            new Tag("TrainingSession", "Troubleshooting"));
            ec2.createTags(createTagsRequest);

        }

        logger.info("Created {}", instances);

        return instances;

    }

    @Nonnull
    public DBInstance createMySqlDatabaseInstanceIfNotExists(String dbInstanceIdentifier, String dbName,
            String jdbcUsername, String jdbcPassword) {
        logger.info("ENFORCE DATABASE {}/{}", dbInstanceIdentifier, dbName);

        DescribeDBInstancesRequest describeDbInstanceRequest = new DescribeDBInstancesRequest()
                .withDBInstanceIdentifier(dbInstanceIdentifier);
        try {
            DescribeDBInstancesResult describeDBInstances = rds.describeDBInstances(describeDbInstanceRequest);
            if (describeDBInstances.getDBInstances().isEmpty()) {
                // unexpected, db does not exist but we expected a
                // DBInstanceNotFoundException
            } else {
                DBInstance dbInstance = Iterables.getFirst(describeDBInstances.getDBInstances(), null);
                logger.info("Database instance '" + dbInstanceIdentifier + "' already exists! Skip creation");
                return dbInstance;
            }
        } catch (DBInstanceNotFoundException e) {
            // good, db does not exist
        }

        CreateDBInstanceRequest createDBInstanceRequest = new CreateDBInstanceRequest() //
                .withDBInstanceIdentifier(dbInstanceIdentifier) //
                .withDBName(dbName) //
                .withEngine("MySQL") //
                .withEngineVersion("5.1.57") //
                .withDBInstanceClass("db.m1.small") //
                .withMasterUsername(jdbcUsername) //
                .withMasterUserPassword(jdbcPassword) //
                .withAllocatedStorage(5) //
                .withBackupRetentionPeriod(0) //
                .withDBSecurityGroups("default") //
                .withLicenseModel("general-public-license") //
        ;

        DBInstance dbInstance = rds.createDBInstance(createDBInstanceRequest);
        logger.info("Created {}", dbInstance);
        return dbInstance;
    }

}