Skip to content

Commit

Permalink
Add logic to specify exec commands during postStart and preStop
Browse files Browse the repository at this point in the history
Signed-off-by: Dennis Leon <dennis.leon39@gmail.com>
  • Loading branch information
DennisDenuto committed Sep 1, 2015
1 parent 6f19245 commit 8d33a6e
Show file tree
Hide file tree
Showing 15 changed files with 285 additions and 42 deletions.
1 change: 1 addition & 0 deletions doc/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
- HTTP method and status code can be specified when waiting on an HTTP URL (#258)
- Introduced global `portPropertyFile` setting (#90)
- Allow the container's host ip to be bound to a maven property and exported
- Add logic to specify exec commands during postStart and preStop (#272)

* **0.13.2**
- "run" directives can be added to the Dockerfile (#191)
Expand Down
9 changes: 8 additions & 1 deletion doc/manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -806,7 +806,10 @@ some condition is met. These conditions can be specified within a
* **shutdown** is the time to wait in milliseconds between stopping a container
and removing it. This might be helpful in situation where a Docker croaks with an
error when trying to remove a container to fast after it has been stopped.

* **exec** Specifies commands to execute during specified lifecycle of the container. It knows the following sub-elements:
- **postStart** Command to run after the above wait criteria has been met
- **preStop** Command to run before the container is stopped.

As soon as one condition is met the build continues. If you add a
`<time>` constraint this works more or less as a timeout for other
conditions. The build will abort if you wait on an url or log output and reach the timeout.
Expand All @@ -823,6 +826,10 @@ Example:
</http>
<time>10000</time>
<shutdown>500</shutdown>
<exec>
<postStart>/opt/init_db.sh</postStart>
<preStop>/opt/notify_end.sh</preStop>
</exec>
</wait>
````

Expand Down
4 changes: 4 additions & 0 deletions src/main/java/org/jolokia/docker/maven/StartMojo.java
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ public synchronized void executeInternal(final DockerAccess dockerAccess) throws

// Wait if requested
waitIfRequested(dockerAccess,imageConfig, projProperties, containerId);
WaitConfiguration waitConfig = runConfig.getWaitConfiguration();
if (waitConfig != null && waitConfig.getExec() != null && waitConfig.getExec().getPostStart() != null) {
runService.createAndStartExecContainer(containerId, waitConfig.getExec().getPostStart());
}
}
if (follow) {
runService.addShutdownHookForStoppingContainers(keepContainer,removeVolumes);
Expand Down
24 changes: 20 additions & 4 deletions src/main/java/org/jolokia/docker/maven/WatchMojo.java
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public class WatchMojo extends AbstractBuildSupportMojo {
// Scheduler
private ScheduledExecutorService executor;


@Override
protected synchronized void executeInternal(DockerAccess dockerAccess) throws DockerAccessException, MojoExecutionException {
// Important to be be a single threaded scheduler since watch jobs must run serialized
Expand All @@ -82,14 +82,14 @@ protected synchronized void executeInternal(DockerAccess dockerAccess) throws Do
RunService runService = serviceHub.getRunService();

MojoParameters mojoParameters = createMojoParameters();

try {
for (StartOrderResolver.Resolvable resolvable : runService.getImagesConfigsInOrder(queryService, getImages())) {
final ImageConfiguration imageConfig = (ImageConfiguration) resolvable;

String imageId = queryService.getImageId(imageConfig.getName());
String containerId = runService.lookupContainer(imageConfig.getName());

ImageWatcher watcher = new ImageWatcher(imageConfig, imageId, containerId);

ArrayList<String> tasks = new ArrayList<>();
Expand Down Expand Up @@ -118,7 +118,7 @@ protected synchronized void executeInternal(DockerAccess dockerAccess) throws Do
}
}

private void scheduleBuildWatchTask(DockerAccess dockerAccess, ImageWatcher watcher,
private void scheduleBuildWatchTask(DockerAccess dockerAccess, ImageWatcher watcher,
MojoParameters mojoParameters, boolean doRestart) throws MojoExecutionException {
executor.scheduleAtFixedRate(
createBuildWatchTask(dockerAccess, watcher, mojoParameters, doRestart),
Expand Down Expand Up @@ -190,12 +190,28 @@ private void restartContainer(ImageWatcher watcher) throws DockerAccessException
ImageConfiguration imageConfig = watcher.getImageConfiguration();
PortMapping mappedPorts = runService.getPortMapping(imageConfig.getRunConfiguration(), project.getProperties());
String id = watcher.getContainerId();

String optionalPreStop = getPreStopCommand(imageConfig);
if (optionalPreStop != null) {
runService.createAndStartExecContainer(id, optionalPreStop);
}
runService.stopContainer(id, false, false);

// Start new one
watcher.setContainerId(runService.createAndStartContainer(imageConfig, mappedPorts, project.getProperties()));
}

private String getPreStopCommand(ImageConfiguration imageConfig) {
if (imageConfig.getRunConfiguration() != null) {
if (imageConfig.getRunConfiguration().getWaitConfiguration() != null) {
if (imageConfig.getRunConfiguration().getWaitConfiguration().getExec() != null) {
return imageConfig.getRunConfiguration().getWaitConfiguration().getExec().getPreStop();
}
}
}
return null;
}

private void callPostGoal(ImageWatcher watcher) throws MojoFailureException, MojoExecutionException {
String postGoal = watcher.getPostGoal();
if (postGoal != null) {
Expand Down
25 changes: 22 additions & 3 deletions src/main/java/org/jolokia/docker/maven/access/DockerAccess.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import org.jolokia.docker.maven.access.log.LogCallback;
import org.jolokia.docker.maven.access.log.LogGetHandle;
import org.jolokia.docker.maven.config.Arguments;
import org.jolokia.docker.maven.model.*;

/**
Expand Down Expand Up @@ -49,18 +50,36 @@ public interface DockerAccess {
* @throws DockerAccessException if the containers could not be listed
*/
List<Container> listContainers(int limit) throws DockerAccessException;

/**
* Starts a previously set up exec instance id.
* this API sets up an interactive session with the exec command
*
* @param containerId id of the exec container
* @return stdout/stderr of running the exec container
* @throws DockerAccessException if the container could not be created.
*/
String startExecContainer(String containerId) throws DockerAccessException;

/**
* Sets up an exec instance for a running container id
*
* @param arguments container exec commands to run
* @param containerId id of the running container which the exec container will be created for
* @throws DockerAccessException if the container could not be created.
*/
String createExecContainer(Arguments arguments, String containerId) throws DockerAccessException;

/**
* Create a container from the given image.
*
*
* <p>The <code>container id</code> will be set on the <code>container</code> upon successful creation.</p>
*
* @param configuration container configuration
* @param containerName name container should be created with or <code>null</code> for a docker provided name
* @throws DockerAccessException if the container could not be created.
*/
String createContainer(ContainerCreateConfig configuration, String containerName) throws DockerAccessException;

/**
* Start a container.
*
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/org/jolokia/docker/maven/access/UrlBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,14 @@ public String startContainer(String containerId) {
return u("containers/%s/start", containerId).toString();
}

public String createExecContainer(String containerId) {
return u("containers/%s/exec", containerId).url;
}

public String startExecContainer(String containerId) {
return u("exec/%s/start", containerId).url;
}

public String stopContainer(String containerId) {
return u("containers/%s/stop", containerId).toString();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,32 @@
package org.jolokia.docker.maven.access.hc;

import java.io.*;
import java.net.URI;
import java.util.*;

import org.apache.http.HttpResponse;
import org.apache.http.client.ResponseHandler;
import org.jolokia.docker.maven.access.*;
import org.jolokia.docker.maven.access.chunked.*;
import org.jolokia.docker.maven.access.hc.http.HttpClientBuilder;
import org.jolokia.docker.maven.access.hc.unix.UnixSocketClientBuilder;
import org.jolokia.docker.maven.access.log.*;
import org.jolokia.docker.maven.model.*;
import org.jolokia.docker.maven.access.log.LogCallback;
import org.jolokia.docker.maven.access.log.LogGetHandle;
import org.jolokia.docker.maven.access.log.LogRequestor;
import org.jolokia.docker.maven.config.Arguments;
import org.jolokia.docker.maven.model.Container;
import org.jolokia.docker.maven.model.ContainerDetails;
import org.jolokia.docker.maven.model.ContainersListElement;
import org.jolokia.docker.maven.util.ImageName;
import org.jolokia.docker.maven.util.Logger;
import org.json.JSONArray;
import org.json.JSONObject;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import static java.net.HttpURLConnection.*;
import static org.jolokia.docker.maven.access.hc.ApacheHttpClientDelegate.*;

Expand Down Expand Up @@ -71,6 +81,48 @@ public DockerAccessWithHcClient(String apiVersion, String baseUrl, String certPa
}
}

@Override
public String startExecContainer(String containerId) throws DockerAccessException {
try {
String url = urlBuilder.startExecContainer(containerId);
JSONObject request = new JSONObject();
request.put("Detach", false);
request.put("Tty", false);

return delegate.post(url, request.toString(), new BodyResponseHandler(), HTTP_OK);
} catch (Exception e) {
log.error(e.getMessage());
throw new DockerAccessException("Unable to start container id [%s]", containerId);
}
}

@Override
public String createExecContainer(Arguments arguments, String containerId) throws DockerAccessException {
String url = urlBuilder.createExecContainer(containerId);
JSONObject request = new JSONObject();
request.put("Tty", false);
request.put("AttachStdin", true);
request.put("AttachStdout", true);
request.put("AttachStderr", true);
request.put("Cmd", arguments.getExec());

String execJsonRequest = request.toString();
try {
String response = delegate.post(url, execJsonRequest, new BodyResponseHandler(), HTTP_CREATED);
JSONObject json = new JSONObject(response);
if (json.has("Warnings")) {
logWarnings(json);
}

return json.getString("Id");
} catch (IOException e) {
log.error(e.getMessage());
throw new DockerAccessException("Unable to exec [%s] on container [%s]", request.toString(),
containerId);
}

}

@Override
public String createContainer(ContainerCreateConfig containerConfig, String containerName)
throws DockerAccessException {
Expand Down Expand Up @@ -126,7 +178,7 @@ public void buildImage(String image, File dockerArchive, boolean forceRemove)
throw new DockerAccessException("Unable to build image [%s]", image);
}
}

@Override
public void getLogSync(String containerId, LogCallback callback) {
LogRequestor
Expand Down Expand Up @@ -229,7 +281,7 @@ public void pullImage(String image, AuthConfig authConfig, String registry)
createPullOrPushResponseHandler(), HTTP_OK);
} catch (IOException e) {
log.error(e.getMessage());
throw new DockerAccessException("Unable to pull '%s'%s", image, (registry != null) ? " from registry '" + registry + "'" : "");
throw new DockerAccessException("Unable to pull '%s'%s", image, (registry != null) ? " from registry '" + registry + "'" : "");
}
}

Expand All @@ -244,7 +296,7 @@ public void pushImage(String image, AuthConfig authConfig, String registry)
createPullOrPushResponseHandler(), HTTP_OK);
} catch (IOException e) {
log.error(e.getMessage());
throw new DockerAccessException("Unable to push '%s'%s", image, (registry != null) ? " from registry '" + registry + "'" : "");
throw new DockerAccessException("Unable to push '%s'%s", image, (registry != null) ? " from registry '" + registry + "'" : "");
} finally {
if (temporaryImage != null) {
removeImage(temporaryImage);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ public class WaitConfiguration {
*/
private HttpConfiguration http;

/**
* @parameter
*/
private ExecConfiguration exec;

/**
* @parameter
*/
Expand All @@ -34,14 +39,14 @@ public class WaitConfiguration {

public WaitConfiguration() {}

private WaitConfiguration(int time, HttpConfiguration http, String log, int shutdown) {
private WaitConfiguration(int time, ExecConfiguration exec, HttpConfiguration http, String log, int shutdown) {
this.time = time;
this.exec = exec;
this.http = http;
this.log = log;
this.shutdown = shutdown;
}


public int getTime() {
return time;
}
Expand All @@ -50,6 +55,10 @@ public String getUrl() {
return http != null ? http.getUrl() : url;
}

public ExecConfiguration getExec() {
return exec;
}

public HttpConfiguration getHttp() {
return http;
}
Expand All @@ -68,6 +77,8 @@ public static class Builder {
private int time = 0,shutdown = 0;
private String url,log,status;
private String method;
private String preStop;
private String postStart;

public Builder time(int time) {
this.time = time;
Expand Down Expand Up @@ -100,7 +111,40 @@ public Builder shutdown(int shutdown) {
}

public WaitConfiguration build() {
return new WaitConfiguration(time,new HttpConfiguration(url,method,status), log,shutdown);
return new WaitConfiguration(time, new ExecConfiguration(postStart, preStop), new HttpConfiguration(url,method,status), log, shutdown);
}

public Builder preStop(String command) {
this.preStop = command;
return this;
}

public Builder postStart(String command) {
this.postStart = command;
return this;
}
}

public static class ExecConfiguration {
/** @parameter */
private String postStart;
/** @parameter */
private String preStop;

public ExecConfiguration() {
}

public ExecConfiguration(String postStart, String preStop) {
this.postStart = postStart;
this.preStop = preStop;
}

public String getPostStart() {
return postStart;
}

public String getPreStop() {
return preStop;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ public enum ConfigKey {
WORKDIR,
VOLUMES_FROM,
WAIT_LOG("wait.log"),
POST_START("wait.exec.postStart"),
PRE_STOP("wait.exec.preStop"),
WAIT_TIME("wait.time"),
WAIT_URL("wait.url"),
WAIT_HTTP_URL("wait.http.url"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@ private WaitConfiguration extractWaitConfig(String prefix, Properties properties
return new WaitConfiguration.Builder()
.time(asInt(withPrefix(prefix, WAIT_TIME,properties)))
.url(url)
.preStop(withPrefix(prefix, PRE_STOP, properties))
.postStart(withPrefix(prefix, POST_START, properties))
.method(withPrefix(prefix, WAIT_HTTP_METHOD, properties))
.status(withPrefix(prefix, WAIT_HTTP_STATUS, properties))
.log(withPrefix(prefix, WAIT_LOG, properties))
Expand Down
Loading

0 comments on commit 8d33a6e

Please sign in to comment.