/*
* 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;
}
}