mirror of https://github.com/apache/jclouds.git
[JCLOUDS-1007] Implemented Docker Exec support in MiscApi
This commit is contained in:
parent
d7b3f5d98f
commit
f6ad2cc380
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Represents a response from Exec Create call (<code>POST /containers/(id)/exec</code>).
|
||||
*/
|
||||
@AutoValue
|
||||
public abstract class Exec {
|
||||
|
||||
public abstract String id();
|
||||
|
||||
@SerializedNames({ "Id"})
|
||||
public static Exec create(String id) {
|
||||
return new AutoValue_Exec(id);
|
||||
}
|
||||
}
|
|
@ -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.domain;
|
||||
|
||||
import static org.jclouds.docker.internal.NullSafeCopies.copyOf;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.jclouds.json.SerializedNames;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
|
||||
/**
|
||||
* Json Parameters (some of them) of Exec Create call.
|
||||
*/
|
||||
@AutoValue
|
||||
public abstract class ExecCreateParams {
|
||||
|
||||
public abstract boolean attachStdout();
|
||||
|
||||
public abstract boolean attachStderr();
|
||||
|
||||
public abstract List<String> cmd();
|
||||
|
||||
@SerializedNames({ "AttachStdout", "AttachStderr", "Cmd" })
|
||||
private static ExecCreateParams create(boolean attachStdout, boolean attachStderr, List<String> cmd) {
|
||||
return builder().attachStdout(attachStdout).attachStderr(attachStderr).cmd(cmd).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates builder for {@link ExecCreateParams}, it sets
|
||||
* {@link #attachStderr()} and {@link #attachStdout()} to true as a default.
|
||||
*
|
||||
* @return new {@link ExecCreateParams.Builder} instance
|
||||
*/
|
||||
public static Builder builder() {
|
||||
return new AutoValue_ExecCreateParams.Builder().attachStderr(true).attachStdout(true);
|
||||
}
|
||||
|
||||
@AutoValue.Builder
|
||||
public abstract static class Builder {
|
||||
|
||||
public abstract Builder attachStdout(boolean b);
|
||||
|
||||
public abstract Builder attachStderr(boolean b);
|
||||
|
||||
public abstract Builder cmd(List<String> cmd);
|
||||
|
||||
abstract List<String> cmd();
|
||||
|
||||
abstract ExecCreateParams autoBuild();
|
||||
|
||||
public ExecCreateParams build() {
|
||||
cmd(copyOf(cmd()));
|
||||
return autoBuild();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Represents a response (part of it) from Exec Inspect call (
|
||||
* <code>GET /exec/(id)/json</code>).
|
||||
*/
|
||||
@AutoValue
|
||||
public abstract class ExecInspect {
|
||||
|
||||
public abstract String id();
|
||||
|
||||
public abstract boolean running();
|
||||
|
||||
public abstract int exitCode();
|
||||
|
||||
@SerializedNames({ "ID", "Running", "ExitCode"})
|
||||
public static ExecInspect create(String id, boolean running, int exitCode) {
|
||||
return new AutoValue_ExecInspect(id, running, exitCode);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Json Parameter(s) (some of them) of Exec Start call.
|
||||
*/
|
||||
@AutoValue
|
||||
public abstract class ExecStartParams {
|
||||
|
||||
public abstract boolean detach();
|
||||
|
||||
@SerializedNames({ "Detach" })
|
||||
public static ExecStartParams create(boolean detach) {
|
||||
return builder().detach(detach).build();
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new AutoValue_ExecStartParams.Builder().detach(false);
|
||||
}
|
||||
|
||||
@AutoValue.Builder
|
||||
public abstract static class Builder {
|
||||
|
||||
public abstract Builder detach(boolean b);
|
||||
|
||||
public abstract ExecStartParams build();
|
||||
}
|
||||
}
|
|
@ -23,13 +23,21 @@ import javax.ws.rs.Consumes;
|
|||
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.docker.domain.Exec;
|
||||
import org.jclouds.docker.domain.ExecCreateParams;
|
||||
import org.jclouds.docker.domain.ExecInspect;
|
||||
import org.jclouds.docker.domain.ExecStartParams;
|
||||
import org.jclouds.docker.domain.Info;
|
||||
import org.jclouds.docker.domain.Version;
|
||||
import org.jclouds.docker.options.BuildOptions;
|
||||
import org.jclouds.docker.util.DockerInputStream;
|
||||
import org.jclouds.io.Payload;
|
||||
import org.jclouds.rest.annotations.BinderParam;
|
||||
import org.jclouds.rest.annotations.Headers;
|
||||
import org.jclouds.rest.binders.BindToJsonPayload;
|
||||
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Path("/v{jclouds.api-version}")
|
||||
|
@ -82,4 +90,51 @@ public interface MiscApi {
|
|||
@Headers(keys = { "Content-Type", "Connection" }, values = { "application/tar", "close" })
|
||||
InputStream build(Payload inputStream, BuildOptions options);
|
||||
|
||||
/**
|
||||
* Sets up an exec instance in a running container with given Id.
|
||||
*
|
||||
* @param containerId
|
||||
* container Id
|
||||
* @param execCreateParams
|
||||
* exec parameters
|
||||
* @return an instance which holds exec identifier
|
||||
*/
|
||||
@Named("container:exec")
|
||||
@POST
|
||||
@Path("/containers/{id}/exec")
|
||||
Exec execCreate(@PathParam("id") String containerId,
|
||||
@BinderParam(BindToJsonPayload.class) ExecCreateParams execCreateParams);
|
||||
|
||||
/**
|
||||
* Starts a previously set up exec instance id. If
|
||||
* {@link ExecStartParams#detach()} is true, this API returns after starting
|
||||
* the exec command. Otherwise, this API sets up an interactive session with
|
||||
* the exec command.
|
||||
*
|
||||
* @param execId
|
||||
* exec instance id
|
||||
* @param execStartParams
|
||||
* start parameters
|
||||
* @return raw docker stream which can be wrapped to
|
||||
* {@link DockerInputStream}
|
||||
* @see #execCreate(String, ExecCreateParams)
|
||||
* @see DockerInputStream
|
||||
*/
|
||||
@Named("exec:start")
|
||||
@POST
|
||||
@Path("/exec/{id}/start")
|
||||
InputStream execStart(@PathParam("id") String execId,
|
||||
@BinderParam(BindToJsonPayload.class) ExecStartParams execStartParams);
|
||||
|
||||
/**
|
||||
* Returns low-level information about the exec command id.
|
||||
*
|
||||
* @param execId
|
||||
* exec instance id
|
||||
* @return details about exec instance
|
||||
*/
|
||||
@Named("exec:inspect")
|
||||
@GET
|
||||
@Path("/exec/{id}/json")
|
||||
ExecInspect execInspect(@PathParam("id") String execId);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* 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.util;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Extension to {@link DataInputStream} which adds method
|
||||
* {@link #readStdStreamData()} to allow read multiplexed standard streams.
|
||||
*/
|
||||
public final class DockerInputStream extends DataInputStream {
|
||||
|
||||
/**
|
||||
* Ctor from superclass.
|
||||
*
|
||||
* @param in
|
||||
* @see DataInputStream#DataInputStream(InputStream)
|
||||
*/
|
||||
public DockerInputStream(InputStream in) {
|
||||
super(in);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link StdStreamData} instance read from the input stream or
|
||||
* <code>null</code> if we reached end of the stream.
|
||||
* @throws IOException
|
||||
*/
|
||||
public StdStreamData readStdStreamData() throws IOException {
|
||||
byte[] header = new byte[8];
|
||||
// try to read first byte from the message header - just to check if we
|
||||
// are at the end
|
||||
// of stream
|
||||
if (-1 == read(header, 0, 1)) {
|
||||
return null;
|
||||
}
|
||||
// read the rest of the header
|
||||
readFully(header, 1, 7);
|
||||
// decode size as an unsigned int
|
||||
long size = (long) (header[4] & 0xFF) << 24 | (header[5] & 0xFF) << 16 | (header[6] & 0xFF) << 8
|
||||
| (header[7] & 0xFF);
|
||||
|
||||
byte[] payload;
|
||||
// The size from the header is an unsigned int so it can happen the byte
|
||||
// array has not a sufficient size and we'll have to truncate the frame
|
||||
payload = new byte[(int) Math.min(Integer.MAX_VALUE, size)];
|
||||
readFully(payload);
|
||||
boolean truncated = false;
|
||||
if (size > Integer.MAX_VALUE) {
|
||||
truncated = true;
|
||||
// skip the rest
|
||||
readFully(new byte[(int) (size - Integer.MAX_VALUE)]);
|
||||
}
|
||||
return new StdStreamData(header[0], payload, truncated);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* 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.util;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Representation of single message from docker-raw-stream. It holds stream
|
||||
* type, data (payload) and flag which says if the payload was truncated. The
|
||||
* truncation can occur when the frame size is greater than
|
||||
* {@link Integer#MAX_VALUE}.
|
||||
*/
|
||||
public final class StdStreamData {
|
||||
|
||||
private final StdStreamType type;
|
||||
private final byte[] payload;
|
||||
private final boolean truncated;
|
||||
|
||||
/**
|
||||
* Ctor.
|
||||
*
|
||||
* @param streamTypeId
|
||||
* standard stream type (0=stdIn, 1=stdOut, 2=stdErr)
|
||||
* @param payload
|
||||
* message data - must not be <code>null</code>
|
||||
* @param truncated
|
||||
* @throws ArrayIndexOutOfBoundsException
|
||||
* if streamTypeId is not an index in {@link StdStreamType} enum.
|
||||
* @throws NullPointerException
|
||||
* if provided payload is <code>null</code>
|
||||
*/
|
||||
StdStreamData(byte streamTypeId, byte[] payload, boolean truncated)
|
||||
throws ArrayIndexOutOfBoundsException, NullPointerException {
|
||||
this.type = StdStreamType.values()[streamTypeId];
|
||||
this.payload = Arrays.copyOf(payload, payload.length);
|
||||
this.truncated = truncated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type of stream.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public StdStreamType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Data from this message.
|
||||
*
|
||||
* @return payload.
|
||||
*/
|
||||
public byte[] getPayload() {
|
||||
return payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag which says if the payload was truncated (because of size).
|
||||
*
|
||||
* @return true if truncated
|
||||
*/
|
||||
public boolean isTruncated() {
|
||||
return truncated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard streams enum. The order of entries is important!
|
||||
*/
|
||||
public static enum StdStreamType {
|
||||
IN, OUT, ERR;
|
||||
}
|
||||
}
|
|
@ -16,23 +16,79 @@
|
|||
*/
|
||||
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.assertTrue;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import org.assertj.core.api.Fail;
|
||||
import org.jclouds.docker.compute.BaseDockerApiLiveTest;
|
||||
import org.jclouds.docker.domain.Config;
|
||||
import org.jclouds.docker.domain.Container;
|
||||
import org.jclouds.docker.domain.Exec;
|
||||
import org.jclouds.docker.domain.ExecCreateParams;
|
||||
import org.jclouds.docker.domain.ExecInspect;
|
||||
import org.jclouds.docker.domain.ExecStartParams;
|
||||
import org.jclouds.docker.domain.Image;
|
||||
import org.jclouds.docker.options.BuildOptions;
|
||||
import org.jclouds.docker.options.CreateImageOptions;
|
||||
import org.jclouds.docker.options.RemoveContainerOptions;
|
||||
import org.jclouds.docker.util.DockerInputStream;
|
||||
import org.jclouds.docker.util.StdStreamData;
|
||||
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 = "MiscApiLiveTest", singleThreaded = true)
|
||||
public class MiscApiLiveTest extends BaseDockerApiLiveTest {
|
||||
|
||||
protected static final String BUSYBOX_IMAGE_TAG = "busybox:ubuntu-12.04";
|
||||
|
||||
private static String imageId;
|
||||
|
||||
private Container container = null;
|
||||
private Image image = null;
|
||||
private Exec exec = null;
|
||||
|
||||
@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);
|
||||
Config containerConfig = Config.builder().image(image.id())
|
||||
.cmd(ImmutableList.of("/bin/sh", "-c", "touch hello; while true; do echo hello world; sleep 1; done"))
|
||||
.build();
|
||||
container = api.getContainerApi().createContainer("miscApiTest", containerConfig);
|
||||
assertNotNull(container);
|
||||
api.getContainerApi().startContainer(container.id());
|
||||
assertTrue(api.getContainerApi().inspectContainer(container.id()).state().running());
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
protected void tearDown() {
|
||||
if (container != null) {
|
||||
if (api.getContainerApi().inspectContainer(container.id()) != null) {
|
||||
api.getContainerApi().removeContainer(container.id(), RemoveContainerOptions.Builder.force(true));
|
||||
}
|
||||
}
|
||||
if (image != null) {
|
||||
api.getImageApi().deleteImage(BUSYBOX_IMAGE_TAG);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVersion() {
|
||||
assertNotNull(api().getVersion().apiVersion());
|
||||
|
@ -61,9 +117,62 @@ public class MiscApiLiveTest extends BaseDockerApiLiveTest {
|
|||
assertNotNull(imageId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecCreate() {
|
||||
exec = api().execCreate(container.id(),
|
||||
ExecCreateParams.builder()
|
||||
.cmd(ImmutableList.<String> of("/bin/sh", "-c",
|
||||
"echo -n Standard >&1 && echo -n Error >&2 && exit 2"))
|
||||
.attachStderr(true).attachStdout(true).build());
|
||||
assertNotNull(exec);
|
||||
assertNotNull(exec.id());
|
||||
}
|
||||
|
||||
@Test(dependsOnMethods = "testExecCreate")
|
||||
public void testExecStart() throws IOException {
|
||||
final ExecStartParams startParams = ExecStartParams.builder().detach(false).build();
|
||||
DockerInputStream inputStream = null;
|
||||
try {
|
||||
inputStream = new DockerInputStream(api().execStart(exec.id(), startParams));
|
||||
assertNotNull(inputStream);
|
||||
ByteArrayOutputStream baosOut = new ByteArrayOutputStream();
|
||||
ByteArrayOutputStream baosErr = new ByteArrayOutputStream();
|
||||
StdStreamData data = null;
|
||||
while (null != (data = inputStream.readStdStreamData())) {
|
||||
assertFalse(data.isTruncated());
|
||||
switch (data.getType()) {
|
||||
case OUT:
|
||||
baosOut.write(data.getPayload());
|
||||
break;
|
||||
case ERR:
|
||||
baosErr.write(data.getPayload());
|
||||
break;
|
||||
default:
|
||||
Fail.fail("Unexpected Stream type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
assertEquals(baosOut.toString(), "Standard");
|
||||
assertEquals(baosErr.toString(), "Error");
|
||||
} finally {
|
||||
if (inputStream != null) {
|
||||
inputStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test(dependsOnMethods = "testExecStart")
|
||||
public void testExecInspect() throws IOException {
|
||||
ExecInspect execInspect = api().execInspect(exec.id());
|
||||
assertNotNull(execInspect);
|
||||
assertEquals(execInspect.id(), exec.id());
|
||||
assertEquals(execInspect.running(), false);
|
||||
assertEquals(execInspect.exitCode(), 2);
|
||||
}
|
||||
|
||||
|
||||
private MiscApi api() {
|
||||
return api.getMiscApi();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -16,25 +16,40 @@
|
|||
*/
|
||||
package org.jclouds.docker.features;
|
||||
|
||||
import com.squareup.okhttp.mockwebserver.MockResponse;
|
||||
import com.squareup.okhttp.mockwebserver.MockWebServer;
|
||||
import com.squareup.okhttp.mockwebserver.RecordedRequest;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.jclouds.docker.compute.BaseDockerApiLiveTest.tarredDockerfile;
|
||||
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 java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
|
||||
import org.jclouds.docker.DockerApi;
|
||||
import org.jclouds.docker.config.DockerParserModule;
|
||||
import org.jclouds.docker.domain.Exec;
|
||||
import org.jclouds.docker.domain.ExecCreateParams;
|
||||
import org.jclouds.docker.domain.ExecInspect;
|
||||
import org.jclouds.docker.domain.ExecStartParams;
|
||||
import org.jclouds.docker.internal.BaseDockerMockTest;
|
||||
import org.jclouds.docker.parse.InfoParseTest;
|
||||
import org.jclouds.docker.parse.VersionParseTest;
|
||||
import org.jclouds.docker.util.DockerInputStream;
|
||||
import org.jclouds.docker.util.StdStreamData;
|
||||
import org.jclouds.docker.util.StdStreamData.StdStreamType;
|
||||
import org.jclouds.io.Payload;
|
||||
import org.jclouds.io.Payloads;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.jclouds.docker.compute.BaseDockerApiLiveTest.tarredDockerfile;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.squareup.okhttp.mockwebserver.MockResponse;
|
||||
import com.squareup.okhttp.mockwebserver.MockWebServer;
|
||||
import com.squareup.okhttp.mockwebserver.RecordedRequest;
|
||||
|
||||
/**
|
||||
* Mock tests for the {@link org.jclouds.docker.features.MiscApi} class.
|
||||
|
@ -92,6 +107,64 @@ public class MiscApiMockTest extends BaseDockerMockTest {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public void testExecCreate() throws Exception {
|
||||
MockWebServer server = mockWebServer(new MockResponse().setBody(payloadFromResource("/exec.json")));
|
||||
MiscApi api = api(DockerApi.class, server.getUrl("/").toString(), new DockerParserModule()).getMiscApi();
|
||||
try {
|
||||
final String containerId = "a40d212a0a379de00426a1da2a8fd3fd20d5f74fd7c2dd42f6c93a6b1b0e6974";
|
||||
final ExecCreateParams execParams = ExecCreateParams.builder()
|
||||
.cmd(ImmutableList.<String> of("/bin/sh", "-c", "echo -n Standard >&1 && echo -n Error >&2"))
|
||||
.attachStderr(true).attachStdout(true).build();
|
||||
final Exec expectedExec = Exec.create("dbf45d296388032ebb9872edb75847f6655a72b4e9ab0d99ae1c75589c4ca957");
|
||||
assertEquals(api.execCreate(containerId, execParams), expectedExec);
|
||||
assertSent(server, "POST", "/containers/" + containerId + "/exec");
|
||||
} finally {
|
||||
server.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
public void testExecStart() throws Exception {
|
||||
MockWebServer server = mockWebServer(new MockResponse().setBody(payloadFromResource("/exec.start")));
|
||||
MiscApi api = api(DockerApi.class, server.getUrl("/").toString(), new DockerParserModule()).getMiscApi();
|
||||
DockerInputStream dis = null;
|
||||
try {
|
||||
final String execId = "dbf45d296388032ebb9872edb75847f6655a72b4e9ab0d99ae1c75589c4ca957";
|
||||
final ExecStartParams startParams = ExecStartParams.builder().detach(false).build();
|
||||
dis = new DockerInputStream(api.execStart(execId, startParams));
|
||||
|
||||
final StdStreamData msg1 = dis.readStdStreamData();
|
||||
assertFalse(msg1.isTruncated());
|
||||
assertEquals(msg1.getPayload(), "Standard".getBytes(StandardCharsets.UTF_8));
|
||||
assertEquals(msg1.getType(), StdStreamType.OUT);
|
||||
|
||||
final StdStreamData msg2 = dis.readStdStreamData();
|
||||
assertFalse(msg2.isTruncated());
|
||||
assertEquals(msg2.getPayload(), "Error".getBytes(StandardCharsets.UTF_8));
|
||||
assertEquals(msg2.getType(), StdStreamType.ERR);
|
||||
|
||||
assertNull(dis.readStdStreamData());
|
||||
assertSent(server, "POST", "/exec/" + execId + "/start");
|
||||
} finally {
|
||||
if (dis != null) {
|
||||
dis.close();
|
||||
}
|
||||
server.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
public void testExecInspect() throws IOException, InterruptedException {
|
||||
MockWebServer server = mockWebServer(new MockResponse().setBody(payloadFromResource("/execInspect.json")));
|
||||
MiscApi api = api(DockerApi.class, server.getUrl("/").toString(), new DockerParserModule()).getMiscApi();
|
||||
final String expectedExecId = "fda1cf8064863fc0667c691c69793fdb7d0bd4a1fabb8250536abe5203e4208a";
|
||||
ExecInspect execInspect = api.execInspect(expectedExecId);
|
||||
assertNotNull(execInspect);
|
||||
assertEquals(execInspect.id(), expectedExecId);
|
||||
assertEquals(execInspect.running(), false);
|
||||
assertEquals(execInspect.exitCode(), 2);
|
||||
assertSent(server, "GET", "/exec/" + expectedExecId + "/json");
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that correct values of HTTP headers are used in Docker build REST
|
||||
* API calls.
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
{"Id":"dbf45d296388032ebb9872edb75847f6655a72b4e9ab0d99ae1c75589c4ca957"}
|
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
{"ID":"fda1cf8064863fc0667c691c69793fdb7d0bd4a1fabb8250536abe5203e4208a","Running":false,"ExitCode":2,"ProcessConfig":{"privileged":false,"user":"","tty":false,"entrypoint":"/bin/sh","arguments":["-c","echo -n Standard \u003e\u00261 \u0026\u0026 echo -n Error \u003e\u00262 \u0026\u0026 exit 2"]},"OpenStdin":false,"OpenStderr":true,"OpenStdout":true,"Container":{"State":{"Running":true,"Paused":false,"Restarting":false,"OOMKilled":false,"Dead":false,"Pid":1561,"ExitCode":0,"Error":"","StartedAt":"2015-09-29T12:36:21.011469908Z","FinishedAt":"0001-01-01T00:00:00Z"},"ID":"07695c5170a1cbf3402552202fd28d2beb81fce5958a244e9ad7c8cb0ded936e","Created":"2015-09-29T12:36:19.829846377Z","Path":"/bin/sh","Args":["-c","touch hello; while true; do echo hello world; sleep 1; done"],"Config":{"Hostname":"07695c5170a1","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"PortSpecs":null,"ExposedPorts":null,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":null,"Cmd":["/bin/sh","-c","touch hello; while true; do echo hello world; sleep 1; done"],"Image":"ff8f955d1fed83a6239675b9a767fc9502db9934922a2f69ac6bf4cad8a7d7be","Volumes":null,"VolumeDriver":"","WorkingDir":"","Entrypoint":null,"NetworkDisabled":false,"MacAddress":"","OnBuild":null,"Labels":{},"Init":""},"Image":"ff8f955d1fed83a6239675b9a767fc9502db9934922a2f69ac6bf4cad8a7d7be","NetworkSettings":{"Bridge":"","EndpointID":"78d38a6f4d63966b16ca824a533b70f0a3a00490586688a188f67ec0bb07e68f","Gateway":"172.17.42.1","GlobalIPv6Address":"","GlobalIPv6PrefixLen":0,"HairpinMode":false,"IPAddress":"172.17.0.3","IPPrefixLen":16,"IPv6Gateway":"","LinkLocalIPv6Address":"","LinkLocalIPv6PrefixLen":0,"MacAddress":"02:42:ac:11:00:03","NetworkID":"880f733909fa4ad45c8fbc95b80c83e22a7b46f8cd7fb23dea62b0996ef1c8c6","PortMapping":null,"Ports":{},"SandboxKey":"/var/run/docker/netns/07695c5170a1","SecondaryIPAddresses":null,"SecondaryIPv6Addresses":null},"ResolvConfPath":"/var/lib/docker/containers/07695c5170a1cbf3402552202fd28d2beb81fce5958a244e9ad7c8cb0ded936e/resolv.conf","HostnamePath":"/var/lib/docker/containers/07695c5170a1cbf3402552202fd28d2beb81fce5958a244e9ad7c8cb0ded936e/hostname","HostsPath":"/var/lib/docker/containers/07695c5170a1cbf3402552202fd28d2beb81fce5958a244e9ad7c8cb0ded936e/hosts","LogPath":"/var/lib/docker/containers/07695c5170a1cbf3402552202fd28d2beb81fce5958a244e9ad7c8cb0ded936e/07695c5170a1cbf3402552202fd28d2beb81fce5958a244e9ad7c8cb0ded936e-json.log","Name":"/miscApiTest","Driver":"devicemapper","ExecDriver":"native-0.2","MountLabel":"","ProcessLabel":"","RestartCount":0,"UpdateDns":false,"MountPoints":{},"Volumes":{},"VolumesRW":{},"Init":"","AppArmorProfile":""}}
|
Loading…
Reference in New Issue