package com.wreckingballmedia.aws;
import java.io.File;
import java.io.FileNotFoundException;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.amazonaws.auth.BasicAWSCredentials;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.amazonaws.AmazonClientException;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.services.elasticbeanstalk.AWSElasticBeanstalk;
import com.amazonaws.services.elasticbeanstalk.AWSElasticBeanstalkClient;
import com.amazonaws.services.elasticbeanstalk.model.CreateApplicationVersionRequest;
import com.amazonaws.services.elasticbeanstalk.model.CreateApplicationVersionResult;
import com.amazonaws.services.elasticbeanstalk.model.S3Location;
import com.amazonaws.services.elasticbeanstalk.model.UpdateEnvironmentRequest;
import com.amazonaws.services.elasticbeanstalk.model.UpdateEnvironmentResult;
import com.amazonaws.services.s3.model.ProgressEvent;
import com.amazonaws.services.s3.model.ProgressListener;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.amazonaws.services.s3.transfer.TransferManager;
import com.amazonaws.services.s3.transfer.Upload;
/**
* This class deploys a given war as a new version in an Elastic Beanstalk environment.
*
* It will first upload the given war to S3,
* then add it to Elastic Beanstalk as a new version with the current date,
* and finally deploy it in the Elastic Beanstalk "test" environment.
*
* @author greenlee - I'm not a Java guru. Room for improvement? LMK! Thanks.
*
*/
public class AWSDeployer
{
private static final Log log = LogFactory.getLog(AWSDeployer.class);
private AWSCredentials credentials;
private String versionLabel;
private String bucketName;
private String warName;
private String environmentName;
private String applicationName;
private Upload upload;
private AWSElasticBeanstalk beanstalk;
// Version, S3Bucket, Filename, Appname, Envname
public AWSDeployer(
String versionLabel
,String bucketName
,String warName
,String applicationName
,String environmentName
,String accessKey
,String secretKey) throws Exception
{
credentials = new BasicAWSCredentials(accessKey,secretKey);
beanstalk = new AWSElasticBeanstalkClient(credentials);
this.versionLabel = versionLabel;
this.bucketName = bucketName;
this.warName = warName;
this.applicationName = applicationName;
this.environmentName = environmentName;
}
public void execute(final String fileName, final FinishedListener listener) throws FileNotFoundException
{
// Upload the new version to S3 so AWS can access it.
File warFile = new File(fileName);
if (!warFile.exists())
{
throw new FileNotFoundException();
}
// A ProgressListener will receive updates about the uploading
// of the file, and we can know when it is finished
ProgressListener progressListener = new ProgressListener()
{
public void progressChanged(ProgressEvent progressEvent)
{
if (upload == null) return;
switch (progressEvent.getEventCode())
{
case ProgressEvent.COMPLETED_EVENT_CODE:
// Completed uploading war! Create a new version in Beanstalk using it.
log.info(fileName + " has been uploaded.");
try {
createApplicationVersion();
deployVersion();
} catch (AmazonClientException e) {
if (listener !=null) listener.failed(e);
}
break;
case ProgressEvent.FAILED_EVENT_CODE:
try {
AmazonClientException e = upload.waitForException();
log.error("Unable to upload file "+fileName+" to Amazon S3: " + e.getMessage(), e);
} catch (InterruptedException e) {}
break;
}
}
};
TransferManager transferManager = new TransferManager(credentials);
PutObjectRequest request = new PutObjectRequest(bucketName, warName, warFile).withProgressListener(progressListener);
// create the bucket if it doesn't already exist
createS3Bucket(transferManager);
log.info("Uploading war to S3...");
upload = transferManager.upload(request);
}
/**
*
* @param fileName the file location of the war to upload
*/
public void execute(final String fileName) throws FileNotFoundException
{
execute(fileName, null);
}
/**
* Create an Elastic Beanstalk application version for this war,
* indicating its location in S3.
*/
private void createApplicationVersion() throws AmazonClientException
{
SimpleDateFormat friendlyDateFormat = new SimpleDateFormat("dd MMM yyy HH:mm");
log.info("Creating application version " + versionLabel);
CreateApplicationVersionRequest request = new CreateApplicationVersionRequest(applicationName, versionLabel);
request.setDescription("Auto deployment on " + friendlyDateFormat.format(new Date()));
request.setSourceBundle(new S3Location(bucketName, warName));
request.setAutoCreateApplication(true);
try {
CreateApplicationVersionResult result = beanstalk.createApplicationVersion(request);
log.info("Version " + result.getApplicationVersion().getVersionLabel() + " has been created.");
log.info("Version details: " + result.getApplicationVersion());
} catch (AmazonServiceException exA) {
log.info("AmazonServiceException! Now logging details: Error Code: " + exA.getErrorCode() + "; Error Type: " + exA.getErrorType() + "; Message: " + exA.getMessage());
System.exit(1);
} catch (AmazonClientException exB) {
log.info("AmazonClientException! Now logging details: Error Message: " + exB.getMessage());
System.exit(1);
} catch (Exception ex) {
log.info("General Exception! Now logging details: Error Message: " + ex.getMessage());
System.exit(1);
}
log.info("Application version created.");
}
/**
* Deploy the newly created version to the test environment.
*/
private void deployVersion() throws AmazonClientException
{
log.info("Updating environment "+ environmentName +" with version " + versionLabel);
UpdateEnvironmentRequest request = new UpdateEnvironmentRequest();
request.setEnvironmentName(environmentName);
request.setVersionLabel(versionLabel);
UpdateEnvironmentResult result = beanstalk.updateEnvironment(request);
log.info("SUCCESS! Version deploy requested. Application: " + applicationName + "; Environment: " + environmentName + " is now " + result.getStatus());
System.exit(0);
}
private void createS3Bucket(TransferManager transferManager)
{
try {
if (transferManager.getAmazonS3Client().doesBucketExist(bucketName) == false) {
transferManager.getAmazonS3Client().createBucket(bucketName);
}
} catch (AmazonClientException ace) {
log.error("Unable to create a new Amazon S3 bucket: " + ace.getMessage(), ace);
}
}
/**
* @param args Multiple arguments containing in order: path-to-WAR, version, S3Bucket, Application, Environment
*/
public static void main(String[] args) throws Exception
{
log.info("Elastic Bean Stalk Deployment by Wrecking Ball Media (v1.1 12/27/2012)");
if (args.length < 6)
{
System.out.println("Required arguments: Path to package, Version, S3Bucket, Application Name, Environment Name, Path to Credentials File");
System.exit(0);
}
String fileName = args[0];
String versionLabel = args[1];
String S3Bucket = args[2];
String ApplicationName = args[3];
String EnvironmentName = args[4];
String accessKey = args[5];
String secretKey = args[6];
FinishedListener listener = new FinishedListener()
{
@Override
public void succeeded()
{
System.exit(0);
}
@Override
public void failed(Throwable e)
{
System.exit(1);
}
};
try {
// Version, S3Bucket, Filename, Appname, Envname
AWSDeployer deployer = new AWSDeployer(versionLabel,S3Bucket,versionLabel + ".war",ApplicationName,EnvironmentName,accessKey,secretKey);
deployer.execute(fileName, listener);
} catch (FileNotFoundException e) {
System.out.println("Sorry, the file "+fileName+" doesn't exist");
System.exit(0);
}
}
private interface FinishedListener
{
void succeeded();
void failed(Throwable e);
}
}