package com.vmware.b20nine;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.node.ObjectNode;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
/**
* @goal deploy-grails
* @execute phase=package
*/
@SuppressWarnings({"JavaDoc"})
public class B20NineMojo extends AbstractMojo {
/**
* The location of the war file.
*
* @parameter expression="${project.build.directory}/${project.build.finalName}.war"
* @required
*/
private File webApp;
/**
* The base deployment URI.
*
* @parameter
* @required
*/
private String baseUri;
/**
* The application name.
*
* @parameter expression="${project.build.finalName}"
* @required
*/
private String name;
/**
* The cloud controller address.
*
* @parameter default-value="http://localhost:8080"
* @required
*/
private String cloudcontroller;
/**
* Number of instances to start.
*
* @parameter default-value="1"
* @required
*/
private int instances;
/**
* Username for accessing the cloud controller.
*
* @parameter
* @required
*/
private String user;
/**
* Password for accessing the cloud controller.
*
* @parameter
* @required
*/
private String password;
private String token;
public B20NineMojo() {
client = new DefaultHttpClient();
}
public void execute() throws MojoExecutionException {
getLog().info(String.format("Deploying \"%s\"...", name));
try {
login();
JsonNode droplet = getExistingDroplet();
if (droplet != null) {
String framework = droplet.get("framework").getTextValue();
if (!GRAILS_FRAMEWORK.equals(framework)) {
throw new MojoExecutionException(String.format(
"Can't update this application since it's not using the Grails framework: %s", framework));
}
getLog().info("Updating existing application");
uploadApplication();
getLog().info("Restarting application.");
updateState(droplet, "STOPPED");
updateState(droplet, "STARTED");
getLog().info(String.format("Updated %s", name));
} else {
createApplication();
provisionDatabase();
uploadApplication();
updateState(getExistingDroplet(), "STARTED");
getLog().info(String.format("Deployed %s", name));
}
} catch (IOException e) {
throw new MojoExecutionException(String.format("Error deploying \"%s\" to: %s", name, baseUri), e);
}
}
private JsonNode getExistingDroplet() throws IOException, MojoExecutionException {
HttpGet request = new HttpGet(cloudcontroller + "/droplets/" + name);
request.addHeader(AUTHORIZATION_HEADER, token);
HttpResponse response = client.execute(request);
int responseCode = response.getStatusLine().getStatusCode();
if (responseCode == HttpStatus.SC_OK) {
ObjectMapper objectMapper = new ObjectMapper();
JsonParser parser = jsonFactory.createJsonParser(response.getEntity().getContent());
JsonNode droplet = objectMapper.readTree(parser);
consumeContent(response);
return droplet;
} else {
consumeContent(response);
if (responseCode == HttpStatus.SC_NOT_FOUND) {
return null;
} else {
throw new MojoExecutionException("Could not look up existing application status.");
}
}
}
private void uploadApplication() throws MojoExecutionException, IOException {
HttpPut request = new HttpPut(cloudcontroller + "/droplets/" + name + "/application");
request.addHeader(AUTHORIZATION_HEADER, token);
MultipartEntity entity = new MultipartEntity(HttpMultipartMode.STRICT);
entity.addPart("application", new FileBody(webApp, "application/x-zip"));
request.setEntity(entity);
HttpResponse response = client.execute(request);
consumeContent(response);
if (response.getStatusLine().getStatusCode() < 400) {
getLog().info(String.format("Uploaded %s bits", name));
} else {
throw new MojoExecutionException(String.format("Error deploying \"%s\" to: %s", name, baseUri));
}
}
private void updateState(JsonNode droplet, String state) throws MojoExecutionException, IOException {
ObjectMapper objectMapper = new ObjectMapper();
((ObjectNode) droplet).put("state", state);
ByteArrayOutputStream out = new ByteArrayOutputStream();
objectMapper.writeValue(out, droplet);
HttpPut request = new HttpPut(cloudcontroller + "/droplets/" + name);
request.addHeader(AUTHORIZATION_HEADER, token);
ByteArrayEntity entity = new ByteArrayEntity(out.toByteArray());
entity.setContentType("application/json");
request.setEntity(entity);
HttpResponse response = client.execute(request);
consumeContent(response);
if (response.getStatusLine().getStatusCode() >= 400) {
throw new MojoExecutionException("Could not update application state.");
}
}
private void login() throws IOException, MojoExecutionException {
HttpPost request = new HttpPost(cloudcontroller + "/login");
StringWriter writer = new StringWriter();
JsonGenerator gen = jsonFactory.createJsonGenerator(writer);
gen.writeStartObject();
gen.writeStringField(EMAIL_FIELD, user);
gen.writeStringField(PASSWORD_FIELD, password);
gen.writeEndObject();
gen.close();
request.setHeader("Content-Type", "application/json");
request.setEntity(new StringEntity(writer.toString()));
HttpResponse response = client.execute(request);
if (response.getStatusLine().getStatusCode() < 400) {
ObjectMapper objectMapper = new ObjectMapper();
JsonParser parser = jsonFactory.createJsonParser(response.getEntity().getContent());
JsonNode tokenJson = objectMapper.readTree(parser);
token = tokenJson.get("token").getTextValue();
consumeContent(response);
getLog().info(String.format("Logged in as \"%s\"", user));
} else {
consumeContent(response);
throw new MojoExecutionException(String.format("Error logging in as \"%s\"", user));
}
}
private void createApplication() throws IOException, MojoExecutionException {
HttpPost request = new HttpPost(cloudcontroller + "/droplets");
request.addHeader(AUTHORIZATION_HEADER, token);
StringWriter writer = new StringWriter();
JsonGenerator gen = jsonFactory.createJsonGenerator(writer);
gen.writeStartObject();
gen.writeStringField("name", name);
gen.writeArrayFieldStart("uris");
gen.writeString(baseUri);
gen.writeEndArray();
gen.writeStringField("framework", GRAILS_FRAMEWORK);
gen.writeNumberField("instances", instances);
gen.writeEndObject();
gen.close();
request.setHeader("Content-Type", "application/json");
request.setEntity(new StringEntity(writer.toString()));
HttpResponse response = client.execute(request);
consumeContent(response);
if (response.getStatusLine().getStatusCode() < 400) {
getLog().info(String.format("Created application \"%s\"", name));
} else {
throw new MojoExecutionException(String.format("Error creating \"%s\"", name));
}
}
private void provisionDatabase() throws IOException, MojoExecutionException {
HttpPost request = new HttpPost(String.format("%s/droplets/%s/services", cloudcontroller, name));
request.addHeader(AUTHORIZATION_HEADER, token);
StringWriter writer = new StringWriter();
JsonGenerator gen = jsonFactory.createJsonGenerator(writer);
gen.writeStartObject();
gen.writeStringField("type", "database");
gen.writeStringField("vendor", "mysql");
gen.writeStringField("version", "5");
gen.writeStringField("tier", "small");
gen.writeEndObject();
gen.close();
request.setHeader("Content-Type", "application/json");
request.setEntity(new StringEntity(writer.toString()));
HttpResponse response = client.execute(request);
consumeContent(response);
if (response.getStatusLine().getStatusCode() < 400) {
getLog().info("Provisioned database");
} else {
throw new MojoExecutionException("Error provisioning database");
}
}
private void consumeContent(HttpResponse response) throws IOException {
HttpEntity entity = response.getEntity();
if (entity != null) {
entity.consumeContent();
}
}
private final HttpClient client;
private static final JsonFactory jsonFactory = new JsonFactory();
private static final String GRAILS_FRAMEWORK = "http://www.grails.org/1.1.1";
private static final String AUTHORIZATION_HEADER = "Authorization";
private static final String EMAIL_FIELD = "email";
private static final String PASSWORD_FIELD = "password";
}