mirror of https://github.com/apache/jclouds.git
[JCLOUDS-1002] provide access to Docker container Config object in the Node template options
This commit is contained in:
parent
3f1fe271ed
commit
831cdc67c3
|
@ -22,8 +22,8 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jclouds.compute.ComputeService;
|
||||
import org.jclouds.compute.options.TemplateOptions;
|
||||
import org.jclouds.docker.domain.Config;
|
||||
import org.jclouds.docker.internal.NullSafeCopies;
|
||||
import org.jclouds.domain.LoginCredentials;
|
||||
import org.jclouds.javax.annotation.Nullable;
|
||||
|
@ -34,23 +34,55 @@ import com.google.common.collect.ImmutableList;
|
|||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
/**
|
||||
* Contains options supported by the {@link ComputeService#createNodesInGroup(String, int, TemplateOptions) createNodes}
|
||||
* operation on the <em>docker</em> provider.
|
||||
*
|
||||
* Contains options supported by the
|
||||
* {@link org.jclouds.compute.ComputeService#createNodesInGroup(String, int, TemplateOptions)
|
||||
* createNodes} operation on the <em>docker</em> provider.
|
||||
*
|
||||
* <h2>Usage</h2>
|
||||
*
|
||||
* The recommended way to instantiate a
|
||||
* DockerTemplateOptions object is to statically import {@code DockerTemplateOptions.Builder.*}
|
||||
* and invoke one of the static creation methods, followed by an instance mutator if needed.
|
||||
* The recommended way to instantiate a DockerTemplateOptions object is to
|
||||
* statically import {@code DockerTemplateOptions.Builder.*} and invoke one of
|
||||
* the static creation methods, followed by an instance mutator if needed.
|
||||
*
|
||||
* <pre>{@code import static org.jclouds.docker.compute.options.DockerTemplateOptions.Builder.*;
|
||||
* <pre>
|
||||
* {@code import static org.jclouds.docker.compute.options.DockerTemplateOptions.Builder.*;
|
||||
*
|
||||
* ComputeService api = // get connection
|
||||
* templateBuilder.options(inboundPorts(22, 80, 8080, 443));
|
||||
* Set<? extends NodeMetadata> set = api.createNodesInGroup(tag, 2, templateBuilder.build());}</pre>
|
||||
* Set<? extends NodeMetadata> set = api.createNodesInGroup(tag, 2, templateBuilder.build());}
|
||||
* </pre>
|
||||
*
|
||||
* <h2>Advanced Usage</h2>
|
||||
* <p>
|
||||
* In addition to basic configuration through its methods, this class also
|
||||
* provides possibility to work directly with Docker API configuration object (
|
||||
* {@link Config.Builder}). When the
|
||||
* {@link #configBuilder(org.jclouds.docker.domain.Config.Builder)} is used to
|
||||
* configure not-<code>null</code> configBuilder, then this configuration object
|
||||
* takes precedence over the other configuration in this class (i.e. the other
|
||||
* config entries are not used)
|
||||
* </p>
|
||||
* <p>
|
||||
* Note: The {@code image} property in the provided {@link Config.Builder} is rewritten by a placeholder value.
|
||||
* The real value is configured by ComputeServiceAdapter.
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* {@code import static org.jclouds.docker.compute.options.DockerTemplateOptions.Builder.*;
|
||||
*
|
||||
* ComputeService api = // get connection
|
||||
* DockerTemplateOptions options = DockerTemplateOptions.Builder
|
||||
* .configBuilder(
|
||||
* Config.builder().env(ImmutableList.<String> of("SSH_PORT=8822"))
|
||||
* .hostConfig(HostConfig.builder().networkMode("host").build()));
|
||||
* templateBuilder.options(options);
|
||||
* Set<? extends NodeMetadata> set = api.createNodesInGroup("sample-group", 1, templateBuilder.build());}
|
||||
* </pre>
|
||||
*/
|
||||
public class DockerTemplateOptions extends TemplateOptions implements Cloneable {
|
||||
|
||||
private static final String NO_IMAGE = "jclouds-placeholder-for-image";
|
||||
|
||||
protected List<String> dns = ImmutableList.of();
|
||||
protected String hostname;
|
||||
protected Integer memory;
|
||||
|
@ -63,6 +95,8 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable
|
|||
protected String networkMode;
|
||||
protected Map<String, String> extraHosts = ImmutableMap.of();
|
||||
|
||||
protected Config.Builder configBuilder;
|
||||
|
||||
@Override
|
||||
public DockerTemplateOptions clone() {
|
||||
DockerTemplateOptions options = new DockerTemplateOptions();
|
||||
|
@ -94,6 +128,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable
|
|||
if (!extraHosts.isEmpty()) {
|
||||
eTo.extraHosts(extraHosts);
|
||||
}
|
||||
eTo.configBuilder(configBuilder);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,12 +148,22 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable
|
|||
equal(this.cpuShares, that.cpuShares) &&
|
||||
equal(this.env, that.env) &&
|
||||
equal(this.portBindings, that.portBindings) &&
|
||||
equal(this.extraHosts, that.extraHosts);
|
||||
equal(this.extraHosts, that.extraHosts) &&
|
||||
buildersEqual(this.configBuilder, that.configBuilder);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compares two Config.Builder instances.
|
||||
*/
|
||||
private boolean buildersEqual(Config.Builder b1, Config.Builder b2) {
|
||||
return b1 == b2 || (b1 != null && b2 != null && b1.build().equals(b2.build()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(super.hashCode(), volumes, hostname, dns, memory, entrypoint, commands, cpuShares, env, portBindings, extraHosts);
|
||||
return Objects.hashCode(super.hashCode(), volumes, hostname, dns, memory, entrypoint, commands, cpuShares, env,
|
||||
portBindings, extraHosts, configBuilder);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -134,6 +179,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable
|
|||
.add("env", env)
|
||||
.add("portBindings", portBindings)
|
||||
.add("extraHosts", extraHosts)
|
||||
.add("configBuilder", configBuilder)
|
||||
.toString();
|
||||
}
|
||||
|
||||
|
@ -235,6 +281,23 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method sets Config.Builder configuration object, which can be used as
|
||||
* a replacement for all the other settings from this class. Some values in
|
||||
* the provided Config.Builder instance (the image name for instance) can be
|
||||
* ignored or their value can be changed.
|
||||
*
|
||||
* @param configBuilder
|
||||
* Config.Builder instance. This instance can be changed in this
|
||||
* method!
|
||||
*/
|
||||
public DockerTemplateOptions configBuilder(Config.Builder configBuilder) {
|
||||
this.configBuilder = configBuilder != null
|
||||
? Config.builder().fromConfig(configBuilder.image(NO_IMAGE).build())
|
||||
: null;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Map<String, String> getVolumes() { return volumes; }
|
||||
|
||||
public List<String> getDns() { return dns; }
|
||||
|
@ -257,6 +320,8 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable
|
|||
|
||||
public Map<String, String> getExtraHosts() { return extraHosts; }
|
||||
|
||||
public Config.Builder getConfigBuilder() { return configBuilder; }
|
||||
|
||||
public static class Builder {
|
||||
|
||||
/**
|
||||
|
@ -379,6 +444,11 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable
|
|||
return options.extraHosts(extraHosts);
|
||||
}
|
||||
|
||||
public static DockerTemplateOptions configBuilder(Config.Builder configBuilder) {
|
||||
DockerTemplateOptions options = new DockerTemplateOptions();
|
||||
return options.configBuilder(configBuilder);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see TemplateOptions#inboundPorts(int...)
|
||||
*/
|
||||
|
|
|
@ -32,6 +32,7 @@ import org.jclouds.compute.domain.Hardware;
|
|||
import org.jclouds.compute.domain.HardwareBuilder;
|
||||
import org.jclouds.compute.domain.Processor;
|
||||
import org.jclouds.compute.domain.Template;
|
||||
import org.jclouds.compute.options.TemplateOptions;
|
||||
import org.jclouds.compute.reference.ComputeServiceConstants;
|
||||
import org.jclouds.docker.DockerApi;
|
||||
import org.jclouds.docker.compute.options.DockerTemplateOptions;
|
||||
|
@ -74,83 +75,97 @@ public class DockerComputeServiceAdapter implements
|
|||
this.api = checkNotNull(api, "api");
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
@Override
|
||||
public NodeAndInitialCredentials<Container> createNodeWithGroupEncodedIntoName(String group, String name,
|
||||
Template template) {
|
||||
checkNotNull(template, "template was null");
|
||||
checkNotNull(template.getOptions(), "template options was null");
|
||||
TemplateOptions options = template.getOptions();
|
||||
checkNotNull(options, "template options was null");
|
||||
|
||||
String imageId = checkNotNull(template.getImage().getId(), "template image id must not be null");
|
||||
String loginUser = template.getImage().getDefaultCredentials().getUser();
|
||||
String loginUserPassword = template.getImage().getDefaultCredentials().getOptionalPassword().or("password");
|
||||
|
||||
DockerTemplateOptions templateOptions = DockerTemplateOptions.class.cast(template.getOptions());
|
||||
int[] inboundPorts = templateOptions.getInboundPorts();
|
||||
DockerTemplateOptions templateOptions = DockerTemplateOptions.class.cast(options);
|
||||
Config.Builder containerConfigBuilder = templateOptions.getConfigBuilder();
|
||||
if (containerConfigBuilder == null) {
|
||||
containerConfigBuilder = Config.builder();
|
||||
|
||||
Map<String, Object> exposedPorts = Maps.newHashMap();
|
||||
for (int inboundPort : inboundPorts) {
|
||||
exposedPorts.put(inboundPort + "/tcp", Maps.newHashMap());
|
||||
}
|
||||
containerConfigBuilder.entrypoint(templateOptions.getEntrypoint());
|
||||
containerConfigBuilder.cmd(templateOptions.getCommands());
|
||||
containerConfigBuilder.memory(templateOptions.getMemory());
|
||||
containerConfigBuilder.hostname(templateOptions.getHostname());
|
||||
containerConfigBuilder.cpuShares(templateOptions.getCpuShares());
|
||||
containerConfigBuilder.env(templateOptions.getEnv());
|
||||
|
||||
Config.Builder containerConfigBuilder = Config.builder()
|
||||
.image(imageId)
|
||||
.exposedPorts(exposedPorts);
|
||||
|
||||
containerConfigBuilder.entrypoint(templateOptions.getEntrypoint());
|
||||
containerConfigBuilder.cmd(templateOptions.getCommands());
|
||||
containerConfigBuilder.memory(templateOptions.getMemory());
|
||||
containerConfigBuilder.hostname(templateOptions.getHostname());
|
||||
containerConfigBuilder.cpuShares(templateOptions.getCpuShares());
|
||||
containerConfigBuilder.env(templateOptions.getEnv());
|
||||
|
||||
if (!templateOptions.getVolumes().isEmpty()) {
|
||||
Map<String, Object> volumes = Maps.newLinkedHashMap();
|
||||
for (String containerDir : templateOptions.getVolumes().values()) {
|
||||
volumes.put(containerDir, Maps.newHashMap());
|
||||
if (!templateOptions.getVolumes().isEmpty()) {
|
||||
Map<String, Object> volumes = Maps.newLinkedHashMap();
|
||||
for (String containerDir : templateOptions.getVolumes().values()) {
|
||||
volumes.put(containerDir, Maps.newHashMap());
|
||||
}
|
||||
containerConfigBuilder.volumes(volumes);
|
||||
}
|
||||
containerConfigBuilder.volumes(volumes);
|
||||
|
||||
HostConfig.Builder hostConfigBuilder = HostConfig.builder()
|
||||
.publishAllPorts(true)
|
||||
.privileged(true);
|
||||
|
||||
if (!templateOptions.getPortBindings().isEmpty()) {
|
||||
Map<String, List<Map<String, String>>> portBindings = Maps.newHashMap();
|
||||
for (Map.Entry<Integer, Integer> entry : templateOptions.getPortBindings().entrySet()) {
|
||||
portBindings.put(entry.getValue() + "/tcp",
|
||||
Lists.<Map<String, String>>newArrayList(ImmutableMap.of("HostPort", Integer.toString(entry.getKey()))));
|
||||
}
|
||||
hostConfigBuilder.portBindings(portBindings);
|
||||
}
|
||||
|
||||
if (!templateOptions.getDns().isEmpty()) {
|
||||
hostConfigBuilder.dns(templateOptions.getDns());
|
||||
}
|
||||
|
||||
if (!templateOptions.getExtraHosts().isEmpty()) {
|
||||
List<String> extraHosts = Lists.newArrayList();
|
||||
for (Map.Entry<String, String> entry : templateOptions.getExtraHosts().entrySet()) {
|
||||
extraHosts.add(entry.getKey() + ":" + entry.getValue());
|
||||
}
|
||||
hostConfigBuilder.extraHosts(extraHosts);
|
||||
}
|
||||
|
||||
if (!templateOptions.getVolumes().isEmpty()) {
|
||||
for (Map.Entry<String, String> entry : templateOptions.getVolumes().entrySet()) {
|
||||
hostConfigBuilder.binds(ImmutableList.of(entry.getKey() + ":" + entry.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
hostConfigBuilder.networkMode(templateOptions.getNetworkMode());
|
||||
containerConfigBuilder.hostConfig(hostConfigBuilder.build());
|
||||
}
|
||||
|
||||
Config containerConfig = containerConfigBuilder.build();
|
||||
containerConfigBuilder.image(imageId);
|
||||
|
||||
// add the inbound ports into exposed ports map
|
||||
Config containerConfig = containerConfigBuilder.build();
|
||||
Map<String, Object> exposedPorts = Maps.newHashMap();
|
||||
if (containerConfig.exposedPorts() == null) {
|
||||
exposedPorts.putAll(containerConfig.exposedPorts());
|
||||
}
|
||||
for (int inboundPort : templateOptions.getInboundPorts()) {
|
||||
String portKey = inboundPort + "/tcp";
|
||||
if (!exposedPorts.containsKey(portKey)) {
|
||||
exposedPorts.put(portKey, Maps.newHashMap());
|
||||
}
|
||||
}
|
||||
containerConfigBuilder.exposedPorts(exposedPorts);
|
||||
|
||||
// build once more after setting inboundPorts
|
||||
containerConfig = containerConfigBuilder.build();
|
||||
|
||||
logger.debug(">> creating new container with containerConfig(%s)", containerConfig);
|
||||
Container container = api.getContainerApi().createContainer(name, containerConfig);
|
||||
logger.trace("<< container(%s)", container.id());
|
||||
|
||||
HostConfig.Builder hostConfigBuilder = HostConfig.builder()
|
||||
.publishAllPorts(true)
|
||||
.privileged(true);
|
||||
|
||||
if (!templateOptions.getPortBindings().isEmpty()) {
|
||||
Map<String, List<Map<String, String>>> portBindings = Maps.newHashMap();
|
||||
for (Map.Entry<Integer, Integer> entry : templateOptions.getPortBindings().entrySet()) {
|
||||
portBindings.put(entry.getValue() + "/tcp",
|
||||
Lists.<Map<String, String>>newArrayList(ImmutableMap.of("HostPort", Integer.toString(entry.getKey()))));
|
||||
}
|
||||
hostConfigBuilder.portBindings(portBindings);
|
||||
}
|
||||
|
||||
if (!templateOptions.getDns().isEmpty()) {
|
||||
hostConfigBuilder.dns(templateOptions.getDns());
|
||||
}
|
||||
|
||||
if (!templateOptions.getExtraHosts().isEmpty()) {
|
||||
List<String> extraHosts = Lists.newArrayList();
|
||||
for (Map.Entry<String, String> entry : templateOptions.getExtraHosts().entrySet()) {
|
||||
extraHosts.add(entry.getKey() + ":" + entry.getValue());
|
||||
}
|
||||
hostConfigBuilder.extraHosts(extraHosts);
|
||||
}
|
||||
|
||||
if (!templateOptions.getVolumes().isEmpty()) {
|
||||
for (Map.Entry<String, String> entry : templateOptions.getVolumes().entrySet()) {
|
||||
hostConfigBuilder.binds(ImmutableList.of(entry.getKey() + ":" + entry.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
hostConfigBuilder.networkMode(templateOptions.getNetworkMode());
|
||||
|
||||
HostConfig hostConfig = hostConfigBuilder.build();
|
||||
HostConfig hostConfig = containerConfig.hostConfig();
|
||||
|
||||
api.getContainerApi().startContainer(container.id(), hostConfig);
|
||||
container = api.getContainerApi().inspectContainer(container.id());
|
||||
|
|
|
@ -35,7 +35,9 @@ import org.jclouds.compute.internal.BaseComputeServiceContextLiveTest;
|
|||
import org.jclouds.docker.DockerApi;
|
||||
import org.jclouds.docker.compute.functions.LoginPortForContainer;
|
||||
import org.jclouds.docker.compute.options.DockerTemplateOptions;
|
||||
import org.jclouds.docker.domain.Config;
|
||||
import org.jclouds.docker.domain.Container;
|
||||
import org.jclouds.docker.domain.HostConfig;
|
||||
import org.jclouds.docker.domain.Image;
|
||||
import org.jclouds.docker.domain.ImageSummary;
|
||||
import org.jclouds.docker.options.BuildOptions;
|
||||
|
@ -46,6 +48,7 @@ import org.testng.annotations.Test;
|
|||
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.inject.AbstractModule;
|
||||
|
@ -124,6 +127,33 @@ public class SshToCustomPortLiveTest extends BaseComputeServiceContextLiveTest {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Test(dependsOnMethods = "testImageCreated")
|
||||
public void testAdvancedConfig() throws RunNodesException {
|
||||
final DockerTemplateOptions options = DockerTemplateOptions.Builder
|
||||
.configBuilder(
|
||||
Config.builder().env(ImmutableList.<String> of("SSH_PORT=" + SSH_PORT, "ROOT_PASSWORD=jcloudsRulez"))
|
||||
.hostConfig(HostConfig.builder().networkMode("host").build())
|
||||
.image("test-if-this-value-is-correctly-overriden"))
|
||||
.overrideLoginUser("root").overrideLoginPassword("jcloudsRulez").blockOnPort(SSH_PORT, 30);
|
||||
|
||||
final Template template = view.getComputeService().templateBuilder().imageId(image.id()).options(options).build();
|
||||
|
||||
String nodeId = null;
|
||||
try {
|
||||
NodeMetadata node = Iterables
|
||||
.getOnlyElement(view.getComputeService().createNodesInGroup("ssh-test-advanced", 1, template));
|
||||
|
||||
nodeId = node.getId();
|
||||
ExecResponse response = view.getComputeService().runScriptOnNode(nodeId, "sh -c 'true'",
|
||||
wrapInInitScript(false));
|
||||
assertEquals(response.getExitStatus(), 0);
|
||||
} finally {
|
||||
if (nodeId != null)
|
||||
view.getComputeService().destroyNode(nodeId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a new image with 2 tags on it in the test preparation phase.
|
||||
*
|
||||
|
|
|
@ -17,8 +17,11 @@
|
|||
package org.jclouds.docker.compute.options;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertNotNull;
|
||||
|
||||
import org.jclouds.compute.options.TemplateOptions;
|
||||
import org.jclouds.docker.domain.Config;
|
||||
import org.jclouds.docker.domain.Config.Builder;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
@ -91,6 +94,21 @@ public class DockerTemplateOptionsTest {
|
|||
assertEquals(options.as(DockerTemplateOptions.class).getNetworkMode(), "host");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConfigBuilder() {
|
||||
Builder builder = Config.builder().memory(1024)
|
||||
.cpuShares(100).cmd(ImmutableList.<String> of("/opt/jboss/wildfly/bin/standalone.sh", "-b", "0.0.0.0"))
|
||||
.env(ImmutableList.<String> of("JAVA_HOME=/opt/jdk-1.8", "MGMT_USER=admin",
|
||||
"MGMT_PASSWORD=Schroedinger's Cat"));
|
||||
TemplateOptions options = DockerTemplateOptions.Builder.configBuilder(builder);
|
||||
Builder builderInOpts = options.as(DockerTemplateOptions.class).getConfigBuilder();
|
||||
assertNotNull(builderInOpts);
|
||||
Config configFromOptions = builderInOpts.build();
|
||||
assertEquals(configFromOptions, builder.build());
|
||||
assertEquals(configFromOptions.env(), ImmutableList.<String> of("JAVA_HOME=/opt/jdk-1.8", "MGMT_USER=admin",
|
||||
"MGMT_PASSWORD=Schroedinger's Cat"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonDockerOptions() {
|
||||
TemplateOptions options = DockerTemplateOptions.Builder.userMetadata(ImmutableMap.of("key", "value")).cpuShares(1);
|
||||
|
|
Loading…
Reference in New Issue