JCLOUDS-737 update docker to support v1.3

This commit is contained in:
Andrea Turli 2014-10-02 18:08:24 +02:00 committed by Adrian Cole
parent b04fd71998
commit 6219b77dbc
37 changed files with 1496 additions and 524 deletions

View File

@ -7,6 +7,10 @@ providers, it supports the same portable abstractions offered by jclouds.
Please follow these steps to configure your workstation for jclouds-docker:
- install the latest Docker release (please visit https://docs.docker.com/installation/)
If you are using boot2docker, notice that from version v1.3.0 the Docker daemon is set to use an encrypted TCP socket (--tls, or --tlsverify),
then you need to create a p12 certificate using the following command:
`openssl pkcs12 -export -out $HOME/.jclouds/docker.p12 -inkey $HOME/.boot2docker/certs/boot2docker-vm/key.pem -in $HOME/.boot2docker/certs/boot2docker-vm/cert.pem -certfile $HOME/.boot2docker/certs/boot2docker-vm/ca.pem`
#How it works
@ -41,8 +45,8 @@ Please follow these steps to configure your workstation for jclouds-docker:
As jclouds docker support is quite new, issues may occasionally arise. Please follow these steps to get things going again:
1. Remove all containers
`$ docker ps -a -q | xargs docker stop | xargs docker rm`
`$ docker ps -aq | xargs docker rm -f`
2. remove all the images
`$ docker images -q | xargs docker rmi`
`$ docker images -q | xargs docker rmi -f`

View File

@ -131,6 +131,7 @@
<goal>test</goal>
</goals>
<configuration>
<threadCount>1</threadCount>
<systemPropertyVariables>
<test.docker.endpoint>${test.docker.endpoint}</test.docker.endpoint>
<test.docker.api-version>${test.docker.api-version}</test.docker.api-version>

View File

@ -16,22 +16,22 @@
*/
package org.jclouds.docker;
import org.jclouds.docker.features.RemoteApi;
import org.jclouds.docker.features.ContainerApi;
import org.jclouds.docker.features.ImageApi;
import org.jclouds.docker.features.MiscApi;
import org.jclouds.rest.annotations.Delegate;
import java.io.Closeable;
/**
* Provides synchronous access to Docker Remote API.
*
* @see <a href="https://docs.docker.com/reference/api/docker_remote_api/"></a>
*/
public interface DockerApi extends Closeable {
/**
* Provides synchronous access to Docker Remote API features.
*/
@Delegate
RemoteApi getRemoteApi();
MiscApi getMiscApi();
@Delegate
ContainerApi getContainerApi();
@Delegate
ImageApi getImageApi();
}

View File

@ -56,7 +56,7 @@ public class DockerApiMetadata extends BaseHttpApiMetadata<DockerApi> {
properties.setProperty("jclouds.ssh.retry-auth", "true");
properties.setProperty(Constants.PROPERTY_CONNECTION_TIMEOUT, "1200000"); // 15 minutes
properties.setProperty(ComputeServiceProperties.IMAGE_LOGIN_USER, "root:password");
properties.setProperty(TEMPLATE, "osFamily=UBUNTU,os64Bit=true,osVersionMatches=1[012].[01][04]");
properties.setProperty(TEMPLATE, "osFamily=UBUNTU,os64Bit=true");
return properties;
}
@ -66,16 +66,16 @@ public class DockerApiMetadata extends BaseHttpApiMetadata<DockerApi> {
super(DockerApi.class);
id("docker")
.name("Docker API")
.identityName("user")
.credentialName("password")
.identityName("Path to Certificate .p12 file")
.credentialName("Password to Certificate")
.documentation(URI.create("https://docs.docker.com/reference/api/docker_remote_api/"))
.version("1.12")
.defaultEndpoint("http://127.0.0.1:2375")
.version("1.15")
.defaultEndpoint("https://127.0.0.1:2376")
.defaultProperties(DockerApiMetadata.defaultProperties())
.view(typeToken(ComputeServiceContext.class))
.defaultModules(ImmutableSet.<Class<? extends Module>>of(
DockerHttpApiModule.class,
DockerParserModule.class,
DockerHttpApiModule.class,
DockerComputeServiceContextModule.class));
}

View File

@ -100,7 +100,6 @@ public class ContainerToNodeMetadata implements Function<Container, NodeMetadata
Image image = images.get().get(imageId);
builder.operatingSystem(image.getOperatingSystem());
}
return builder.build();
}

View File

