![JAR search and dependency download from the Maven repository](/logo.png)
com.erigir.maven.plugin.StartNewEnvironmentMojo Maven / Gradle / Ivy
package com.erigir.maven.plugin;
import com.amazonaws.services.elasticbeanstalk.AWSElasticBeanstalkClient;
import com.amazonaws.services.elasticbeanstalk.model.*;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.transfer.TransferManager;
import com.amazonaws.services.s3.transfer.model.UploadResult;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Collection;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
/**
* Copyright 2014 Christopher Weiss
*
* 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.
**/
@Mojo(name = "start-new-environment", defaultPhase = LifecyclePhase.PRE_INTEGRATION_TEST)
public class StartNewEnvironmentMojo extends AbstractSeedyMojo {
private static final int MAX_ENVIRONMENT_NAME_LENGTH = 23; // This comes from AMZN
/**
* If deploying to a different account, this is the ARN of the role in that account
* with privs to execute the deployment
*/
@Parameter(property = "s3-upload.assumedRoleArn")
String assumedRoleArn;
/**
* If deploying to a different account, this is the external ID set on the role in that account
* with privs to execute the deployment
*/
@Parameter(property = "s3-upload.assumedRoleExternalId")
String assumedRoleExternalId;
/**
* S3 Bucket to hold the WAR file
*/
@Parameter(property = "start-new-environment.s3Bucket", required = true)
private String s3Bucket;
/**
* S3 Prefix to hold the WAR file
*/
@Parameter(property = "start-new-environment.s3Prefix", required = true)
private String s3Prefix;
/**
* Application Name in elastic beanstalk
*/
@Parameter(property = "start-new-environment.applicationFile", required = true)
private String applicationFile;
/**
* Application Name in elastic beanstalk
*/
@Parameter(property = "start-new-environment.applicationName", required = true)
private String applicationName;
/**
* Pool config file
*/
@Parameter(property = "start-new-environment.poolConfigFile", defaultValue = "src/main/config/live-config.json")
private String poolConfigFile;
/**
* Machine type
*/
@Parameter(property = "start-new-environment.solutionStack", defaultValue = "Tomcat 8 Java 8 on 64bit Amazon Linux 2015.03 v1.4.5")
private String solutionStack;
/**
* Seconds to sleep before beginning pings
*/
@Parameter(property = "start-new-environment.prePingSleepSeconds", defaultValue = "300")
private int prePingSleepSeconds;
/**
* Seconds to sleep after green
*/
@Parameter(property = "start-new-environment.afterGreenSleepSeconds", defaultValue = "30")
private int afterGreenSleepSeconds;
/**
* Max Seconds to wait before giving up
*/
@Parameter(property = "start-new-environment.maxWaitSeconds", defaultValue = "420")
private int maxWaitSeconds;
/**
* Machine type
*/
@Parameter(property = "start-new-environment.liveServerDomainName", required = true)
private String liveServerDomainName;
/**
* Max Seconds to wait before giving up
*/
@Parameter(property = "start-new-environment.preFlipLiveWaitSeconds", defaultValue = "15")
private int preFlipLiveWaitSeconds;
/**
* Machine type
*/
@Parameter(property = "start-new-environment.terminateOldEnviroment", defaultValue = "false")
private boolean terminateOldEnviroment;
@Override
public void execute() throws MojoExecutionException {
String buildId = buildId();
int buildNumber = buildNumber(1);
String environmentName = applicationName + "-" + buildNumber;
String appVersionLabel = applicationName + "-" + buildId;
if (environmentName.length() > MAX_ENVIRONMENT_NAME_LENGTH) {
getLog().warn("Environment name '" + environmentName + " would be longer than the max " + MAX_ENVIRONMENT_NAME_LENGTH + " allowed, aborting");
throw new MojoExecutionException("Environment name '" + environmentName + " would be longer than the max " + MAX_ENVIRONMENT_NAME_LENGTH + " allowed, aborting");
}
AWSElasticBeanstalkClient eb = new AWSElasticBeanstalkClient(credentials());
S3Location warLocation = uploadPackageFile();
getLog().info("Current available solution stacks: " + eb.listAvailableSolutionStacks());
CreateApplicationVersionRequest cavr = new CreateApplicationVersionRequest().withApplicationName(applicationName).withDescription(applicationName + " build " + buildId)
.withVersionLabel(appVersionLabel).withSourceBundle(warLocation);
getLog().info("Creating new elastic beanstalk version " + cavr);
eb.createApplicationVersion(cavr);
// Create a new environment for that version
Collection settings = loadSettingsFromFile();
getLog().info("settings:" + settings);
CreateEnvironmentRequest cer = new CreateEnvironmentRequest().withApplicationName(applicationName).withVersionLabel(appVersionLabel).withEnvironmentName(environmentName)
.withSolutionStackName(solutionStack).withCNAMEPrefix(environmentName).withOptionSettings(settings);
getLog().info("Creating new environment " + cer);
eb.createEnvironment(cer);
getLog().info("Sleeping " + prePingSleepSeconds + " seconds before we even begin polling");
safeSleep(prePingSleepSeconds * 1000);
getLog().info("Now waiting for either a 200 response or " + maxWaitSeconds + " have passed");
String newEnvironmentUrl = environmentName + ".elasticbeanstalk.com";
waitForUrl(newEnvironmentUrl, maxWaitSeconds * 1000);
getLog().info("Now waiting until the environment is green");
waitForEnvironmentGreen(eb, environmentName, maxWaitSeconds * 1000);
getLog().info("Waiting " + preFlipLiveWaitSeconds + " seconds more just to make sure its ready");
safeSleep(preFlipLiveWaitSeconds * 1000);
getLog().info("--Should run integration tests here--");
getLog().info("Finding current live url");
EnvironmentDescription liveEnvironment = findEnvironmentByCNAME(eb, liveServerDomainName);
/*
getLog().info("Swapping new version live (old live is " + liveEnvironment + ")");
SwapEnvironmentCNAMEsRequest swap = new SwapEnvironmentCNAMEsRequest().withSourceEnvironmentName(liveEnvironment.getEnvironmentName()).withDestinationEnvironmentName(environmentName);
eb.swapEnvironmentCNAMEs(swap);
if (terminateOldEnviroment) {
getLog().info("Waiting " + preFlipLiveWaitSeconds + " seconds post live before terminating old environment");
safeSleep(preFlipLiveWaitSeconds * 1000);
getLog().info("Terminating old environment");
TerminateEnvironmentRequest ter = new TerminateEnvironmentRequest().withEnvironmentName(liveEnvironment.getEnvironmentName());
eb.terminateEnvironment(ter);
}
*/
getLog().info("Deploy is now complete");
}
// Create a new environment for that version
private List loadSettingsFromFile()
throws MojoExecutionException {
List rval = new LinkedList();
if (poolConfigFile != null) {
File file = new File(poolConfigFile);
if (file.exists()) {
try {
ObjectMapper mapper = new ObjectMapper();
List temp = mapper.readValue(file, new TypeReference>() {
});
for (ConfigurationOptionSettingBridge c : temp) {
rval.add(new ConfigurationOptionSetting().withNamespace(c.namespace).withOptionName(c.optionName).withValue(c.value));
}
} catch (IOException ioe) {
throw new MojoExecutionException("Error trying to read live settings file " + file, ioe);
}
} else {
getLog().info("Pool config file doesn't exist - skipping (was " + file + ")");
}
}
return rval;
}
private S3Location uploadPackageFile()
throws MojoExecutionException {
AmazonS3 s3 = s3();
TransferManager mgr = new TransferManager(s3);
// Upload the WAR file to S3
UploadResult ur = null;
try {
File warFile = new File(applicationFile);
if (!warFile.exists()) {
throw new MojoExecutionException("File " + warFile + " doesn't exist - aborting");
}
getLog().info("Uploading " + warFile + " (" + warFile.length() + " bytes) to " + s3Bucket + "/" + s3Prefix);
ur = mgr.upload(s3Bucket, s3Prefix, warFile).waitForUploadResult();
getLog().info("Upload finished");
} catch (InterruptedException ie) {
getLog().info("Interrupted during upload");
}
// Create a new EB Version
S3Location warLocation = new S3Location().withS3Bucket(ur.getBucketName()).withS3Key(ur.getKey());
return warLocation;
}
private EnvironmentDescription findEnvironmentByName(AWSElasticBeanstalkClient eb, String environmentName) {
getLog().info("Finding environment " + environmentName);
DescribeEnvironmentsResult res = eb.describeEnvironments();
EnvironmentDescription rval = null;
for (EnvironmentDescription ed : res.getEnvironments()) {
if (ed.getEnvironmentName().equals(environmentName)) {
getLog().debug("Found match " + ed);
if (rval == null) {
rval = ed;
} else {
getLog().warn("Found multiple matches, using newer");
if (ed.getDateUpdated().after(rval.getDateUpdated())) {
rval = ed;
}
}
}
}
getLog().info("Found environment " + rval + " for name " + environmentName);
return rval;
}
private EnvironmentDescription findEnvironmentByCNAME(AWSElasticBeanstalkClient eb, String domainName) {
DescribeEnvironmentsResult res = eb.describeEnvironments();
EnvironmentDescription rval = null;
for (EnvironmentDescription ed : res.getEnvironments()) {
if (ed.getCNAME().equals(domainName)) {
rval = ed;
}
}
return rval;
}
private String getEnvironmentColor(AWSElasticBeanstalkClient eb, String environmentName)
throws MojoExecutionException {
EnvironmentDescription ed = findEnvironmentByName(eb, environmentName);
if (ed == null) {
getLog().info("Couldnt find environment with name " + environmentName);
throw new MojoExecutionException("Couldnt find environment with name " + environmentName);
} else {
String rval = ed.getHealth();
getLog().info("Returning " + rval);
return rval;
}
}
public void waitForEnvironmentGreen(AWSElasticBeanstalkClient eb, String environmentName, long maxWaitMS)
throws MojoExecutionException {
long startTime = System.currentTimeMillis();
long step = 20000;
long timeoutAt = startTime + maxWaitMS;
getLog().info("Environment is " + environmentName + " started waiting for green at " + new Date());
String color = "NULL";
while (!"Green".equalsIgnoreCase(color) && System.currentTimeMillis() < timeoutAt) {
color = getEnvironmentColor(eb, environmentName);
if (!"Green".equalsIgnoreCase(color)) {
long elapsed = System.currentTimeMillis() - startTime;
getLog().info(environmentName + " was not green, waiting " + step + "ms " + elapsed + "ms have elapsed");
safeSleep(step);
}
}
if (!"Green".equalsIgnoreCase(color)) {
getLog().info(environmentName + " not green in " + maxWaitMS + "ms, giving up");
throw new MojoExecutionException(environmentName + " not green in " + maxWaitMS + "ms, giving up");
}
getLog().info(environmentName + " is now green");
}
private void waitForUrl(String url, long maxWaitMS)
throws MojoExecutionException {
long startTime = System.currentTimeMillis();
long timeoutAt = startTime + maxWaitMS;
long stepTime = 20000; // 20 second steps
getLog().info("URL is http://" + url + " started at " + new Date());
URL u = null;
try {
u = new URL("http://" + url);
} catch (MalformedURLException mue) {
throw new MojoExecutionException("Bad URL " + url);
}
boolean found = false;
while (!found && System.currentTimeMillis() < timeoutAt) {
try {
URLConnection uc = u.openConnection();
found = true;
} catch (IOException ioe) {
long elapsed = System.currentTimeMillis() - startTime;
getLog().info(url + " Not found, waiting " + stepTime + "ms " + elapsed + "ms already elapsed");
}
safeSleep(stepTime);
}
if (!found) {
getLog().info("Timed out waiting for URL " + url);
throw new MojoExecutionException("Timed out");
}
}
/**
* Ok, I must be doing this wrong, but I can't find anything in their docs for reading the
* standard JSON file with these, so here I go
*/
static class ConfigurationOptionSettingBridge {
@JsonProperty("OptionName")
private String optionName;
@JsonProperty("Value")
private String value;
@JsonProperty("Namespace")
private String namespace;
public String getOptionName() {
return optionName;
}
public void setOptionName(String optionName) {
this.optionName = optionName;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getNamespace() {
return namespace;
}
public void setNamespace(String namespace) {
this.namespace = namespace;
}
}
@Override
public String getAssumedRoleArn() {
return assumedRoleArn;
}
@Override
public String getAssumedRoleExternalId() {
return assumedRoleExternalId;
}
}