Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add logic to specify exec commands during postStart and preStop #272

Merged
merged 1 commit into from
Sep 13, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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