diff --git a/pom.xml b/pom.xml index ee059b729..36e53d71e 100644 --- a/pom.xml +++ b/pom.xml @@ -114,20 +114,26 @@ 1.51 + + org.jmockit + jmockit + ${jmockit.version} + test + + junit junit 4.11 test - + - org.jmockit - jmockit - ${jmockit.version} + org.skyscreamer + jsonassert + 1.2.3 test - diff --git a/src/main/java/org/jolokia/docker/maven/StartMojo.java b/src/main/java/org/jolokia/docker/maven/StartMojo.java index 0c0ec528b..6101e7188 100644 --- a/src/main/java/org/jolokia/docker/maven/StartMojo.java +++ b/src/main/java/org/jolokia/docker/maven/StartMojo.java @@ -59,18 +59,15 @@ public void executeInternal(DockerAccess docker) throws DockerAccessException, M checkImage(docker,imageName); RunImageConfiguration runConfig = imageConfig.getRunConfiguration(); - if (runConfig == null) { - // It's a data image which needs not to have a runtime configuration - runConfig = RunImageConfiguration.DEFAULT; - } - PortMapping mappedPorts = getPortMapping(runConfig); - String container = docker.createContainer(imageName, - runConfig.getCommand(), mappedPorts.getContainerPorts(), - runConfig.getEnv()); - docker.startContainer(container, - mappedPorts, - findContainersForImages(runConfig.getVolumesFrom()), findLinksWithContainerNames(docker,runConfig.getLinks())); + PortMapping mappedPorts = getPortMapping(runConfig, project.getProperties()); + + ContainerConfig containerConfig = createContainerConfig(imageName, runConfig, mappedPorts.getContainerPorts()); + String container = docker.createContainer(containerConfig); + + ContainerHostConfig hostConfig = createHostConfig(docker, runConfig, mappedPorts); + docker.startContainer(container, hostConfig); registerContainer(container, imageConfig); + info("Created and started container " + getContainerAndImageDescription(container, imageConfig.getDescription())); @@ -90,10 +87,36 @@ public void executeInternal(DockerAccess docker) throws DockerAccessException, M } } } + + // visible for testing + ContainerConfig createContainerConfig(String imageName, RunImageConfiguration runConfig, Set ports) + throws MojoExecutionException { + try { + return new ContainerConfig(imageName).hostname(runConfig.getHostname()).domainname(runConfig.getDomainname()) + .user(runConfig.getUser()).workingDir(runConfig.getWorkingDir()).memory(runConfig.getMemory()) + .memorySwap(runConfig.getMemorySwap()).entrypoint(runConfig.getEntrypoint()).exposedPorts(ports) + .environment(runConfig.getEnv()).command(runConfig.getCommand()).bind(runConfig.getBind()); + } + catch (IllegalArgumentException e) { + throw new MojoExecutionException(String.format("Failed to create contained configuration for [%s]", imageName), e); + } + } - private PortMapping getPortMapping(RunImageConfiguration runConfig) throws MojoExecutionException { + // visible for testing + ContainerHostConfig createHostConfig(DockerAccess docker, RunImageConfiguration runConfig, PortMapping mappedPorts) + throws DockerAccessException, MojoExecutionException { + RunImageConfiguration.RestartPolicy restartPolicy = runConfig.getRestartPolicy(); + return new ContainerHostConfig().bind(runConfig.getBind()).links(findLinksWithContainerNames(docker, runConfig.getLinks())) + .portBindings(mappedPorts).privileged(runConfig.getPrivileged()).dns(runConfig.getDns()) + .dnsSearch(runConfig.getDnsSearch()).volumesFrom(findContainersForImages(runConfig.getVolumesFrom())) + .capAdd(runConfig.getCapAdd()).capDrop(runConfig.getCapDrop()) + .restartPolicy(restartPolicy.getName(), restartPolicy.getRetry()); + } + + // visible for testing + PortMapping getPortMapping(RunImageConfiguration runConfig, Properties properties) throws MojoExecutionException { try { - return new PortMapping(runConfig.getPorts(), project.getProperties()); + return new PortMapping(runConfig.getPorts(), properties); } catch (IllegalArgumentException exp) { throw new MojoExecutionException("Cannot parse portmapping",exp); } @@ -108,7 +131,8 @@ private List getImagesConfigsInOrder() throws Moj } } - private List findLinksWithContainerNames(DockerAccess docker, List links) throws DockerAccessException { + // visible for testing + List findLinksWithContainerNames(DockerAccess docker, List links) throws DockerAccessException { List ret = new ArrayList<>(); for (String[] link : EnvUtil.splitLinks(links)) { String container = lookupContainer(link[0]); @@ -120,7 +144,8 @@ private List findLinksWithContainerNames(DockerAccess docker, List findContainersForImages(List images) throws MojoExecutionException { + // visible for testing + List findContainersForImages(List images) throws MojoExecutionException { List containers = new ArrayList<>(); if (images != null) { for (String image : images) { @@ -177,7 +202,6 @@ public void checkImage(DockerAccess docker,String image) throws DockerAccessExce private void waitIfRequested(RunImageConfiguration runConfig, PortMapping mappedPorts, DockerAccess docker, String containerId) { WaitConfiguration wait = runConfig.getWaitConfiguration(); if (wait != null) { - int maxTime = wait.getTime(); ArrayList checkers = new ArrayList<>(); ArrayList logOut = new ArrayList<>(); if (wait.getUrl() != null) { diff --git a/src/main/java/org/jolokia/docker/maven/access/ContainerConfig.java b/src/main/java/org/jolokia/docker/maven/access/ContainerConfig.java new file mode 100644 index 000000000..4331ca22f --- /dev/null +++ b/src/main/java/org/jolokia/docker/maven/access/ContainerConfig.java @@ -0,0 +1,132 @@ +package org.jolokia.docker.maven.access; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.jolokia.docker.maven.util.EnvUtil; +import org.json.JSONArray; +import org.json.JSONObject; + + +/** + * Represents a configuration used to create a container + */ +public class ContainerConfig { + + private final String imageName; + + private final JSONObject object = new JSONObject(); + + public ContainerConfig(String imageName) { + this.imageName = imageName; + object.put("Image", imageName); + } + + public ContainerConfig bind(List bind) { + if (bind != null && !bind.isEmpty()) { + JSONObject volumes = new JSONObject(); + for (String volume : bind) { + if (volume.contains(":")) { + volume = volume.split(":")[0]; + } + volumes.put(volume, new JSONObject()); + } + object.put("Volumes", volumes); + } + return this; + } + + public ContainerConfig command(String command) { + if (command != null) { + JSONArray a = new JSONArray(); + for (String s : EnvUtil.splitWOnSpaceWithEscape(command)) { + a.put(s); + } + object.put("Cmd", a); + } + return this; + } + + public ContainerConfig domainname(String domainname) { + add("Domainname", domainname); + return this; + } + + public ContainerConfig entrypoint(String entrypoint) { + if (entrypoint != null) { + add("Entrypoint", entrypoint); + } + return this; + } + + public ContainerConfig environment(Map env) throws IllegalArgumentException { + if (env != null && env.size() > 0) { + JSONArray a = new JSONArray(); + for (Map.Entry entry : env.entrySet()) { + String value = entry.getValue(); + if (value == null || value.length() == 0) { + throw new IllegalArgumentException(String.format("Env variable '%s' must not be null or empty when running %s", + entry.getKey(), imageName)); + } + a.put(entry.getKey() + "=" + entry.getValue()); + } + object.put("Env", a); + } + return this; + } + + public ContainerConfig exposedPorts(Set ports) { + if (ports != null && ports.size() > 0) { + JSONObject exposedPorts = new JSONObject(); + for (Integer port : ports) { + exposedPorts.put(port.toString() + "/tcp", new JSONObject()); + } + object.put("ExposedPorts", exposedPorts); + } + return this; + } + + public String getImageName() { + return imageName; + } + + public ContainerConfig hostname(String hostname) { + add("Hostname", hostname); + return this; + } + + public ContainerConfig memory(long memory) { + if (memory != 0) { + object.put("Memory", memory); + } + return this; + } + + public ContainerConfig memorySwap(long memorySwap) { + if (memorySwap != 0) { + object.put("MemorySwap", memorySwap); + } + return this; + } + + public String toJson() { + return object.toString(); + } + + public ContainerConfig user(String user) { + add("User", user); + return this; + } + + public ContainerConfig workingDir(String workingDir) { + add("WorkingDir", workingDir); + return this; + } + + private void add(String name, String value) { + if (value != null) { + object.put(name, value); + } + } +} diff --git a/src/main/java/org/jolokia/docker/maven/access/ContainerHostConfig.java b/src/main/java/org/jolokia/docker/maven/access/ContainerHostConfig.java new file mode 100644 index 000000000..fd063958e --- /dev/null +++ b/src/main/java/org/jolokia/docker/maven/access/ContainerHostConfig.java @@ -0,0 +1,125 @@ +package org.jolokia.docker.maven.access; + +import java.util.List; +import java.util.Map; + +import org.json.JSONArray; +import org.json.JSONObject; + + +/** + * Represents a configuration used to start a container + */ +public class ContainerHostConfig { + + private final JSONObject object = new JSONObject(); + + public ContainerHostConfig bind(List bind) { + if (bind != null && !bind.isEmpty()) { + JSONArray host = new JSONArray(); + for (String volume : bind) { + if (volume.contains(":")) { + host.put(volume); + } + } + object.put("Binds", host); + } + return this; + } + + public ContainerHostConfig capAdd(List capAdd) + { + if (capAdd != null) { + object.put("CapAdd", new JSONArray(capAdd)); + } + return this; + } + + public ContainerHostConfig capDrop(List capDrop) + { + if (capDrop != null) { + object.put("CapDrop", new JSONArray(capDrop)); + } + return this; + } + + public ContainerHostConfig dns(List dns) + { + if (dns != null) { + object.put("Dns", new JSONArray(dns)); + } + return this; + } + + public ContainerHostConfig dnsSearch(List dnsSearch) + { + if (dnsSearch != null) { + object.put("DnsSearch", new JSONArray(dnsSearch)); + } + return this; + } + + public ContainerHostConfig links(List links) { + if (links != null) { + object.put("Links", new JSONArray(links)); + } + return this; + } + + public ContainerHostConfig portBindings(PortMapping portMapping) { + Map portMap = portMapping.getPortsMap(); + if (!portMap.isEmpty()) { + JSONObject portBindings = new JSONObject(); + Map bindToMap = portMapping.getBindToMap(); + + for (Map.Entry entry : portMap.entrySet()) { + Integer port = entry.getKey(); + Integer hostPort = entry.getValue(); + + JSONObject o = new JSONObject(); + o.put("HostPort", hostPort != null ? hostPort.toString() : ""); + + if (bindToMap.containsKey(port)) { + o.put("HostIp", bindToMap.get(port)); + } + + JSONArray array = new JSONArray(); + array.put(o); + + portBindings.put(port + "/tcp", array); + } + + object.put("PortBindings", portBindings); + } + return this; + } + + public ContainerHostConfig privileged(boolean privileged) + { + object.put("Privileged", privileged); + return this; + } + + public ContainerHostConfig restartPolicy(String name, int retry) + { + if (name != null) { + JSONObject policy = new JSONObject(); + policy.put("Name", name); + policy.put("MaximumRetryCount", retry); + + object.put("RestartPolicy", policy); + } + return this; + } + + public String toJson() { + return object.toString(); + } + + public ContainerHostConfig volumesFrom(List volumesFrom) { + if (volumesFrom != null) { + object.put("VolumesFrom", new JSONArray(volumesFrom)); + } + return this; + } +} diff --git a/src/main/java/org/jolokia/docker/maven/access/DockerAccess.java b/src/main/java/org/jolokia/docker/maven/access/DockerAccess.java index ef054eea7..47bcded86 100644 --- a/src/main/java/org/jolokia/docker/maven/access/DockerAccess.java +++ b/src/main/java/org/jolokia/docker/maven/access/DockerAccess.java @@ -24,14 +24,11 @@ public interface DockerAccess { /** * Create a container from the given image. * - * @param image the image from which the container should be created - * @param command an optional command which gets executed when starting the container. might be null. - * @param ports ports to expose, can be null - * @param env map with environment variables to use + * @param configuration container configuration * @return the container id * @throws DockerAccessException if the container could not be created. */ - String createContainer(String image, String command, Set ports, Map env) throws DockerAccessException; + String createContainer(ContainerConfig configuration) throws DockerAccessException; /** * Get the the name of a container for a given container id @@ -45,12 +42,11 @@ public interface DockerAccess { /** * Start a container. * - * @param containerId id of container to start - * @param portMapping port mapping to use. Must not be null. - * @param volumesFrom mount volumes from the given container id. Can be null. + * @param containerId container id + * @param configuration container host configuration * @throws DockerAccessException if the container could not be started. */ - void startContainer(String containerId, PortMapping portMapping, List volumesFrom, List links) throws DockerAccessException; + void startContainer(String containerId, ContainerHostConfig configuration) throws DockerAccessException; /** * Stop a container. diff --git a/src/main/java/org/jolokia/docker/maven/access/DockerAccessWithHttpClient.java b/src/main/java/org/jolokia/docker/maven/access/DockerAccessWithHttpClient.java index 79a7bcef4..505d5f1f2 100644 --- a/src/main/java/org/jolokia/docker/maven/access/DockerAccessWithHttpClient.java +++ b/src/main/java/org/jolokia/docker/maven/access/DockerAccessWithHttpClient.java @@ -1,19 +1,31 @@ package org.jolokia.docker.maven.access; -import java.io.*; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.Charset; import java.security.GeneralSecurityException; import java.security.KeyStore; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.net.ssl.SSLContext; -import org.apache.http.*; +import org.apache.http.Header; +import org.apache.http.HttpResponse; +import org.apache.http.StatusLine; import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.*; +import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.conn.ssl.SSLContexts; import org.apache.http.entity.FileEntity; @@ -21,8 +33,11 @@ import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; -import org.jolokia.docker.maven.util.*; -import org.json.*; +import org.jolokia.docker.maven.util.ImageName; +import org.jolokia.docker.maven.util.LogHandler; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; /** * Implementation using Apache HttpComponents for accessing remotely @@ -41,7 +56,7 @@ public class DockerAccessWithHttpClient implements DockerAccess { // Used Docker API - static private final String DOCKER_API_VERSION = "v1.10"; + static private final String DOCKER_API_VERSION = "v1.15"; public static final String HEADER_ACCEPT = "Accept"; public static final String HEADER_ACCEPT_ALL = "*/*"; @@ -81,10 +96,13 @@ public boolean hasImage(String image) throws DockerAccessException { /** {@inheritDoc} */ @Override - public String createContainer(String image, String command, Set ports, Map env) throws DockerAccessException { - HttpUriRequest post = newPost(baseUrl + "/containers/create", getContainerConfig(image, ports, command, env)); + public String createContainer(ContainerConfig configuration) throws DockerAccessException { + String createJson = configuration.toJson(); + log.debug("Container create config: " + createJson); + + HttpUriRequest post = newPost(baseUrl + "/containers/create", createJson); HttpResponse resp = request(post); - checkReturnCode("Creating container for image '" + image + "'", resp, 201); + checkReturnCode("Creating container for image '" + configuration.getImageName() + "'", resp, 201); JSONObject json = asJsonObject(resp); logWarnings(json); return json.getString("Id"); @@ -101,10 +119,11 @@ public String getContainerName(String id) throws DockerAccessException { /** {@inheritDoc} */ @Override - public void startContainer(String containerId, PortMapping portMapping,List volumesFrom, List links) - throws DockerAccessException { - HttpUriRequest req = newPost(baseUrl + "/containers/" + encode(containerId) + "/start", - getStartConfig(portMapping, volumesFrom, links)); + public void startContainer(String containerId, ContainerHostConfig configuration) throws DockerAccessException { + String startJson = configuration.toJson(); + log.debug("Container start config: " + startJson); + + HttpUriRequest req = newPost(baseUrl + "/containers/" + encode(containerId) + "/start", startJson); HttpResponse resp = request(req); checkReturnCode("Starting container with id " + containerId, resp, 204); } @@ -365,54 +384,6 @@ private void parsePortSpecAndUpdateMapping(Map portMapping, Ob } } - private String getContainerConfig(String image, Set ports, String command, Map env) { - JSONObject ret = new JSONObject(); - ret.put("Image", image); - if (ports != null && ports.size() > 0) { - JSONObject exposedPorts = new JSONObject(); - for (Integer port : ports) { - exposedPorts.put(port.toString() + "/tcp", new JSONObject()); - } - ret.put("ExposedPorts", exposedPorts); - } - if (command != null) { - JSONArray a = new JSONArray(); - for (String s : EnvUtil.splitWOnSpaceWithEscape(command)) { - a.put(s); - } - ret.put("Cmd",a); - } - if (env != null && env.size() > 0) { - JSONArray a = new JSONArray(); - for (Map.Entry entry : env.entrySet()) { - String value = entry.getValue(); - if (value == null || value.length() == 0) { - throw new IllegalArgumentException("Env variable '" + entry.getKey() + - "' must not be null or empty when running " + image); - } - a.put(entry.getKey() + "=" + entry.getValue()); - } - ret.put("Env", a); - } - log.debug("Container create config: " + ret.toString()); - return ret.toString(); - } - - private String getStartConfig(PortMapping portMapping, List volumesFrom, List links) { - JSONObject ret = new JSONObject(); - if (portMapping != null && !portMapping.isEmpty()) { - ret.put("PortBindings", portMapping.toDockerConfig()); - } - if (volumesFrom != null) { - ret.put("VolumesFrom", new JSONArray(volumesFrom)); - } - if (links != null) { - ret.put("Links", new JSONArray(links)); - } - log.debug("Container start config: " + ret.toString()); - return ret.toString(); - } - // ====================================================================================================== private void dump(HttpResponse resp) { @@ -468,9 +439,8 @@ private String addRegistry(String url, ImageName name) { private String addQueryParam(String url, String param, String value) { if (value != null) { return url + (url.contains("?") ? "&" : "?") + param + "=" + encode(value); - } else { - return url; - } + } + return url; } private void processPullOrPushResponse(final String image, HttpResponse resp, final String action) diff --git a/src/main/java/org/jolokia/docker/maven/config/ImageConfiguration.java b/src/main/java/org/jolokia/docker/maven/config/ImageConfiguration.java index fcec70391..f72409716 100644 --- a/src/main/java/org/jolokia/docker/maven/config/ImageConfiguration.java +++ b/src/main/java/org/jolokia/docker/maven/config/ImageConfiguration.java @@ -56,12 +56,13 @@ public String getName() { return name; } - public String getAlias() { + @Override + public String getAlias() { return alias; } public RunImageConfiguration getRunConfiguration() { - return run; + return (run == null) ? RunImageConfiguration.DEFAULT : run; } public BuildImageConfiguration getBuildConfiguration() { diff --git a/src/main/java/org/jolokia/docker/maven/config/RunImageConfiguration.java b/src/main/java/org/jolokia/docker/maven/config/RunImageConfiguration.java index eda5d8d65..fdf5b7907 100644 --- a/src/main/java/org/jolokia/docker/maven/config/RunImageConfiguration.java +++ b/src/main/java/org/jolokia/docker/maven/config/RunImageConfiguration.java @@ -3,32 +3,101 @@ import java.util.List; import java.util.Map; +import org.jolokia.docker.maven.access.ContainerHostConfig; +import org.jolokia.docker.maven.config.RunImageConfiguration.Builder; +import org.jolokia.docker.maven.config.RunImageConfiguration.RestartPolicy; + + /** * @author roland * @since 02.09.14 */ public class RunImageConfiguration { - public static final RunImageConfiguration DEFAULT = new RunImageConfiguration(); + static final RunImageConfiguration DEFAULT = new RunImageConfiguration(); // Environment variables to set when starting the container. key: variable name, value: env value /** * @parameter */ - private Map env; + private Map env; + private boolean privileged; + // Command to execute in container /** * @parameter */ private String command; + // container domain name + /** + * @parameter + */ + private String domainname; + + // container entry point + /** + * @parameter + */ + private String entrypoint; + + // container hostname + /** + * @parameter + */ + private String hostname; + + // container user + /** + * @parameter + */ + private String user; + + // working directory + /** + * @paramter + */ + private String workingDir; + + // memory in bytes + /** + * @parameter + */ + private long memory; + + // total memory (swap + ram) in bytes, -1 to disable + /** + * @parameter + */ + private long memorySwap; + // Path to a file where the dynamically mapped properties are written to /** * @parameter */ private String portPropertyFile; + /** + * @parameter + */ + private List dns; + + /** + * @parameter + */ + private List dnsSearch; + + /** + * @parameter + */ + private List capAdd; + + /** + * @parameter + */ + private List capDrop; + // Port mapping. Can contain symbolic names in which case dynamic // ports are used /** @@ -42,6 +111,11 @@ public class RunImageConfiguration { */ private List volumes; + /** + * @parameter + */ + private List bind; + // Links to other container started /** * @parameter @@ -55,24 +129,42 @@ public class RunImageConfiguration { */ private WaitConfiguration wait; - public RunImageConfiguration() {} + /** + * @parameter + */ + private RestartPolicy restartPolicy; - RunImageConfiguration(Map env, String command, String portPropertyFile, - List ports, List volumes, List links, - WaitConfiguration wait) { - this.env = env; - this.command = command; - this.portPropertyFile = portPropertyFile; - this.ports = ports; - this.volumes = volumes; - this.links = links; - this.wait = wait; + public RunImageConfiguration() { } public Map getEnv() { return env; } + public String getEntrypoint() { + return entrypoint; + } + + public String getHostname() { + return hostname; + } + + public String getDomainname() { + return domainname; + } + + public String getUser() { + return user; + } + + public long getMemory() { + return memory; + } + + public long getMemorySwap() { + return memorySwap; + } + public List getPorts() { return ports; } @@ -85,10 +177,34 @@ public String getPortPropertyFile() { return portPropertyFile; } + public String getWorkingDir() { + return workingDir; + } + public WaitConfiguration getWaitConfiguration() { return wait; } + public List getBind() { + return bind; + } + + public List getCapAdd() { + return capAdd; + } + + public List getCapDrop() { + return capDrop; + } + + public List getDns() { + return dns; + } + + public List getDnsSearch() { + return dnsSearch; + } + public List getVolumesFrom() { return volumes; } @@ -97,55 +213,156 @@ public List getLinks() { return links; } - // ====================================================================================== + public boolean getPrivileged() { + return privileged; + } + + public RunImageConfiguration.RestartPolicy getRestartPolicy() { + return (restartPolicy == null) ? RestartPolicy.DEFAULT : restartPolicy; + } + +// ====================================================================================== public static class Builder { - private Map env; - private String command; - private String portPropertyFile; - private List ports; - private List volumes; - private List links; - private WaitConfiguration wait; + private RunImageConfiguration config = new RunImageConfiguration(); public Builder env(Map env) { - this.env = env; + config.env = env; return this; } public Builder command(String command) { - this.command = command; + config.command = command; + return this; + } + + public Builder domainname(String domainname) { + config.domainname = domainname; + return this; + } + + public Builder entrypoint(String entrypoint) { + config.entrypoint = entrypoint; + return this; + } + + public Builder hostname(String hostname) { + config.hostname = hostname; return this; } public Builder portPropertyFile(String portPropertyFile) { - this.portPropertyFile = portPropertyFile; + config.portPropertyFile = portPropertyFile; + return this; + } + + public Builder workingDir(String workingDir) { + config.workingDir = workingDir; + return this; + } + + public Builder user(String user) { + config.user = user; return this; } + public Builder memory(long memory) { + config.memory = memory; + return this; + } + + public Builder memorySwap(long memorySwap) { + config.memorySwap = memorySwap; + return this; + } + + public Builder bind(List bind) { + config.bind = bind; + return this; + } + + public Builder capAdd(List capAdd) { + config.capAdd = capAdd; + return this; + } + + public Builder capDrop(List capDrop) { + config.capDrop = capDrop; + return this; + } + + public Builder dns(List dns) { + config.dns = dns; + return this; + } + + public Builder dnsSearch(List dnsSearch) { + config.dnsSearch = dnsSearch; + return this; + } + public Builder ports(List ports) { - this.ports = ports; + config.ports = ports; return this; } public Builder volumes(List volumes) { - this.volumes = volumes; + config.volumes = volumes; return this; } public Builder links(List links) { - this.links = links; + config.links = links; return this; } public Builder wait(WaitConfiguration wait) { - this.wait = wait; + config.wait = wait; return this; } + public Builder privileged(boolean privileged) { + config.privileged = privileged; + return this; + } + + public Builder restartPolicy(RestartPolicy restartPolicy) { + config.restartPolicy = restartPolicy; + return this; + } + public RunImageConfiguration build() { - return new RunImageConfiguration(env, command, portPropertyFile, ports, - volumes, links, wait); + return config; + } + } + + public static class RestartPolicy { + + private static final RestartPolicy DEFAULT = new RestartPolicy(); + + /** + * @parameter + */ + private String name; + + /** + * @parameter + */ + private int retry; + + public RestartPolicy() {} + + public RestartPolicy(String name, int retry) { + this.name = name; + this.retry = retry; + } + + public String getName() { + return name; + } + + public int getRetry() { + return retry; } } } diff --git a/src/test/java/org/jolokia/docker/maven/StartMojoContainerConfigsTest.java b/src/test/java/org/jolokia/docker/maven/StartMojoContainerConfigsTest.java new file mode 100644 index 000000000..a54daf7fe --- /dev/null +++ b/src/test/java/org/jolokia/docker/maven/StartMojoContainerConfigsTest.java @@ -0,0 +1,125 @@ +package org.jolokia.docker.maven; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Scanner; + +import org.apache.maven.plugin.MojoExecutionException; +import org.jolokia.docker.maven.access.ContainerConfig; +import org.jolokia.docker.maven.access.ContainerHostConfig; +import org.jolokia.docker.maven.access.DockerAccess; +import org.jolokia.docker.maven.access.DockerAccessException; +import org.jolokia.docker.maven.access.PortMapping; +import org.jolokia.docker.maven.config.RunImageConfiguration; +import org.junit.Test; +import org.skyscreamer.jsonassert.JSONAssert; + + +public class StartMojoContainerConfigsTest { + + private static final String BIND = "/tmp:/tmp"; + + @Test + @SuppressWarnings("unused") + public void testCreateContainerAllConfig() throws Exception { + /*- + * this is really two tests in one + * - verify the start mojo calls all the methods to build the container configs + * - the container configs produce the correct json when all options are specified + * + * it didn't seem worth the effor to build a separate test to verify the json and then mock/verify all the calls here + */ + final RunImageConfiguration runConfig = + new RunImageConfiguration.Builder().hostname("hostname").domainname("domain.com").user("user").memory(1).memorySwap(1) + .env(env()).command("date").entrypoint("entrypoint").bind(bind()).workingDir("/foo").ports(ports()).links(links()) + .volumes(volumesFrom()).dns(dns()).dnsSearch(dnsSearch()).privileged(true).capAdd(capAdd()).capDrop(capDrop()) + .restartPolicy(restartPolicy()).build(); + + StartMojo mojo = new StartMojo() { + @Override + List findContainersForImages(List images) throws MojoExecutionException { + return images; + } + + @Override + List findLinksWithContainerNames(DockerAccess docker, List links) throws DockerAccessException { + return links; + } + }; + + PortMapping portMapping = mojo.getPortMapping(runConfig, new Properties()); + + ContainerConfig config = mojo.createContainerConfig("base", runConfig, portMapping.getContainerPorts()); + ContainerHostConfig hostConfig = mojo.createHostConfig(null, runConfig, portMapping); + + String expectedConfig = loadFile("docker/createContainerAll.json"); + JSONAssert.assertEquals(expectedConfig, config.toJson(), true); + + String expectedHostConfig = loadFile("docker/createHostConfigAll.json"); + JSONAssert.assertEquals(expectedHostConfig, hostConfig.toJson(), true); + } + + private List bind() { + return Arrays.asList(BIND); + } + + private List capAdd() { + return Arrays.asList("NET_ADMIN"); + } + + private List capDrop() { + return Arrays.asList("MKNOD"); + } + + private List dns() { + return Arrays.asList("8.8.8.8"); + } + + private List dnsSearch() { + return Arrays.asList("domain.com"); + } + + private Map env() { + Map env = new HashMap<>(); + env.put("foo", "bar"); + + return env; + } + + private List links() { + return Arrays.asList("redis3:redis"); + } + + private String loadFile(String fileName) { + StringBuilder buffer = new StringBuilder(); + File file = new File(getClass().getClassLoader().getResource(fileName).getFile()); + + try (Scanner scanner = new Scanner(file)) { + while (scanner.hasNextLine()) { + buffer.append(scanner.nextLine()); + } + } + catch (IOException e) { + throw new RuntimeException(e); + } + + return buffer.toString(); + } + + private List ports() { + return Arrays.asList("0.0.0.0:11022:22"); + } + + private RunImageConfiguration.RestartPolicy restartPolicy() { + return new RunImageConfiguration.RestartPolicy("on-failure", 1); + } + + private List volumesFrom() { + return Arrays.asList("parent", "other:ro"); + } +} diff --git a/src/test/resources/docker/createContainerAll.json b/src/test/resources/docker/createContainerAll.json new file mode 100644 index 000000000..47e60319b --- /dev/null +++ b/src/test/resources/docker/createContainerAll.json @@ -0,0 +1,14 @@ +{ + "Hostname": "hostname", + "Domainname": "domain.com", + "User": "user", + "Memory": 1, + "MemorySwap": 1, + "Env": [ "foo=bar" ], + "Cmd": [ "date" ], + "Entrypoint": "entrypoint", + "Image": "base", + "Volumes": { "/tmp": { } }, + "WorkingDir": "/foo", + "ExposedPorts": { "22/tcp": { } } +} \ No newline at end of file diff --git a/src/test/resources/docker/createHostConfigAll.json b/src/test/resources/docker/createHostConfigAll.json new file mode 100644 index 000000000..8b63d4a53 --- /dev/null +++ b/src/test/resources/docker/createHostConfigAll.json @@ -0,0 +1,12 @@ +{ + "Binds": [ "/tmp:/tmp" ], + "Links": [ "redis3:redis" ], + "PortBindings": { "22/tcp": [ { "HostIp" : "0.0.0.0", "HostPort": "11022" }] }, + "Privileged":true, + "Dns": ["8.8.8.8"], + "DnsSearch": ["domain.com"], + "VolumesFrom": [ "parent", "other:ro" ], + "CapAdd": ["NET_ADMIN"], + "CapDrop": ["MKNOD"], + "RestartPolicy": { "Name": "on-failure", "MaximumRetryCount": 1 } +} \ No newline at end of file