@ -19,6 +19,7 @@ package org.jclouds.docker.compute.functions;
import com.google.common.base.Function;
import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
import org.jclouds.compute.domain.Image;
import org.jclouds.compute.domain.ImageBuilder;
import org.jclouds.compute.domain.OperatingSystem;
@ -44,6 +45,7 @@ public class ImageToImage implements Function<org.jclouds.docker.domain.Image, o
@Override
public Image apply(org.jclouds.docker.domain.Image from) {
checkNotNull(from, "image");
String description = checkNotNull(Iterables.getFirst(from.repoTags(), "image must have at least one repo tag"));
OsFamily osFamily = osFamily().apply(description);

View File

@ -215,7 +215,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable
}
/**
* @see DockerTemplateOptions#memory(int)
* @see DockerTemplateOptions#memory
*/
public static DockerTemplateOptions memory(int memory) {
DockerTemplateOptions options = new DockerTemplateOptions();

View File

@ -37,6 +37,7 @@ import org.jclouds.docker.DockerApi;
import org.jclouds.docker.compute.options.DockerTemplateOptions;
import org.jclouds.docker.domain.Config;
import org.jclouds.docker.domain.Container;
import org.jclouds.docker.domain.ContainerSummary;
import org.jclouds.docker.domain.HostConfig;
import org.jclouds.docker.domain.Image;
import org.jclouds.docker.options.ListContainerOptions;
@ -78,7 +79,7 @@ public class DockerComputeServiceAdapter implements
String imageId = checkNotNull(template.getImage().getId(), "template image id must not be null");
String loginUser = template.getImage().getDefaultCredentials().getUser();
String loginUserPassword = template.getImage().getDefaultCredentials().getPassword();
String loginUserPassword = template.getImage().getDefaultCredentials().getOptionalPassword().or("password");
DockerTemplateOptions templateOptions = DockerTemplateOptions.class.cast(template.getOptions());
int[] inboundPorts = templateOptions.getInboundPorts();
@ -119,10 +120,15 @@ public class DockerComputeServiceAdapter implements
}
containerConfigBuilder.volumes(volumes);
}
if (templateOptions.getEnv().isPresent()) {
containerConfigBuilder.env(templateOptions.getEnv().get());
}
Config containerConfig = containerConfigBuilder.build();
logger.debug(">> creating new container with containerConfig(%s)", containerConfig);
Container container = api.getRemoteApi().createContainer(name, containerConfig);
Container container = api.getContainerApi().createContainer(name, containerConfig);
logger.trace("<< container(%s)", container.id());
HostConfig.Builder hostConfigBuilder = HostConfig.builder()
@ -140,13 +146,13 @@ public class DockerComputeServiceAdapter implements
}
HostConfig hostConfig = hostConfigBuilder.build();
api.getRemoteApi().startContainer(container.id(), hostConfig);
container = api.getRemoteApi().inspectContainer(container.id());
api.getContainerApi().startContainer(container.id(), hostConfig);
container = api.getContainerApi().inspectContainer(container.id());
if (container.state().exitCode() != 0) {
destroyNode(container.id());
throw new IllegalStateException(String.format("Container %s has not started correctly", container.id()));
}
return new NodeAndInitialCredentials<Container>(container, container.id(),
return new NodeAndInitialCredentials(container, container.id(),
LoginCredentials.builder().user(loginUser).password(loginUserPassword).build());
}
@ -156,21 +162,21 @@ public class DockerComputeServiceAdapter implements
// todo they are only placeholders at the moment
hardware.add(new HardwareBuilder().ids("micro").hypervisor("lxc").name("micro").processor(new Processor(1, 1)).ram(512).build());
hardware.add(new HardwareBuilder().ids("small").hypervisor("lxc").name("small").processor(new Processor(1, 1)).ram(1024).build());
hardware.add(new HardwareBuilder().ids("medium").hypervisor("lxc").name("medium").processor(new Processor(1, 1)).ram(2048).build());
hardware.add(new HardwareBuilder().ids("large").hypervisor("lxc").name("large").processor(new Processor(1, 1)).ram(3072).build());
hardware.add(new HardwareBuilder().ids("medium").hypervisor("lxc").name("medium").processor(new Processor(2, 1)).ram(2048).build());
hardware.add(new HardwareBuilder().ids("large").hypervisor("lxc").name("large").processor(new Processor(2, 1)).ram(3072).build());
return hardware;
}
@Override
public Set<Image> listImages() {
Set<Image> images = Sets.newHashSet();
for (Image image : api.getRemoteApi().listImages()) {
for (Image image : api.getImageApi().listImages()) {
// less efficient than just listImages but returns richer json that needs repoTags coming from listImages
Image inspected = api.getRemoteApi().inspectImage(image.id());
Image inspected = api.getImageApi().inspectImage(image.id());
if (inspected.repoTags().isEmpty()) {
inspected = Image.create(inspected.id(), inspected.parent(), inspected.created(), inspected.container(),
inspected.dockerVersion(), inspected.architecture(), inspected.os(), inspected.size(),
inspected.virtualSize(), image.repoTags());
inspected = Image.create(inspected.id(), inspected.parent(), inspected.container(), inspected.created(),
inspected.dockerVersion(), inspected.architecture(), inspected.os(), inspected.size(),
inspected.virtualSize(), image.repoTags());
}
images.add(inspected);
}
@ -192,9 +198,9 @@ public class DockerComputeServiceAdapter implements
@Override
public Iterable<Container> listNodes() {
Set<Container> containers = Sets.newHashSet();
for (Container container : api.getRemoteApi().listContainers(ListContainerOptions.Builder.all(true))) {
for (ContainerSummary containerSummary : api.getContainerApi().listContainers(ListContainerOptions.Builder.all(true))) {
// less efficient than just listNodes but returns richer json
containers.add(api.getRemoteApi().inspectContainer(container.id()));
containers.add(api.getContainerApi().inspectContainer(containerSummary.id()));
}
return containers;
}
@ -203,7 +209,7 @@ public class DockerComputeServiceAdapter implements
public Iterable<Container> listNodesByIds(final Iterable<String> ids) {
Set<Container> containers = Sets.newHashSet();
for (String id : ids) {
containers.add(api.getRemoteApi().inspectContainer(id));
containers.add(api.getContainerApi().inspectContainer(id));
}
return containers;
}
@ -215,18 +221,18 @@ public class DockerComputeServiceAdapter implements
@Override
public Container getNode(String id) {
return api.getRemoteApi().inspectContainer(id);
return api.getContainerApi().inspectContainer(id);
}
@Override
public void destroyNode(String id) {
api.getRemoteApi().removeContainer(id, RemoveContainerOptions.Builder.force(true));
api.getContainerApi().removeContainer(id, RemoveContainerOptions.Builder.force(true));
}
@Override
public void rebootNode(String id) {
api.getRemoteApi().stopContainer(id);
api.getRemoteApi().startContainer(id);
api.getContainerApi().stopContainer(id);
api.getContainerApi().startContainer(id);
}
@Override

View File

@ -16,8 +16,14 @@
*/
package org.jclouds.docker.config;
import java.security.KeyStore;
import javax.net.ssl.SSLContext;
import org.jclouds.docker.DockerApi;
import org.jclouds.docker.handlers.DockerErrorHandler;
import org.jclouds.docker.suppliers.KeyStoreSupplier;
import org.jclouds.docker.suppliers.SSLContextWithKeysSupplier;
import org.jclouds.http.HttpErrorHandler;
import org.jclouds.http.annotation.ClientError;
import org.jclouds.http.annotation.Redirection;
@ -25,6 +31,9 @@ import org.jclouds.http.annotation.ServerError;
import org.jclouds.rest.ConfiguresHttpApi;
import org.jclouds.rest.config.HttpApiModule;
import com.google.common.base.Supplier;
import com.google.inject.TypeLiteral;
/**
* Configures the Docker connection.
*/
@ -37,4 +46,18 @@ public class DockerHttpApiModule extends HttpApiModule<DockerApi> {
bind(HttpErrorHandler.class).annotatedWith(ClientError.class).to(DockerErrorHandler.class);
bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to(DockerErrorHandler.class);
}
/**
* This configures SSL certificate authentication when the Docker daemon is set to use an encrypted TCP socket
*/
@Override
protected void configure() {
super.configure();
bind(new TypeLiteral<Supplier<SSLContext>>() {
}).to(new TypeLiteral<SSLContextWithKeysSupplier>() {
});
bind(new TypeLiteral<Supplier<KeyStore>>() {
}).to(new TypeLiteral<KeyStoreSupplier>() {
});
}
}

View File

@ -123,6 +123,7 @@ public abstract class Config {
private List<String> entrypoint = ImmutableList.of();
private boolean networkDisabled;
private List<String> onBuild = ImmutableList.of();
private Map<String, String> restartPolicy = ImmutableMap.of();
public Builder hostname(String hostname) {
this.hostname = hostname;
@ -239,6 +240,11 @@ public abstract class Config {
return this;
}
public Builder restartPolicy(Map<String, String> restartPolicy) {
this.restartPolicy = ImmutableMap.copyOf(restartPolicy);
return this;
}
public Config build() {
return Config.create(hostname, domainname, user, memory, memorySwap, cpuShares, attachStdin, attachStdout,
attachStderr, exposedPorts, tty, openStdin, stdinOnce, env, cmd, dns, image, volumes, volumesFrom,

View File

@ -0,0 +1,49 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jclouds.docker.domain;
import static org.jclouds.docker.internal.NullSafeCopies.copyOf;
import java.util.List;
import org.jclouds.json.SerializedNames;
import com.google.auto.value.AutoValue;
// TODO it may be redundant (we already have Container value class)
@AutoValue
public abstract class ContainerSummary {
public abstract String id();
public abstract List<String> names();
public abstract String created();
public abstract String image();
public abstract String command();
public abstract List<Port> ports();
public abstract String status();
@SerializedNames({"Id", "Names", "Created", "Image", "Command", "Ports", "Status"})
public static ContainerSummary create(String id, List<String> names, String created, String image, String command, List<Port> ports, String status) {
return new AutoValue_ContainerSummary(id, copyOf(names), created, image, command, copyOf(ports), status);
}
}

View File

@ -35,7 +35,7 @@ public abstract class HostConfig {
public abstract List<String> binds();
public abstract Map<String, String> lxcConf();
public abstract List<Map<String, String>> lxcConf();
public abstract boolean privileged();
@ -53,7 +53,7 @@ public abstract class HostConfig {
@SerializedNames({ "ContainerIDFile", "Binds", "LxcConf", "Privileged", "Dns", "DnsSearch", "PortBindings",
"Links", "PublishAllPorts", "VolumesFrom" })
public static HostConfig create(String containerIDFile, List<String> binds, Map<String, String> lxcConf,
public static HostConfig create(String containerIDFile, List<String> binds, List<Map<String, String>> lxcConf,
boolean privileged, String dns, String dnsSearch, Map<String, List<Map<String, String>>> portBindings,
List<String> links, boolean publishAllPorts, List<String> volumesFrom) {
return new AutoValue_HostConfig(containerIDFile, copyOf(binds), copyOf(lxcConf), privileged, dns, dnsSearch,
@ -72,7 +72,7 @@ public abstract class HostConfig {
private String containerIDFile;
private List<String> binds = Lists.newArrayList();
private Map<String, String> lxcConf = Maps.newLinkedHashMap();
private List<Map<String, String>> lxcConf = Lists.newArrayList();
private boolean privileged;
private String dns;
private String dnsSearch;
@ -91,8 +91,8 @@ public abstract class HostConfig {
return this;
}
public Builder lxcConf(Map<String, String> lxcConf) {
this.lxcConf.putAll(checkNotNull(lxcConf, "lxcConf"));
public Builder lxcConf(List<Map<String, String>> lxcConf) {
this.lxcConf.addAll(checkNotNull(lxcConf, "lxcConf"));
return this;
}

View File

@ -48,10 +48,11 @@ public abstract class Image {
public abstract List<String> repoTags();
@SerializedNames({ "Id", "Parent", "Created", "Container", "DockerVersion", "Architecture", "Os", "Size",
"VirtualSize", "RepoTags", "Architecture" })
"VirtualSize", "RepoTags" })
public static Image create(String id, String parent, String created, String container, String dockerVersion,
String architecture, String os, long size, long virtualSize, List<String> repoTags) {
return new AutoValue_Image(id, parent, created, container, dockerVersion, architecture, os, size, virtualSize,
copyOf(repoTags));
}
}

View File

@ -0,0 +1,63 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jclouds.docker.domain;
import org.jclouds.json.SerializedNames;
import com.google.auto.value.AutoValue;
@AutoValue
public abstract class Info {
public abstract int containers();
public abstract int images();
public abstract String driver();
public abstract String executionDriver();
public abstract String kernelVersion();
public abstract int debug();
public abstract int nFd();
public abstract int nGoroutines();
public abstract int nEventsListener();
public abstract String initPath();
public abstract String indexServerAddress();
public abstract int memoryLimit();
public abstract int swapLimit();
public abstract int iPv4Forwarding();
@SerializedNames(
{"Containers", "Images", "Driver", "ExecutionDriver", "KernelVersion", "Debug", "NFd", "NGoroutines",
"NEventsListener", "InitPath", "IndexServerAddress", "MemoryLimit", "SwapLimit", "IPv4Forwarding"})
public static Info create(int containers, int images, String driver, String executionDriver, String kernelVersion, int debug,
int nFd, int nGoroutines, int nEventsListener, String initPath, String indexServerAddress,
int memoryLimit, int swapLimit, int iPv4Forwarding) {
return new AutoValue_Info(containers, images, driver, executionDriver, kernelVersion, debug, nFd, nGoroutines,
nEventsListener, initPath, indexServerAddress, memoryLimit, swapLimit, iPv4Forwarding);
}
}

View File

@ -22,6 +22,9 @@ import com.google.auto.value.AutoValue;
@AutoValue
public abstract class Version {
public abstract String apiVersion();
public abstract String arch();
public abstract String gitCommit();
@ -34,9 +37,9 @@ public abstract class Version {
public abstract String version();
@SerializedNames({ "Arch", "GitCommit", "GoVersion", "KernelVersion", "Os", "Version" })
public static Version create(String arch, String gitCommit, String goVersion, String kernelVersion, String os,
String version) {
return new AutoValue_Version(arch, gitCommit, goVersion, kernelVersion, os, version);
@SerializedNames({ "ApiVersion", "Arch", "GitCommit", "GoVersion", "KernelVersion", "Os", "Version" })
public static Version create(String apiVersion, String arch, String gitCommit, String goVersion,
String kernelVersion, String os, String version) {
return new AutoValue_Version(apiVersion, arch, gitCommit, goVersion, kernelVersion, os, version);
}
}

View File

@ -16,9 +16,7 @@
*/
package org.jclouds.docker.features;
import java.io.Closeable;
import java.io.InputStream;
import java.util.Set;
import java.util.List;
import javax.inject.Named;
import javax.ws.rs.Consumes;
@ -33,34 +31,18 @@ import javax.ws.rs.core.MediaType;
import org.jclouds.Fallbacks;
import org.jclouds.docker.domain.Config;
import org.jclouds.docker.domain.Container;
import org.jclouds.docker.domain.ContainerSummary;
import org.jclouds.docker.domain.HostConfig;
import org.jclouds.docker.domain.Image;
import org.jclouds.docker.domain.Version;
import org.jclouds.docker.options.BuildOptions;
import org.jclouds.docker.options.CommitOptions;
import org.jclouds.docker.options.CreateImageOptions;
import org.jclouds.docker.options.DeleteImageOptions;
import org.jclouds.docker.options.ListContainerOptions;
import org.jclouds.docker.options.ListImageOptions;
import org.jclouds.docker.options.RemoveContainerOptions;
import org.jclouds.io.Payload;
import org.jclouds.rest.annotations.BinderParam;
import org.jclouds.rest.annotations.Fallback;
import org.jclouds.rest.annotations.Headers;
import org.jclouds.rest.binders.BindToJsonPayload;
@Consumes(MediaType.APPLICATION_JSON)
public interface RemoteApi extends Closeable {
/**
* Get the information of the current docker version.
*
* @return The information of the current docker version.
*/
@Named("version")
@GET
@Path("/version")
Version getVersion();
public interface ContainerApi {
/**
* List all running containers
@ -70,8 +52,8 @@ public interface RemoteApi extends Closeable {
@Named("containers:list")
@GET
@Path("/containers/json")
@Fallback(Fallbacks.EmptySetOnNotFoundOr404.class)
Set<Container> listContainers();
@Fallback(Fallbacks.EmptyListOnNotFoundOr404.class)
List<ContainerSummary> listContainers();
/**
* List all running containers
@ -82,8 +64,8 @@ public interface RemoteApi extends Closeable {
@Named("containers:list")
@GET
@Path("/containers/json")
@Fallback(Fallbacks.EmptySetOnNotFoundOr404.class)
Set<Container> listContainers(ListContainerOptions options);
@Fallback(Fallbacks.EmptyListOnNotFoundOr404.class)
List<ContainerSummary> listContainers(ListContainerOptions options);
/**
* Create a container
@ -172,85 +154,4 @@ public interface RemoteApi extends Closeable {
@Path("/commit")
Image commit(CommitOptions options);
/**
* List images
*
* @return the images available.
*/
@Named("images:list")
@GET
@Path("/images/json")
@Fallback(Fallbacks.EmptySetOnNotFoundOr404.class)
Set<Image> listImages();
/**
* List images
*
* @param options the configuration to list images (@see ListImageOptions)
* @return the images available.
*/
@Named("images:list")
@GET
@Path("/images/json")
@Fallback(Fallbacks.EmptySetOnNotFoundOr404.class)
Set<Image> listImages(ListImageOptions options);
/**
* Inspect an image
*
* @param imageName The id of the image to inspect.
* @return low-level information on the image name
*/
@Named("image:inspect")
@GET
@Path("/images/{name}/json")
Image inspectImage(@PathParam("name") String imageName);
/**
* Create an image, either by pull it from the registry or by importing it
*
* @param options the configuration to create an image (@see CreateImageOptions)
* @return a stream of the image creation.
*/
@Named("image:create")
@POST
@Path("/images/create")
InputStream createImage(CreateImageOptions options);
/**
* Delete an image.
*
* @param name the image name to be deleted
* @return the stream of the deletion execution.
*/
@Named("image:delete")
@DELETE
@Path("/images/{name}")
InputStream deleteImage(@PathParam("name") String name);
/**
* Remove the image from the filesystem by name
*
* @param name the name of the image to be removed
* @param options the image deletion's options (@see DeleteImageOptions)
* @return the stream of the deletion execution.
*/
@Named("image:delete")
@DELETE
@Path("/images/{name}")
InputStream deleteImage(@PathParam("name") String name, DeleteImageOptions options);
/**
* Build an image from Dockerfile via stdin
*
* @param inputStream The stream must be a tar archive compressed with one of the following algorithms: identity
* (no compression), gzip, bzip2, xz.
* @param options the image build's options (@see BuildOptions)
* @return a stream of the build execution
*/
@Named("image:build")
@POST
@Path("/build")
@Headers(keys = "Content-Type", values = "application/tar")
InputStream build(Payload inputStream, BuildOptions options);
}

View File

@ -0,0 +1,110 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jclouds.docker.features;
import java.io.InputStream;
import java.util.List;
import javax.inject.Named;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.MediaType;
import org.jclouds.Fallbacks;
import org.jclouds.docker.domain.Image;
import org.jclouds.docker.options.CreateImageOptions;
import org.jclouds.docker.options.DeleteImageOptions;
import org.jclouds.docker.options.ListImageOptions;
import org.jclouds.rest.annotations.Fallback;
@Consumes(MediaType.APPLICATION_JSON)
public interface ImageApi {
/**
* List images
*
* @return the images available.
*/
@Named("images:list")
@GET
@Path("/images/json")
@Fallback(Fallbacks.EmptyListOnNotFoundOr404.class)
List<Image> listImages();
/**
* List images
*
* @param options the configuration to list images (@see ListImageOptions)
* @return the images available.
*/
@Named("images:list")
@GET
@Path("/images/json")
@Fallback(Fallbacks.EmptyListOnNotFoundOr404.class)
List<Image> listImages(ListImageOptions options);
/**
* Inspect an image
*
* @param imageName The id of the image to inspect.
* @return low-level information on the image name
*/
@Named("image:inspect")
@GET
@Path("/images/{name}/json")
@Fallback(Fallbacks.VoidOnNotFoundOr404.class)
Image inspectImage(@PathParam("name") String imageName);
/**
* Create an image, either by pull it from the registry or by importing it
*
* @param options the configuration to create an image (@see CreateImageOptions)
* @return a stream of the image creation.
*/
@Named("image:create")
@POST
@Path("/images/create")
InputStream createImage(CreateImageOptions options);
/**
* Delete an image.
*
* @param name the image name to be deleted
* @return the stream of the deletion execution.
*/
@Named("image:delete")
@DELETE
@Path("/images/{name}")
InputStream deleteImage(@PathParam("name") String name);
/**
* Remove the image from the filesystem by name
*
* @param name the name of the image to be removed
* @param options the image deletion's options (@see DeleteImageOptions)
* @return the stream of the deletion execution.
*/
@Named("image:delete")
@DELETE
@Path("/images/{name}")
InputStream deleteImage(@PathParam("name") String name, DeleteImageOptions options);
}

View File

@ -0,0 +1,72 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jclouds.docker.features;
import java.io.InputStream;
import javax.inject.Named;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.MediaType;
import org.jclouds.docker.domain.Info;
import org.jclouds.docker.domain.Version;
import org.jclouds.docker.options.BuildOptions;
import org.jclouds.io.Payload;
import org.jclouds.rest.annotations.Headers;
@Consumes(MediaType.APPLICATION_JSON)
public interface MiscApi {
/**
* Get the information of the current docker version.
*
* @return The information of the current docker version.
*/
@Named("version")
@GET
@Path("/version")
Version getVersion();
/**
* Get the information of the current docker version.
*
* @return The information of the current docker version.
*/
@Named("info")
@GET
@Path("/info")
Info getInfo();
/**
* Build an image from Dockerfile via stdin
*
* @param inputStream The stream must be a tar archive compressed with one of the following algorithms: identity
* (no compression), gzip, bzip2, xz.
* @param options the image build's options (@see BuildOptions)
* @return a stream of the build execution
*/
@Named("image:build")
@POST
@Path("/build")
@Headers(keys = "Content-Type", values = "application/tar")
InputStream build(Payload inputStream, BuildOptions options);
}

View File

@ -0,0 +1,130 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jclouds.docker.suppliers;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Throwables.propagate;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Collection;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.jclouds.crypto.Crypto;
import org.jclouds.crypto.Pems;
import org.jclouds.domain.Credentials;
import org.jclouds.location.Provider;
import org.jclouds.rest.AuthorizationException;
import com.google.common.base.Charsets;
import com.google.common.base.Supplier;
import com.google.common.io.ByteSource;
@Singleton
public class KeyStoreSupplier implements Supplier<KeyStore> {
private final Crypto crypto;
private final Supplier<Credentials> creds;
@Inject
KeyStoreSupplier(Crypto crypto, @Provider Supplier<Credentials> creds) {
this.crypto = crypto;
this.creds = creds;
}
@Override
public KeyStore get() {
Credentials currentCreds = checkNotNull(creds.get(), "credential supplier returned null");
String cert = checkNotNull(currentCreds.identity, "credential supplier returned null identity (should be cert)");
String keyStorePassword = checkNotNull(currentCreds.credential,
"credential supplier returned null credential (should be keyStorePassword)");
try {
KeyStore keyStore = KeyStore.getInstance("PKCS12");
File certFile = new File(checkNotNull(cert));
if (certFile.isFile()) { // cert is path to pkcs12 file
FileInputStream stream = new FileInputStream(certFile);
try {
keyStore.load(stream, keyStorePassword.toCharArray());
} finally {
stream.close();
}
} else { // cert is PEM encoded, containing private key and certs
// split in private key and certs
int privateKeyBeginIdx = cert.indexOf("-----BEGIN PRIVATE KEY");
int privateKeyEndIdx = cert.indexOf("-----END PRIVATE KEY");
if (privateKeyBeginIdx != -1) {
String pemPrivateKey = cert.substring(privateKeyBeginIdx, privateKeyEndIdx + 26);
StringBuilder pemCerts = new StringBuilder();
int certsBeginIdx = 0;
do {
certsBeginIdx = cert.indexOf("-----BEGIN CERTIFICATE", certsBeginIdx);
if (certsBeginIdx >= 0) {
int certsEndIdx = cert.indexOf("-----END CERTIFICATE", certsBeginIdx) + 26;
pemCerts.append(cert.substring(certsBeginIdx, certsEndIdx));
certsBeginIdx = certsEndIdx;
}
} while (certsBeginIdx != -1);
// parse private key
KeySpec keySpec = Pems.privateKeySpec(ByteSource.wrap(pemPrivateKey.getBytes(Charsets.UTF_8)));
PrivateKey privateKey = crypto.rsaKeyFactory().generatePrivate(keySpec);
// populate keystore with private key and certs
CertificateFactory cf = CertificateFactory.getInstance("X.509");
@SuppressWarnings("unchecked")
Collection<Certificate> certs = (Collection<Certificate>) cf.generateCertificates(new ByteArrayInputStream(
pemCerts.toString().getBytes(Charsets.UTF_8)));
keyStore.load(null);
keyStore.setKeyEntry("dummy", privateKey, keyStorePassword.toCharArray(),
certs.toArray(new Certificate[0]));
} else {
throw new AuthorizationException();
}
}
return keyStore;
} catch (NoSuchAlgorithmException e) {
throw propagate(e);
} catch (KeyStoreException e) {
throw propagate(e);
} catch (CertificateException e) {
throw propagate(e);
} catch (FileNotFoundException e) {
throw propagate(e);
} catch (IOException e) {
throw propagate(e);
} catch (InvalidKeySpecException e) {
throw propagate(e);
}
}
}

View File

@ -0,0 +1,77 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jclouds.docker.suppliers;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Throwables.propagate;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import org.jclouds.domain.Credentials;
import org.jclouds.http.HttpUtils;
import org.jclouds.http.config.SSLModule.TrustAllCerts;
import org.jclouds.location.Provider;
import com.google.common.base.Supplier;
@Singleton
public class SSLContextWithKeysSupplier implements Supplier<SSLContext> {
private final Supplier<KeyStore> keyStore;
private final TrustManager[] trustManager;
private final Supplier<Credentials> creds;
@Inject
SSLContextWithKeysSupplier(Supplier<KeyStore> keyStore, @Provider Supplier<Credentials> creds, HttpUtils utils,
TrustAllCerts trustAllCerts) {
this.keyStore = keyStore;
this.trustManager = utils.trustAllCerts() ? new TrustManager[] { trustAllCerts } : null;
this.creds = creds;
}
@Override
public SSLContext get() {
Credentials currentCreds = checkNotNull(creds.get(), "credential supplier returned null");
String keyStorePassword = checkNotNull(currentCreds.credential,
"credential supplier returned null credential (should be keyStorePassword)");
KeyManagerFactory kmf;
try {
kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(keyStore.get(), keyStorePassword.toCharArray());
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(kmf.getKeyManagers(), trustManager, new SecureRandom());
return sc;
} catch (NoSuchAlgorithmException e) {
throw propagate(e);
} catch (UnrecoverableKeyException e) {
throw propagate(e);
} catch (KeyStoreException e) {
throw propagate(e);
} catch (KeyManagementException e) {
throw propagate(e);
}
}
}

View File

@ -31,6 +31,7 @@ import org.jboss.shrinkwrap.api.asset.ClassLoaderAsset;
import org.jboss.shrinkwrap.api.exporter.TarExporter;
import org.jclouds.Constants;
import org.jclouds.apis.BaseApiLiveTest;
import org.jclouds.compute.config.ComputeServiceProperties;
import org.jclouds.docker.DockerApi;
import org.jclouds.io.Payload;
import org.jclouds.io.Payloads;
@ -58,6 +59,7 @@ public class BaseDockerApiLiveTest extends BaseApiLiveTest<DockerApi> {
Properties overrides = super.setupProperties();
overrides.setProperty(Constants.PROPERTY_MAX_RETRIES, "15");
overrides.setProperty("jclouds.ssh.retry-auth", "true");
overrides.setProperty(ComputeServiceProperties.IMAGE_LOGIN_USER, "root:password");
return overrides;
}

View File

@ -22,17 +22,24 @@ import static org.testng.Assert.assertNotNull;
import java.util.Properties;
import java.util.Random;
import org.jclouds.compute.ComputeService;
import org.jclouds.compute.ComputeServiceAdapter.NodeAndInitialCredentials;
import org.jclouds.compute.domain.Hardware;
import org.jclouds.compute.domain.Template;
import org.jclouds.compute.domain.TemplateBuilder;
import org.jclouds.docker.DockerApi;
import org.jclouds.docker.compute.options.DockerTemplateOptions;
import org.jclouds.docker.compute.strategy.DockerComputeServiceAdapter;
import org.jclouds.docker.domain.Container;
import org.jclouds.docker.domain.Image;
import org.jclouds.docker.options.CreateImageOptions;
import org.jclouds.docker.options.DeleteImageOptions;
import org.jclouds.sshj.config.SshjSshClientModule;
import org.testng.annotations.AfterGroups;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.inject.Injector;
@ -41,27 +48,47 @@ import com.google.inject.Module;
@Test(groups = "live", singleThreaded = true, testName = "DockerComputeServiceAdapterLiveTest")
public class DockerComputeServiceAdapterLiveTest extends BaseDockerApiLiveTest {
private static final String SSHABLE_IMAGE = "tutum/ubuntu";
private static final String SSHABLE_IMAGE_TAG = "trusty";
private Image defaultImage;
private DockerComputeServiceAdapter adapter;
private TemplateBuilder templateBuilder;
private ComputeService computeService;
private NodeAndInitialCredentials<Container> guest;
@BeforeClass
protected void init() {
super.initialize();
String imageName = SSHABLE_IMAGE + ":" + SSHABLE_IMAGE_TAG;
Image image = api.getImageApi().inspectImage(imageName);
if (image == null) {
CreateImageOptions options = CreateImageOptions.Builder.fromImage(SSHABLE_IMAGE).tag(SSHABLE_IMAGE_TAG);
api.getImageApi().createImage(options);
}
defaultImage = api.getImageApi().inspectImage(imageName);
assertNotNull(defaultImage);
}
@Override
protected DockerApi create(Properties props, Iterable<Module> modules) {
Injector injector = newBuilder().modules(modules).overrides(props).buildInjector();
adapter = injector.getInstance(DockerComputeServiceAdapter.class);
templateBuilder = injector.getInstance(TemplateBuilder.class);
computeService = injector.getInstance(ComputeService.class);
return injector.getInstance(DockerApi.class);
}
public void testCreateNodeWithGroupEncodedIntoNameThenStoreCredentials() {
String group = "foo";
String name = "container-" + new Random().nextInt();
String name = "container" + new Random().nextInt();
Template template = templateBuilder.smallest()
.osDescriptionMatches("jclouds/default:latest").build();
Template template = templateBuilder.imageId(defaultImage.id()).build();
DockerTemplateOptions options = template.getOptions().as(DockerTemplateOptions.class);
options.env(ImmutableList.of("ROOT_PASS=password"));
guest = adapter.createNodeWithGroupEncodedIntoName(group, name, template);
assertEquals(guest.getNodeId(), guest.getNode().id() + "");
assertEquals(guest.getNodeId(), guest.getNode().id());
}
public void testListHardwareProfiles() {
@ -78,6 +105,9 @@ public class DockerComputeServiceAdapterLiveTest extends BaseDockerApiLiveTest {
if (guest != null) {
adapter.destroyNode(guest.getNode().id() + "");
}
if (defaultImage != null) {
api.getImageApi().deleteImage(defaultImage.id(), DeleteImageOptions.Builder.force(true));
}
super.tearDown();
}

View File

@ -16,30 +16,67 @@
*/
package org.jclouds.docker.compute;
import static com.google.common.collect.Iterables.getOnlyElement;
import static java.lang.String.format;
import static java.util.concurrent.TimeUnit.SECONDS;
import static java.util.logging.Logger.getAnonymousLogger;
import static org.jclouds.compute.options.RunScriptOptions.Builder.nameTask;
import static org.jclouds.compute.options.TemplateOptions.Builder.runAsRoot;
import static org.jclouds.util.Predicates2.retry;
import static org.testng.Assert.assertNotNull;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.concurrent.TimeUnit;
import org.jclouds.compute.ComputeService;
import org.jclouds.compute.JettyStatements;
import org.jclouds.compute.RunNodesException;
import org.jclouds.compute.domain.ExecResponse;
import org.jclouds.compute.domain.Image;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.Template;
import org.jclouds.compute.domain.TemplateBuilder;
import org.jclouds.compute.internal.BaseComputeServiceLiveTest;
import org.jclouds.compute.internal.BaseComputeServiceContextLiveTest;
import org.jclouds.compute.util.OpenSocketFinder;
import org.jclouds.docker.DockerApi;
import org.jclouds.docker.compute.options.DockerTemplateOptions;
import org.jclouds.docker.domain.Container;
import org.jclouds.docker.features.ImageApi;
import org.jclouds.docker.options.CreateImageOptions;
import org.jclouds.docker.options.DeleteImageOptions;
import org.jclouds.domain.LoginCredentials;
import org.jclouds.predicates.SocketOpen;
import org.jclouds.scriptbuilder.domain.Statement;
import org.jclouds.scriptbuilder.domain.Statements;
import org.jclouds.sshj.config.SshjSshClientModule;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeGroups;
import org.testng.annotations.Test;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.base.Stopwatch;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.net.HostAndPort;
import com.google.inject.Module;
/**
* Live tests for the {@link org.jclouds.compute.ComputeService} integration.
*/
@Test(groups = "live", singleThreaded = true, testName = "DockerComputeServiceLiveTest")
public class DockerComputeServiceLiveTest extends BaseComputeServiceLiveTest {
public class DockerComputeServiceLiveTest extends BaseComputeServiceContextLiveTest {
private static final String DEFAULT_JCLOUDS_IMAGE = "jclouds/default";
private static final String SSHABLE_IMAGE = "tutum/ubuntu";
private static final String SSHABLE_IMAGE_TAG = "trusty";
private Image defaultImage;
protected Template template;
protected Predicate<HostAndPort> socketTester;
protected OpenSocketFinder openSocketFinder;
protected ComputeService client;
public DockerComputeServiceLiveTest() {
provider = "docker";
@ -50,93 +87,134 @@ public class DockerComputeServiceLiveTest extends BaseComputeServiceLiveTest {
return new SshjSshClientModule();
}
@BeforeGroups(groups = { "integration", "live" })
@Override
public void setupContext() {
super.setupContext();
buildSocketTester();
}
@Override
protected void initializeContext() {
super.initializeContext();
Optional<? extends Image> optionalImage = Iterables.tryFind(client.listImages(), new Predicate<Image>() {
@Override
public boolean apply(Image image) {
return image.getName().equals(DEFAULT_JCLOUDS_IMAGE);
}
});
if (optionalImage.isPresent()) {
defaultImage = optionalImage.get();
} else {
Assert.fail("Please create an ssh-able image called " + DEFAULT_JCLOUDS_IMAGE);
client = view.getComputeService();
String imageName = SSHABLE_IMAGE + ":" + SSHABLE_IMAGE_TAG;
org.jclouds.docker.domain.Image image = imageApi().inspectImage(imageName);
if (image == null) {
CreateImageOptions options = CreateImageOptions.Builder.fromImage(SSHABLE_IMAGE).tag(SSHABLE_IMAGE_TAG);
imageApi().createImage(options);
}
image = imageApi().inspectImage(imageName);
defaultImage = client.getImage(image.id());
assertNotNull(defaultImage);
}
@AfterClass
@Override
protected void tearDownContext() {
super.tearDownContext();
if (defaultImage != null) {
imageApi().deleteImage(SSHABLE_IMAGE + ":" + SSHABLE_IMAGE_TAG, DeleteImageOptions.Builder.force(true));
}
}
@Override
private ImageApi imageApi() {
return client.getContext().unwrapApi(DockerApi.class).getImageApi();
}
protected Template buildTemplate(TemplateBuilder templateBuilder) {
return templateBuilder.imageId(defaultImage.getId()).build();
String imageName = SSHABLE_IMAGE + ":" + SSHABLE_IMAGE_TAG;
org.jclouds.docker.domain.Image image = imageApi().inspectImage(imageName);
if (image == null) {
CreateImageOptions options = CreateImageOptions.Builder.fromImage(SSHABLE_IMAGE).tag(SSHABLE_IMAGE_TAG);
imageApi().createImage(options);
}
image = imageApi().inspectImage(imageName);
defaultImage = client.getImage(image.id());
DockerTemplateOptions options = new DockerTemplateOptions();
options.env(ImmutableList.of("ROOT_PASS=password"));
options.overrideLoginCredentials(LoginCredentials.builder().identity("root").credential("password").build());
template = templateBuilder.imageId(defaultImage.getId()).options(options).build();
return template;
}
@Override
public void testOptionToNotBlock() throws Exception {
// Docker ComputeService implementation has to block until the node
// is provisioned, to be able to return it.
protected void createAndRunAServiceInGroup(String group) throws RunNodesException {
// note that some cloud providers do not support mixed case tag names
ImmutableMap<String, String> userMetadata = ImmutableMap.of("test", group);
ImmutableSet<String> tags = ImmutableSet.of(group);
Stopwatch watch = Stopwatch.createStarted();
template = buildTemplate(client.templateBuilder());
template.getOptions().inboundPorts(22, 8080).blockOnPort(22, 300).userMetadata(userMetadata).tags(tags);
NodeMetadata node = getOnlyElement(client.createNodesInGroup(group, 1, template));
long createSeconds = watch.elapsed(TimeUnit.SECONDS);
final String nodeId = node.getId();
//checkUserMetadataContains(node, userMetadata);
//checkTagsInNodeEquals(node, tags);
getAnonymousLogger().info(
format("<< available node(%s) os(%s) in %ss", node.getId(), node.getOperatingSystem(), createSeconds));
watch.reset().start();
client.runScriptOnNode(nodeId, JettyStatements.install(), nameTask("configure-jetty"));
long configureSeconds = watch.elapsed(TimeUnit.SECONDS);
getAnonymousLogger().info(
format(
"<< configured node(%s) with %s and jetty %s in %ss",
nodeId,
exec(nodeId, "java -fullversion"),
exec(nodeId, JettyStatements.version()), configureSeconds));
trackProcessOnNode(JettyStatements.start(), "start jetty", node);
client.runScriptOnNode(nodeId, JettyStatements.stop(), runAsRoot(false).wrapInInitScript(false));
trackProcessOnNode(JettyStatements.start(), "start jetty", node);
}
@Override
protected void checkTagsInNodeEquals(NodeMetadata node, ImmutableSet<String> tags) {
// Docker does not support tags
protected void trackProcessOnNode(Statement process, String processName, NodeMetadata node) {
ServiceStats stats = new ServiceStats();
Stopwatch watch = Stopwatch.createStarted();
ExecResponse exec = client.runScriptOnNode(node.getId(), process, runAsRoot(false).wrapInInitScript(false));
stats.backgroundProcessMilliseconds = watch.elapsed(TimeUnit.MILLISECONDS);
Container container = client.getContext().unwrapApi(DockerApi.class).getContainerApi().inspectContainer(node.getId());
Map<String, List<Map<String, String>>> ports = container.networkSettings().ports();
int port = Integer.parseInt(getOnlyElement(ports.get("8080/tcp")).get("HostPort"));
watch.reset().start();
HostAndPort socket;
try {
socket = openSocketFinder.findOpenSocketOnNode(node, port, 600, TimeUnit.SECONDS);
} catch (NoSuchElementException e) {
throw new NoSuchElementException(format("%s%n%s%s", e.getMessage(), exec.getOutput(), exec.getError()));
}
stats.socketOpenMilliseconds = watch.elapsed(TimeUnit.MILLISECONDS);
getAnonymousLogger().info(format("<< %s on node(%s)[%s] %s", processName, node.getId(), socket, stats));
}
@Override
protected void checkUserMetadataContains(NodeMetadata node, ImmutableMap<String, String> userMetadata) {
// Docker does not support user metadata
static class ServiceStats {
long backgroundProcessMilliseconds;
long socketOpenMilliseconds;
@Override
public String toString() {
return format("[backgroundProcessMilliseconds=%s, socketOpenMilliseconds=%s]",
backgroundProcessMilliseconds, socketOpenMilliseconds);
}
}
@Override
public void testCreateAndRunAService() throws Exception {
// Docker does not support blockOnPort
protected String exec(final String nodeId, String command) {
return exec(nodeId, Statements.exec(command));
}
@Override
@Test(enabled = true, dependsOnMethods = { "testCompareSizes" })
public void testAScriptExecutionAfterBootWithBasicTemplate() throws Exception {
super.testAScriptExecutionAfterBootWithBasicTemplate();
protected String exec(final String nodeId, Statement command) {
return client.runScriptOnNode(nodeId, command, runAsRoot(false).wrapInInitScript(false)).getOutput().trim();
}
@Override
@Test(enabled = true, dependsOnMethods = "testReboot", expectedExceptions = UnsupportedOperationException.class)
public void testSuspendResume() throws Exception {
super.testSuspendResume();
}
@Override
@Test(enabled = true, dependsOnMethods = "testSuspendResume")
public void testGetNodesWithDetails() throws Exception {
super.testGetNodesWithDetails();
}
@Override
@Test(enabled = true, dependsOnMethods = "testSuspendResume")
public void testListNodes() throws Exception {
super.testListNodes();
}
@Override
@Test(enabled = true, dependsOnMethods = "testSuspendResume")
public void testListNodesByIds() throws Exception {
super.testListNodesByIds();
}
@Override
@Test(enabled = true, dependsOnMethods = { "testListNodes", "testGetNodesWithDetails", "testListNodesByIds" })
public void testDestroyNodes() {
super.testDestroyNodes();
}
@Test(enabled = true, expectedExceptions = NullPointerException.class)
public void testCorrectExceptionRunningNodesNotFound() throws Exception {
super.testCorrectExceptionRunningNodesNotFound();
}
@Test(enabled = true, expectedExceptions = NullPointerException.class)
public void testCorrectAuthException() throws Exception {
// Docker does not support authentication yet
super.testCorrectAuthException();
protected void buildSocketTester() {
SocketOpen socketOpen = view.utils().injector().getInstance(SocketOpen.class);
socketTester = retry(socketOpen, 60, 1, SECONDS);
// wait a maximum of 60 seconds for port 8080 to open.
openSocketFinder = context.utils().injector().getInstance(OpenSocketFinder.class);
}
}

View File

@ -43,6 +43,7 @@ import org.jclouds.docker.domain.State;
import org.jclouds.domain.Location;
import org.jclouds.domain.LocationBuilder;
import org.jclouds.domain.LocationScope;
import org.jclouds.domain.LoginCredentials;
import org.jclouds.providers.ProviderMetadata;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
@ -59,6 +60,9 @@ import com.google.inject.Guice;
*/
@Test(groups = "unit", testName = "ContainerToNodeMetadataTest")
public class ContainerToNodeMetadataTest {
private LoginCredentials credentials;
private ContainerToNodeMetadata function;
private Container container;
@ -164,6 +168,8 @@ public class ContainerToNodeMetadataTest {
}
};
credentials = LoginCredentials.builder().user("foo").password("bar").build();
function = new ContainerToNodeMetadata(providerMetadata, toPortableStatus(), namingConvention, images, locations);
}

View File

@ -33,6 +33,7 @@ import com.google.common.collect.ImmutableList;
*/
@Test(groups = "unit", testName = "ImageToImageTest")
public class ImageToImageTest {
private ImageToImage function;
private org.jclouds.docker.domain.Image image;

View File

@ -16,55 +16,52 @@
*/
package org.jclouds.docker.features;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
import java.io.IOException;
import java.net.URISyntaxException;
import java.io.InputStream;
import java.util.List;
import org.jclouds.docker.compute.BaseDockerApiLiveTest;
import org.jclouds.docker.domain.Config;
import org.jclouds.docker.domain.Container;
import org.jclouds.docker.domain.ContainerSummary;
import org.jclouds.docker.domain.Image;
import org.jclouds.docker.options.BuildOptions;
import org.jclouds.docker.options.CreateImageOptions;
import org.jclouds.docker.options.DeleteImageOptions;
import org.jclouds.rest.ResourceNotFoundException;
import org.jclouds.docker.options.ListContainerOptions;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
@Test(groups = "live", testName = "RemoteApiLiveTest", singleThreaded = true)
public class RemoteApiLiveTest extends BaseDockerApiLiveTest {
public class ContainerApiLiveTest extends BaseDockerApiLiveTest {
private static final String BUSYBOX_IMAGE = "busybox";
private Container container = null;
private Image image = null;
protected static final String BUSYBOX_IMAGE_TAG = "busybox:ubuntu-12.04";
protected Image image = null;
@Test
public void testVersion() {
assertEquals(api().getVersion().version(), "1.0.0");
}
@Test(dependsOnMethods = "testVersion")
public void testCreateImage() throws IOException, InterruptedException {
CreateImageOptions options = CreateImageOptions.Builder.fromImage(BUSYBOX_IMAGE);
consumeStream(api().createImage(options));
image = api().inspectImage(BUSYBOX_IMAGE);
@BeforeClass
protected void init() {
if (api.getImageApi().inspectImage(BUSYBOX_IMAGE_TAG) == null) {
CreateImageOptions options = CreateImageOptions.Builder.fromImage(BUSYBOX_IMAGE_TAG);
InputStream createImageStream = api.getImageApi().createImage(options);
consumeStream(createImageStream);
}
image = api.getImageApi().inspectImage(BUSYBOX_IMAGE_TAG);
assertNotNull(image);
}
@Test(dependsOnMethods = "testCreateImage")
public void testListImages() {
assertNotNull(api().listImages());
@AfterClass
protected void tearDown() {
if (image != null) {
api.getImageApi().deleteImage(BUSYBOX_IMAGE_TAG);
}
}
@Test(dependsOnMethods = "testListImages")
public void testCreateContainer() throws IOException, InterruptedException {
Config containerConfig = Config.builder().image(image.id())
.cmd(ImmutableList.of("/bin/sh", "-c", "while true; do echo hello world; sleep 1; done"))
@ -86,30 +83,23 @@ public class RemoteApiLiveTest extends BaseDockerApiLiveTest {
assertFalse(api().inspectContainer(container.id()).state().running());
}
@Test
public void testListContainers() {
List<ContainerSummary> containerSummaries = api().listContainers(ListContainerOptions.Builder.all(true));
for (ContainerSummary containerSummary : containerSummaries) {
assertNotNull(containerSummary.id());
assertNotNull(containerSummary.image());
assertFalse(containerSummary.names().isEmpty());
}
}
@Test(dependsOnMethods = "testStopContainer", expectedExceptions = NullPointerException.class)
public void testRemoveContainer() {
api().removeContainer(container.id());
assertFalse(api().inspectContainer(container.id()).state().running());
}
@Test(dependsOnMethods = "testRemoveContainer", expectedExceptions = ResourceNotFoundException.class)
public void testDeleteImage() {
consumeStream(api().deleteImage(image.id()));
assertNull(api().inspectImage(image.id()));
}
public void testBuildImage() throws IOException, InterruptedException, URISyntaxException {
BuildOptions options = BuildOptions.Builder.tag("testBuildImage").verbose(false).nocache(false);
String buildStream = consumeStream(api().build(tarredDockerfile(), options));
Iterable<String> splitted = Splitter.on("\n").split(buildStream.replace("\r", "").trim());
String lastStreamedLine = Iterables.getLast(splitted).trim();
String rawImageId = Iterables.getLast(Splitter.on("Successfully built ").split(lastStreamedLine));
String imageId = rawImageId.substring(0, 11);
Image image = api().inspectImage(imageId);
api().deleteImage(image.id(), DeleteImageOptions.Builder.force(true));
}
private RemoteApi api() {
return api.getRemoteApi();
private ContainerApi api() {
return api.getContainerApi();
}
}

View File

@ -16,50 +16,45 @@
*/
package org.jclouds.docker.features;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMultimap;
import com.squareup.okhttp.mockwebserver.MockResponse;
import com.squareup.okhttp.mockwebserver.MockWebServer;
import org.jclouds.docker.DockerApi;
import org.jclouds.docker.domain.Config;
import org.jclouds.docker.domain.Container;
import org.jclouds.docker.internal.BaseDockerMockTest;
import org.jclouds.docker.options.BuildOptions;
import org.jclouds.docker.options.CreateImageOptions;
import org.jclouds.docker.options.ListContainerOptions;
import org.jclouds.io.Payload;
import org.jclouds.io.Payloads;
import org.jclouds.rest.ResourceNotFoundException;
import org.testng.annotations.Test;
import java.io.File;
import java.io.FileInputStream;
import java.util.Set;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
import java.util.List;
import org.jclouds.docker.DockerApi;
import org.jclouds.docker.domain.Config;
import org.jclouds.docker.domain.Container;
import org.jclouds.docker.domain.ContainerSummary;
import org.jclouds.docker.internal.BaseDockerMockTest;
import org.jclouds.docker.options.ListContainerOptions;
import org.jclouds.rest.ResourceNotFoundException;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMultimap;
import com.squareup.okhttp.mockwebserver.MockResponse;
import com.squareup.okhttp.mockwebserver.MockWebServer;
/**
* Mock tests for the {@link org.jclouds.docker.DockerApi} class.
* Mock tests for the {@link org.jclouds.docker.features.ContainerApi} class.
*/
@Test(groups = "unit", testName = "RemoteApiMockTest")
public class RemoteApiMockTest extends BaseDockerMockTest {
@Test(groups = "unit", testName = "ContainerApiMockTest")
public class ContainerApiMockTest extends BaseDockerMockTest {
public void testListContainers() throws Exception {
MockWebServer server = mockWebServer();
server.enqueue(new MockResponse().setBody(payloadFromResource("/containers.json")));
DockerApi api = api(server.getUrl("/"));
RemoteApi remoteApi = api.getRemoteApi();
DockerApi dockerApi = api(server.getUrl("/"));
ContainerApi api = dockerApi.getContainerApi();
try {
Set<Container> containers = remoteApi.listContainers();
List<ContainerSummary> containerSummaries = api.listContainers();
assertRequestHasCommonFields(server.takeRequest(), "/containers/json");
assertEquals(containers.size(), 1);
assertEquals(containerSummaries.size(), 1);
} finally {
api.close();
dockerApi.close();
server.shutdown();
}
}
@ -67,16 +62,14 @@ public class RemoteApiMockTest extends BaseDockerMockTest {
public void testListNonexistentContainers() throws Exception {
MockWebServer server = mockWebServer();
server.enqueue(new MockResponse().setResponseCode(404));
DockerApi api = api(server.getUrl("/"));
RemoteApi remoteApi = api.getRemoteApi();
DockerApi dockerApi = api(server.getUrl("/"));
ContainerApi api = dockerApi.getContainerApi();
try {
Set<Container> containers = remoteApi.listContainers();
List<ContainerSummary> containerSummaries = api.listContainers();
assertRequestHasCommonFields(server.takeRequest(), "/containers/json");
assertTrue(containers.isEmpty());
assertTrue(containerSummaries.isEmpty());
} finally {
api.close();
dockerApi.close();
server.shutdown();
}
}
@ -86,14 +79,14 @@ public class RemoteApiMockTest extends BaseDockerMockTest {
public void testListAllContainers() throws Exception {
MockWebServer server = mockWebServer();
server.enqueue(new MockResponse().setBody(payloadFromResource("/containers.json")));
DockerApi api = api(server.getUrl("/"));
RemoteApi remoteApi = api.getRemoteApi();
DockerApi dockerApi = api(server.getUrl("/"));
ContainerApi api = dockerApi.getContainerApi();
try {
Set<Container> containers = remoteApi.listContainers(ListContainerOptions.Builder.all(true));
List<ContainerSummary> containerSummaries = api.listContainers(ListContainerOptions.Builder.all(true));
assertRequestHasParameters(server.takeRequest(), "/containers/json", ImmutableMultimap.of("all", "true"));
assertEquals(containers.size(), 1);
assertEquals(containerSummaries.size(), 1);
} finally {
api.close();
dockerApi.close();
server.shutdown();
}
}
@ -101,20 +94,20 @@ public class RemoteApiMockTest extends BaseDockerMockTest {
public void testGetContainer() throws Exception {
MockWebServer server = mockWebServer();
server.enqueue(new MockResponse().setBody(payloadFromResource("/container.json")));
DockerApi api = api(server.getUrl("/"));
RemoteApi remoteApi = api.getRemoteApi();
DockerApi dockerApi = api(server.getUrl("/"));
ContainerApi api = dockerApi.getContainerApi();
String containerId = "b03d4cd15b76f8876110615cdeed15eadf77c9beb408d62f1687dcc69192cd6d";
try {
Container container = remoteApi.inspectContainer(containerId);
Container container = api.inspectContainer(containerId);
assertRequestHasCommonFields(server.takeRequest(), "/containers/" + containerId + "/json");
assertNotNull(container);
assertNotNull(container.id(), containerId);
assertNotNull(container.config());
assertNotNull(container.hostConfig());
assertEquals(container.name(), "/tender_lumiere");
assertTrue(container.name().contains("/weave"));
assertEquals(container.state().running(), true);
} finally {
api.close();
dockerApi.close();
server.shutdown();
}
}
@ -122,14 +115,14 @@ public class RemoteApiMockTest extends BaseDockerMockTest {
public void testGetNonExistingContainer() throws Exception {
MockWebServer server = mockWebServer();
server.enqueue(new MockResponse().setResponseCode(404));
DockerApi api = api(server.getUrl("/"));
RemoteApi remoteApi = api.getRemoteApi();
DockerApi dockerApi = api(server.getUrl("/"));
ContainerApi api = dockerApi.getContainerApi();
String containerId = "notExisting";
try {
remoteApi.inspectContainer(containerId);
Container container = api.inspectContainer(containerId);
assertRequestHasCommonFields(server.takeRequest(), "/containers/" + containerId + "/json");
} finally {
api.close();
dockerApi.close();
server.shutdown();
}
}
@ -137,9 +130,8 @@ public class RemoteApiMockTest extends BaseDockerMockTest {
public void testCreateContainer() throws Exception {
MockWebServer server = mockWebServer();
server.enqueue(new MockResponse().setBody(payloadFromResource("/container-creation.json")));
DockerApi api = api(server.getUrl("/"));
RemoteApi remoteApi = api.getRemoteApi();
DockerApi dockerApi = api(server.getUrl("/"));
ContainerApi api = dockerApi.getContainerApi();
Config containerConfig = Config.builder().cmd(ImmutableList.of("date"))
.attachStdin(false)
.attachStderr(true)
@ -148,12 +140,12 @@ public class RemoteApiMockTest extends BaseDockerMockTest {
.image("base")
.build();
try {
Container container = remoteApi.createContainer("test", containerConfig);
Container container = api.createContainer("test", containerConfig);
assertRequestHasCommonFields(server.takeRequest(), "POST", "/containers/create?name=test");
assertNotNull(container);
assertEquals(container.id(), "c6c74153ae4b1d1633d68890a68d89c40aa5e284a1ea016cbc6ef0e634ee37b2");
} finally {
api.close();
dockerApi.close();
server.shutdown();
}
}
@ -161,16 +153,15 @@ public class RemoteApiMockTest extends BaseDockerMockTest {
public void testRemoveContainer() throws Exception {
MockWebServer server = mockWebServer();
server.enqueue(new MockResponse().setResponseCode(204));
DockerApi api = api(server.getUrl("/"));
RemoteApi remoteApi = api.getRemoteApi();
DockerApi dockerApi = api(server.getUrl("/"));
ContainerApi api = dockerApi.getContainerApi();
String containerId = "6d35806c1bd2b25cd92bba2d2c2c5169dc2156f53ab45c2b62d76e2d2fee14a9";
try {
remoteApi.removeContainer(containerId);
api.removeContainer(containerId);
assertRequestHasCommonFields(server.takeRequest(), "DELETE", "/containers/" + containerId);
} finally {
api.close();
dockerApi.close();
server.shutdown();
}
}
@ -178,16 +169,16 @@ public class RemoteApiMockTest extends BaseDockerMockTest {
public void testRemoveNonExistingContainer() throws Exception {
MockWebServer server = mockWebServer();
server.enqueue(new MockResponse().setResponseCode(404));
DockerApi api = api(server.getUrl("/"));
RemoteApi remoteApi = api.getRemoteApi();
DockerApi dockerApi = api(server.getUrl("/"));
ContainerApi api = dockerApi.getContainerApi();
String containerId = "nonExisting";
try {
remoteApi.removeContainer(containerId);
api.removeContainer(containerId);
fail("Remove container must fail on 404");
} catch (ResourceNotFoundException ex) {
// Expected exception
} finally {
api.close();
dockerApi.close();
server.shutdown();
}
}
@ -195,13 +186,13 @@ public class RemoteApiMockTest extends BaseDockerMockTest {
public void testStartContainer() throws Exception {
MockWebServer server = mockWebServer();
server.enqueue(new MockResponse().setResponseCode(200));
DockerApi api = api(server.getUrl("/"));
RemoteApi remoteApi = api.getRemoteApi();
DockerApi dockerApi = api(server.getUrl("/"));
ContainerApi api = dockerApi.getContainerApi();
try {
remoteApi.startContainer("1");
api.startContainer("1");
assertRequestHasCommonFields(server.takeRequest(), "POST", "/containers/1/start");
} finally {
api.close();
dockerApi.close();
server.shutdown();
}
}
@ -209,17 +200,17 @@ public class RemoteApiMockTest extends BaseDockerMockTest {
public void testStartNonExistingContainer() throws Exception {
MockWebServer server = mockWebServer();
server.enqueue(new MockResponse().setResponseCode(404));
DockerApi api = api(server.getUrl("/"));
RemoteApi remoteApi = api.getRemoteApi();
DockerApi dockerApi = api(server.getUrl("/"));
ContainerApi api = dockerApi.getContainerApi();
try {
try {
remoteApi.startContainer("1");
api.startContainer("1");
fail("Start container must fail on 404");
} catch (ResourceNotFoundException ex) {
// Expected exception
}
} finally {
api.close();
dockerApi.close();
server.shutdown();
}
}
@ -227,13 +218,13 @@ public class RemoteApiMockTest extends BaseDockerMockTest {
public void testStopContainer() throws Exception {
MockWebServer server = mockWebServer();
server.enqueue(new MockResponse().setResponseCode(200));
DockerApi api = api(server.getUrl("/"));
RemoteApi remoteApi = api.getRemoteApi();
DockerApi dockerApi = api(server.getUrl("/"));
ContainerApi api = dockerApi.getContainerApi();
try {
remoteApi.stopContainer("1");
api.stopContainer("1");
assertRequestHasCommonFields(server.takeRequest(), "POST", "/containers/1/stop");
} finally {
api.close();
dockerApi.close();
server.shutdown();
}
}
@ -241,97 +232,17 @@ public class RemoteApiMockTest extends BaseDockerMockTest {
public void testStopNonExistingContainer() throws Exception {
MockWebServer server = mockWebServer();
server.enqueue(new MockResponse().setResponseCode(404));
DockerApi api = api(server.getUrl("/"));
RemoteApi remoteApi = api.getRemoteApi();
DockerApi dockerApi = api(server.getUrl("/"));
ContainerApi api = dockerApi.getContainerApi();
try {
remoteApi.stopContainer("1");
api.stopContainer("1");
fail("Stop container must fail on 404");
} catch (ResourceNotFoundException ex) {
// Expected exception
} finally {
api.close();
dockerApi.close();
server.shutdown();
}
}
public void testCreateImage() throws Exception {
MockWebServer server = mockWebServer();
server.enqueue(new MockResponse().setResponseCode(200));
DockerApi api = api(server.getUrl("/"));
RemoteApi remoteApi = api.getRemoteApi();
try {
remoteApi.createImage(CreateImageOptions.Builder.fromImage("base"));
assertRequestHasParameters(server.takeRequest(), "POST", "/images/create", ImmutableMultimap.of("fromImage",
"base"));
} finally {
api.close();
server.shutdown();
}
}
public void testCreateImageFailure() throws Exception {
MockWebServer server = mockWebServer();
server.enqueue(new MockResponse().setResponseCode(404));
DockerApi api = api(server.getUrl("/"));
RemoteApi remoteApi = api.getRemoteApi();
try {
remoteApi.createImage(CreateImageOptions.Builder.fromImage("base"));
fail("Create image must fail on 404");
} catch (ResourceNotFoundException ex) {
// Expected exception
} finally {
api.close();
server.shutdown();
}
}
public void testDeleteImage() throws Exception {
MockWebServer server = mockWebServer();
server.enqueue(new MockResponse().setResponseCode(204));
DockerApi api = api(server.getUrl("/"));
RemoteApi remoteApi = api.getRemoteApi();
try {
remoteApi.deleteImage("1");
assertRequestHasCommonFields(server.takeRequest(), "DELETE", "/images/1");
} finally {
api.close();
server.shutdown();
}
}
public void testDeleteNotExistingImage() throws Exception {
MockWebServer server = mockWebServer();
server.enqueue(new MockResponse().setResponseCode(404));
DockerApi api = api(server.getUrl("/"));
RemoteApi remoteApi = api.getRemoteApi();
try {
remoteApi.deleteImage("1");
fail("Delete image must fail on 404");
} catch (ResourceNotFoundException ex) {
// Expected exception
} finally {
api.close();
server.shutdown();
}
}
public void testBuildContainerUsingPayload() throws Exception {
MockWebServer server = mockWebServer();
server.enqueue(new MockResponse().setResponseCode(200));
DockerApi api = api(server.getUrl("/"));
RemoteApi remoteApi = api.getRemoteApi();
File file = File.createTempFile("docker", "tmp");
FileInputStream data = new FileInputStream(file);
Payload payload = Payloads.newInputStreamPayload(data);
payload.getContentMetadata().setContentLength(file.length());
try {
remoteApi.build(payload, BuildOptions.NONE);
assertRequestHasCommonFields(server.takeRequest(), "POST", "/build");
} finally {
api.close();
server.shutdown();
}
}
}

View File

@ -0,0 +1,59 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jclouds.docker.features;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertNull;
import java.io.InputStream;
import org.jclouds.docker.compute.BaseDockerApiLiveTest;
import org.jclouds.docker.options.CreateImageOptions;
import org.testng.annotations.Test;
@Test(groups = "live", testName = "RemoteApiLiveTest", singleThreaded = true)
public class ImageApiLiveTest extends BaseDockerApiLiveTest {
private static final String DEFAULT_IMAGE = "busybox";
private static final String DEFAULT_TAG = "ubuntu-14.04";
@Test
public void testCreateImage() {
InputStream createImageStream = api().createImage(CreateImageOptions.Builder.fromImage(DEFAULT_IMAGE).tag(DEFAULT_TAG));
consumeStream(createImageStream);
}
@Test(dependsOnMethods = "testCreateImage")
public void testInspectImage() {
assertNotNull(api.getImageApi().inspectImage(String.format("%s:%s", DEFAULT_IMAGE, DEFAULT_TAG)));
}
@Test(dependsOnMethods = "testInspectImage")
public void testListImages() {
assertNotNull(api().listImages());
}
@Test(dependsOnMethods = "testListImages")
public void testDeleteImage() {
consumeStream(api().deleteImage(String.format("%s:%s", DEFAULT_IMAGE, DEFAULT_TAG)));
assertNull(api().inspectImage(String.format("%s:%s", DEFAULT_IMAGE, DEFAULT_TAG)));
}
private ImageApi api() {
return api.getImageApi();
}
}

View File

@ -0,0 +1,97 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jclouds.docker.features;
import static org.testng.Assert.fail;
import org.jclouds.docker.DockerApi;
import org.jclouds.docker.internal.BaseDockerMockTest;
import org.jclouds.docker.options.CreateImageOptions;
import org.jclouds.rest.ResourceNotFoundException;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableMultimap;
import com.squareup.okhttp.mockwebserver.MockResponse;
import com.squareup.okhttp.mockwebserver.MockWebServer;
/**
* Mock tests for the {@link org.jclouds.docker.features.ImageApi} class.
*/
@Test(groups = "unit", testName = "ImageApiMockTest")
public class ImageApiMockTest extends BaseDockerMockTest {
public void testCreateImage() throws Exception {
MockWebServer server = mockWebServer();
server.enqueue(new MockResponse().setResponseCode(200));
DockerApi dockerApi = api(server.getUrl("/"));
ImageApi api = dockerApi.getImageApi();
try {
api.createImage(CreateImageOptions.Builder.fromImage("base"));
assertRequestHasParameters(server.takeRequest(), "POST", "/images/create", ImmutableMultimap.of("fromImage", "base"));
} finally {
dockerApi.close();
server.shutdown();
}
}
public void testCreateImageFailure() throws Exception {
MockWebServer server = mockWebServer();
server.enqueue(new MockResponse().setResponseCode(404));
DockerApi dockerApi = api(server.getUrl("/"));
ImageApi api = dockerApi.getImageApi();
try {
api.createImage(CreateImageOptions.Builder.fromImage("base"));
fail("Create image must fail on 404");
} catch (ResourceNotFoundException ex) {
// Expected exception
} finally {
dockerApi.close();
server.shutdown();
}
}
public void testDeleteImage() throws Exception {
MockWebServer server = mockWebServer();
server.enqueue(new MockResponse().setResponseCode(204));
DockerApi dockerApi = api(server.getUrl("/"));
ImageApi api = dockerApi.getImageApi();
try {
api.deleteImage("1");
assertRequestHasCommonFields(server.takeRequest(), "DELETE", "/images/1");
} finally {
dockerApi.close();
server.shutdown();
}
}
public void testDeleteNotExistingImage() throws Exception {
MockWebServer server = mockWebServer();
server.enqueue(new MockResponse().setResponseCode(404));
DockerApi dockerApi = api(server.getUrl("/"));
ImageApi api = dockerApi.getImageApi();
try {
api.deleteImage("1");
fail("Delete image must fail on 404");
} catch (ResourceNotFoundException ex) {
// Expected exception
} finally {
dockerApi.close();
server.shutdown();
}
}
}

View File

@ -0,0 +1,86 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jclouds.docker.features;
import org.jclouds.docker.compute.BaseDockerApiLiveTest;
import org.jclouds.docker.options.BuildOptions;
import org.testng.annotations.AfterClass;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
@Test(groups = "live", testName = "MiscApiLiveTest", singleThreaded = true)
public class MiscApiLiveTest extends BaseDockerApiLiveTest {
private static final String API_VERSION = "1.15";
private static final String VERSION = "1.3.0";
private static final String GIT_COMMIT = "c78088f";
private static final String GO_VERSION = "go1.3.3";
private static final String KERNEL_VERSION = "3.16.4-tinycore64";
private static final String ARCH = "amd64";
private static final String OS = "linux";
private static String imageId;
@Test
public void testVersion() {
assertEquals(api().getVersion().apiVersion(), API_VERSION);
assertEquals(api().getVersion().version(), VERSION);
assertEquals(api().getVersion().gitCommit(), GIT_COMMIT);
assertEquals(api().getVersion().goVersion(), GO_VERSION);
assertEquals(api().getVersion().kernelVersion(), KERNEL_VERSION);
assertEquals(api().getVersion().arch(), ARCH);
assertEquals(api().getVersion().os(), OS);
}
@Test
public void testInfo() {
assertNotNull(api().getInfo());
}
@Test
public void testBuildImageFromDockerfile() throws IOException, InterruptedException, URISyntaxException {
BuildOptions options = BuildOptions.Builder.tag("testBuildImage").verbose(false).nocache(false);
InputStream buildImageStream = api().build(tarredDockerfile(), options);
String buildStream = consumeStream(buildImageStream);
Iterable<String> splitted = Splitter.on("\n").split(buildStream.replace("\r", "").trim());
String lastStreamedLine = Iterables.getLast(splitted).trim();
String rawImageId = Iterables.getLast(Splitter.on("Successfully built ").split(lastStreamedLine));
imageId = rawImageId.substring(0, 11);
assertNotNull(imageId);
}
@AfterClass
protected void tearDown() {
if (imageId != null) {
consumeStream(api.getImageApi().deleteImage(imageId));
}
}
private MiscApi api() {
return api.getMiscApi();
}
}

View File

@ -0,0 +1,159 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jclouds.docker.features;
import com.squareup.okhttp.mockwebserver.MockResponse;
import com.squareup.okhttp.mockwebserver.MockWebServer;
import org.jclouds.docker.DockerApi;
import org.jclouds.docker.domain.Info;
import org.jclouds.docker.domain.Version;
import org.jclouds.docker.internal.BaseDockerMockTest;
import org.jclouds.docker.options.BuildOptions;
import org.jclouds.io.Payload;
import org.jclouds.io.Payloads;
import org.jclouds.rest.ResourceNotFoundException;
import org.testng.annotations.Test;
import static org.jclouds.docker.compute.BaseDockerApiLiveTest.tarredDockerfile;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.fail;
import java.io.File;
import java.io.FileInputStream;
/**
* Mock tests for the {@link org.jclouds.docker.features.MiscApi} class.
*/
@Test(groups = "unit", testName = "MiscApiMockTest")
public class MiscApiMockTest extends BaseDockerMockTest {
private static final String API_VERSION = "1.15";
private static final String VERSION = "1.3.0";
private static final String GIT_COMMIT = "c78088f";
private static final String GO_VERSION = "go1.3.3";
private static final String KERNEL_VERSION = "3.16.4-tinycore64";
private static final String ARCH = "amd64";
private static final String OS = "linux";
public void testGetVersion() throws Exception {
MockWebServer server = mockWebServer();
server.enqueue(new MockResponse().setBody(payloadFromResource("/version.json")));
DockerApi dockerApi = api(server.getUrl("/"));
MiscApi api = dockerApi.getMiscApi();
try {
Version version = api.getVersion();
assertRequestHasCommonFields(server.takeRequest(), "/version");
assertNotNull(version);
assertEquals(version.version(), VERSION);
assertEquals(version.gitCommit(), GIT_COMMIT);
assertEquals(version.apiVersion(), API_VERSION);
assertEquals(version.goVersion(), GO_VERSION);
assertEquals(version.kernelVersion(), KERNEL_VERSION);
assertEquals(version.arch(), ARCH);
assertEquals(version.os(), OS);
} finally {
dockerApi.close();
server.shutdown();
}
}
public void testGetInfo() throws Exception {
MockWebServer server = mockWebServer();
server.enqueue(new MockResponse().setBody(payloadFromResource("/info.json")));
DockerApi dockerApi = api(server.getUrl("/"));
MiscApi api = dockerApi.getMiscApi();
try {
Info info = api.getInfo();
assertRequestHasCommonFields(server.takeRequest(), "/info");
assertNotNull(info);
assertNotNull(info.containers());
assertNotNull(info.debug());
assertNotNull(info.driver());
assertNotNull(info.executionDriver());
assertNotNull(info.images());
assertNotNull(info.indexServerAddress());
assertNotNull(info.initPath());
assertNotNull(info.iPv4Forwarding());
assertNotNull(info.kernelVersion());
assertNotNull(info.memoryLimit());
assertNotNull(info.nEventsListener());
assertNotNull(info.nFd());
assertNotNull(info.nGoroutines());
assertNotNull(info.swapLimit());
} finally {
dockerApi.close();
server.shutdown();
}
}
public void testBuildContainer() throws Exception {
MockWebServer server = mockWebServer();
server.enqueue(new MockResponse().setResponseCode(200));
DockerApi dockerApi = api(server.getUrl("/"));
MiscApi api = dockerApi.getMiscApi();
File dockerFile = File.createTempFile("docker", "tmp");
try {
api.build(tarredDockerfile(), BuildOptions.NONE);
assertRequestHasCommonFields(server.takeRequest(), "POST", "/build");
} finally {
dockerFile.delete();
dockerApi.close();
server.shutdown();
}
}
public void testBuildContainerUsingPayload() throws Exception {
MockWebServer server = mockWebServer();
server.enqueue(new MockResponse().setResponseCode(200));
DockerApi dockerApi = api(server.getUrl("/"));
MiscApi api = dockerApi.getMiscApi();
File file = File.createTempFile("docker", "tmp");
FileInputStream data = new FileInputStream(file);
Payload payload = Payloads.newInputStreamPayload(data);
payload.getContentMetadata().setContentLength(file.length());
try {
api.build(payload, BuildOptions.NONE);
assertRequestHasCommonFields(server.takeRequest(), "POST", "/build");
} finally {
dockerApi.close();
server.shutdown();
}
}
public void testBuildNonexistentContainer() throws Exception {
MockWebServer server = mockWebServer();
server.enqueue(new MockResponse().setResponseCode(404));
DockerApi dockerApi = api(server.getUrl("/"));
MiscApi api = dockerApi.getMiscApi();
File dockerFile = File.createTempFile("docker", "tmp");
try {
try {
api.build(tarredDockerfile(), BuildOptions.NONE);
fail("Build container must fail on 404");
} catch (ResourceNotFoundException ex) {
// Expected exception
}
} finally {
dockerFile.delete();
dockerApi.close();
server.shutdown();
}
}
}

View File

@ -58,8 +58,8 @@ public class BaseDockerMockTest {
return ContextBuilder.newBuilder(provider)
.credentials("clientid", "apikey")
.endpoint(url.toString())
.modules(modules)
.overrides(setupProperties())
.modules(modules)
.overrides(setupProperties())
.buildApi(DockerApi.class);
}

View File

@ -14,16 +14,19 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
FROM ubuntu:14.04
MAINTAINER Sven Dowideit <SvenDowideit@docker.com>
FROM centos:6.4
MAINTAINER Andrea Turli <andrea.turli@gmail.com>
RUN apt-get update && apt-get install -y openssh-server
RUN mkdir /var/run/sshd
RUN echo 'root:screencast' | chpasswd
RUN sed -i 's/PermitRootLogin without-password/PermitRootLogin yes/' /etc/ssh/sshd_config
# RUN yum -y groupinstall 'Development Tools'
RUN yum -y install openssh-server openssh-clients
# SSH login fix. Otherwise user is kicked off after login
RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd
RUN chkconfig sshd on
RUN service sshd start
RUN echo 'root:password' | chpasswd
ENV NOTVISIBLE "in users profile"
RUN echo "export VISIBLE=now" >> /etc/profile
EXPOSE 22
CMD ["/usr/sbin/sshd", "-D"]

View File

@ -0,0 +1,18 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
FROM scratch

View File

@ -1,81 +1,129 @@
{
"Args": [
"-c",
"yum -y install openssh-server openssh-clients"
],
"Config": {
"AttachStderr": false,
"AttachStdin": false,
"AttachStdout": false,
"Cmd": [
"/bin/sh",
"-c",
"yum -y install openssh-server openssh-clients"
"Args": [
"-iface",
"ethwe",
"-wait",
"5",
"-name",
"7a:63:a2:39:7b:0f"
],
"CpuShares": 0,
"Cpuset": "",
"Domainname": "",
"Entrypoint": [ "/usr/bin/sshd", "-d" ],
"Env": [
"HOME=/",
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"ExposedPorts": null,
"Hostname": "9088c45a9592",
"Image": "1e2d12a45cd57ae3fe3c31ede149d800aaf6a711c61646fad340080f531775c8",
"Memory": 0,
"MemorySwap": 0,
"NetworkDisabled": false,
"OnBuild": [],
"OpenStdin": false,
"PortSpecs": null,
"StdinOnce": false,
"Tty": false,
"User": "",
"Volumes": null,
"WorkingDir": ""
},
"Created": "2014-06-18T08:49:25.36448943Z",
"Driver": "aufs",
"ExecDriver": "native-0.2",
"HostConfig": {
"Binds": null,
"ContainerIDFile": "",
"Dns": null,
"DnsSearch": null,
"Links": null,
"LxcConf": null,
"NetworkMode": "",
"PortBindings": null,
"Privileged": false,
"PublishAllPorts": false,
"VolumesFrom": null
},
"HostnamePath": "/mnt/sda1/var/lib/docker/containers/be1d295c091720abc9a3105219ab75a0a7367d74156cc6048aa599fcc7d650e2/hostname",
"HostsPath": "/mnt/sda1/var/lib/docker/containers/be1d295c091720abc9a3105219ab75a0a7367d74156cc6048aa599fcc7d650e2/hosts",
"Id": "be1d295c091720abc9a3105219ab75a0a7367d74156cc6048aa599fcc7d650e2",
"Image": "1e2d12a45cd57ae3fe3c31ede149d800aaf6a711c61646fad340080f531775c8",
"MountLabel": "",
"Name": "/tender_lumiere",
"NetworkSettings": {
"Bridge": "docker0",
"Gateway": "172.17.42.1",
"IPAddress": "172.17.0.100",
"IPPrefixLen": 16,
"PortMapping": null,
"Ports": {}
},
"Path": "/bin/sh",
"ProcessLabel": "",
"ResolvConfPath": "/mnt/sda1/var/lib/docker/containers/be1d295c091720abc9a3105219ab75a0a7367d74156cc6048aa599fcc7d650e2/resolv.conf",
"State": {
"ExitCode": 0,
"FinishedAt": "0001-01-01T00:00:00Z",
"Paused": false,
"Pid": 16422,
"Running": true,
"StartedAt": "2014-06-18T08:49:25.63685385Z"
},
"Volumes": {},
"VolumesRW": {}
"Config": {
"AttachStderr": false,
"AttachStdin": false,
"AttachStdout": false,
"Cmd": [
"-name",
"7a:63:a2:39:7b:0f"
],
"CpuShares": 0,
"Cpuset": "",
"Domainname": "",
"Entrypoint": [
"/home/weave/weaver",
"-iface",
"ethwe",
"-wait",
"5"
],
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"ExposedPorts": {
"6783/tcp": {},
"6783/udp": {}
},
"Hostname": "6c9932f478bd",
"Image": "zettio/weave",
"Memory": 0,
"MemorySwap": 0,
"NetworkDisabled": false,
"OnBuild": null,
"OpenStdin": false,
"PortSpecs": null,
"SecurityOpt": null,
"StdinOnce": false,
"Tty": false,
"User": "",
"Volumes": null,
"WorkingDir": "/home/weave"
},
"Created": "2014-10-31T17:00:21.544197943Z",
"Driver": "aufs",
"ExecDriver": "native-0.2",
"HostConfig": {
"Binds": null,
"CapAdd": null,
"CapDrop": null,
"ContainerIDFile": "",
"Devices": [],
"Dns": null,
"DnsSearch": null,
"ExtraHosts": null,
"Links": null,
"LxcConf": [],
"NetworkMode": "bridge",
"PortBindings": {
"6783/tcp": [
{
"HostIp": "",
"HostPort": "6783"
}
],
"6783/udp": [
{
"HostIp": "",
"HostPort": "6783"
}
]
},
"Privileged": true,
"PublishAllPorts": false,
"RestartPolicy": {
"MaximumRetryCount": 0,
"Name": ""
},
"VolumesFrom": null
},
"HostnamePath": "/var/lib/docker/containers/6c9932f478bd761f32ddb54ed28ab42ab6fac6f2a279f561ea31503ee9d39524/hostname",
"HostsPath": "/var/lib/docker/containers/6c9932f478bd761f32ddb54ed28ab42ab6fac6f2a279f561ea31503ee9d39524/hosts",
"Id": "6c9932f478bd761f32ddb54ed28ab42ab6fac6f2a279f561ea31503ee9d39524",
"Image": "57e570db16baba1e8c0d6f3c15868ddb400f64ff76ec948e65c3ca3f15fb3587",
"MountLabel": "",
"Name": "/weave",
"NetworkSettings": {
"Bridge": "docker0",
"Gateway": "172.17.42.1",
"IPAddress": "172.17.0.7",
"IPPrefixLen": 16,
"MacAddress": "02:42:ac:11:00:07",
"PortMapping": null,
"Ports": {
"6783/tcp": [
{
"HostIp": "0.0.0.0",
"HostPort": "6783"
}
],
"6783/udp": [
{
"HostIp": "0.0.0.0",
"HostPort": "6783"
}
]
}
},
"Path": "/home/weave/weaver",
"ProcessLabel": "",
"ResolvConfPath": "/var/lib/docker/containers/6c9932f478bd761f32ddb54ed28ab42ab6fac6f2a279f561ea31503ee9d39524/resolv.conf",
"State": {
"ExitCode": 0,
"FinishedAt": "0001-01-01T00:00:00Z",
"Paused": false,
"Pid": 3939,
"Restarting": false,
"Running": true,
"StartedAt": "2014-10-31T17:00:21.802008706Z"
},
"Volumes": {},
"VolumesRW": {}
}

View File

@ -0,0 +1,28 @@
{
"Containers": 0,
"Debug": 1,
"Driver": "aufs",
"DriverStatus": [
[
"Root Dir",
"/mnt/sda1/var/lib/docker/aufs"
],
[
"Dirs",
"15"
]
],
"ExecutionDriver": "native-0.2",
"IPv4Forwarding": 1,
"Images": 15,
"IndexServerAddress": "https://index.docker.io/v1/",
"InitPath": "/usr/local/bin/docker",
"InitSha1": "",
"KernelVersion": "3.16.4-tinycore64",
"MemoryLimit": 1,
"NEventsListener": 0,
"NFd": 10,
"NGoroutines": 11,
"OperatingSystem": "Boot2Docker 1.3.0 (TCL 5.4); master : a083df4 - Thu Oct 16 17:05:03 UTC 2014",
"SwapLimit": 1
}

View File

@ -0,0 +1,9 @@
{
"ApiVersion": "1.15",
"Arch": "amd64",
"GitCommit": "c78088f",
"GoVersion": "go1.3.3",
"KernelVersion": "3.16.4-tinycore64",
"Os": "linux",
"Version": "1.3.0"
}