From 2d828ad527e41cbfd04b1e9777b0e3a92ea1baee Mon Sep 17 00:00:00 2001 From: Marcus Linke Date: Fri, 6 Nov 2015 19:06:59 +0100 Subject: [PATCH 01/13] Added AHC dependency --- pom.xml | 9 +- .../ahc/AttachContainerCmdExec.java | 101 ++++++++++++++++++ 2 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/github/dockerjava/ahc/AttachContainerCmdExec.java diff --git a/pom.xml b/pom.xml index dfee63050..60fac661f 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,5 @@ - + 4.0.0 @@ -201,6 +202,12 @@ 3.0.0 provided + + + org.asynchttpclient + async-http-client + 2.0.0-alpha22 + diff --git a/src/main/java/com/github/dockerjava/ahc/AttachContainerCmdExec.java b/src/main/java/com/github/dockerjava/ahc/AttachContainerCmdExec.java new file mode 100644 index 000000000..99be7d507 --- /dev/null +++ b/src/main/java/com/github/dockerjava/ahc/AttachContainerCmdExec.java @@ -0,0 +1,101 @@ +package com.github.dockerjava.ahc; + +import java.io.ByteArrayOutputStream; +import java.net.URL; +import java.util.concurrent.Future; + +import com.github.dockerjava.api.async.ResultCallback; +import com.github.dockerjava.api.command.AttachContainerCmd; +import com.github.dockerjava.api.model.Frame; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.core.async.FrameStreamProcessor; +import com.github.dockerjava.jaxrs.async.AbstractCallbackNotifier; +import com.github.dockerjava.jaxrs.async.POSTCallbackNotifier; + +import org.asynchttpclient.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.ws.rs.client.WebTarget; + +public class AttachContainerCmdExec implements AttachContainerCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(AttachContainerCmdExec.class); + + private URL baseResource; + + private DockerClientConfig dockerClientConfig; + + public AttachContainerCmdExec(URL baseResource, DockerClientConfig dockerClientConfig) { + this.baseResource = baseResource; + this.dockerClientConfig = dockerClientConfig; + } + + // @Override + // protected AbstractCallbackNotifier callbackNotifier(AttachContainerCmd command, + // ResultCallback resultCallback) { + // + // WebTarget webTarget = getBaseResource().path("/containers/{id}/attach").resolveTemplate("id", + // command.getContainerId()); + // + // webTarget = booleanQueryParam(webTarget, "logs", command.hasLogsEnabled()); + // webTarget = booleanQueryParam(webTarget, "stdout", command.hasStdoutEnabled()); + // // webTarget = booleanQueryParam(webTarget, "stdin", command.hasStdinEnabled()); + // webTarget = booleanQueryParam(webTarget, "stderr", command.hasStderrEnabled()); + // webTarget = booleanQueryParam(webTarget, "stream", command.hasFollowStreamEnabled()); + // + // LOGGER.trace("POST: {}", webTarget); + // + // return new POSTCallbackNotifier(new FrameStreamProcessor(), resultCallback, webTarget.request(), null); + // } + + @Override + public Void exec(AttachContainerCmd command, ResultCallback resultCallback) { + AsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder().build(); + + AsyncHttpClient c = new DefaultAsyncHttpClient(config); + c.prepareGet("http://www.ning.com/").execute(new AsyncHandler() { + private ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + + @Override + public State onStatusReceived(HttpResponseStatus status) throws Exception { + int statusCode = status.getStatusCode(); + // The Status have been read + // If you don't want to read the headers,body or stop processing the response + if (statusCode >= 500) { + return State.ABORT; + } + + return State.CONTINUE; + } + + @Override + public State onHeadersReceived(HttpResponseHeaders h) throws Exception { + + // The headers have been read + // If you don't want to read the body, or stop processing the response + return State.CONTINUE; + } + + @Override + public State onBodyPartReceived(HttpResponseBodyPart bodyPart) throws Exception { + bytes.write(bodyPart.getBodyPartBytes()); + return State.CONTINUE; + } + + @Override + public String onCompleted() throws Exception { + // Will be invoked once the response has been fully read or a ResponseComplete exception + // has been thrown. + // NOTE: should probably use Content-Encoding from headers + return bytes.toString("UTF-8"); + } + + @Override + public void onThrowable(Throwable t) { + } + }); + + return null; + } +} From bc05ff913b6826a39fbe32eec4ca5a6a58e5f6f8 Mon Sep 17 00:00:00 2001 From: marcuslinke Date: Fri, 13 Nov 2015 21:53:14 +0100 Subject: [PATCH 02/13] start netty implementation (proof of concept) --- pom.xml | 29 +- .../ahc/AttachContainerCmdExec.java | 101 ---- .../api/command/ExecCreateCmdResponse.java | 8 + .../dockerjava/core/command/FrameReader.java | 9 +- .../dockerjava/netty/AbstrDockerCmdExec.java | 70 +++ .../netty/AbstrSyncDockerCmdExec.java | 33 ++ .../dockerjava/netty/ChannelProvider.java | 7 + .../netty/DockerCmdExecFactoryImpl.java | 484 ++++++++++++++++++ .../dockerjava/netty/ExecCreateCmdExec.java | 30 ++ .../dockerjava/netty/ExecStartCmdExec.java | 29 ++ .../github/dockerjava/netty/InfoCmdExec.java | 42 ++ .../dockerjava/netty/InvocationBuilder.java | 313 +++++++++++ .../github/dockerjava/netty/MediaType.java | 16 + .../github/dockerjava/netty/WebTarget.java | 69 +++ .../handler/AwaitObjectInboundHandler.java | 39 ++ .../DeserializeJsonInboundHandler.java | 31 ++ .../netty/handler/DockerRawStreamHandler.java | 124 +++++ .../handler/HijackHttpConnectionHandler.java | 57 +++ .../netty/handler/HttpRequestProvider.java | 9 + .../handler/HttpResponseInboundHandler.java | 128 +++++ .../HttpResponseStreamInboundHandler.java | 93 ++++ .../handler/SerializeJsonOutboundHandler.java | 19 + 22 files changed, 1635 insertions(+), 105 deletions(-) delete mode 100644 src/main/java/com/github/dockerjava/ahc/AttachContainerCmdExec.java create mode 100644 src/main/java/com/github/dockerjava/netty/AbstrDockerCmdExec.java create mode 100644 src/main/java/com/github/dockerjava/netty/AbstrSyncDockerCmdExec.java create mode 100644 src/main/java/com/github/dockerjava/netty/ChannelProvider.java create mode 100644 src/main/java/com/github/dockerjava/netty/DockerCmdExecFactoryImpl.java create mode 100644 src/main/java/com/github/dockerjava/netty/ExecCreateCmdExec.java create mode 100644 src/main/java/com/github/dockerjava/netty/ExecStartCmdExec.java create mode 100644 src/main/java/com/github/dockerjava/netty/InfoCmdExec.java create mode 100644 src/main/java/com/github/dockerjava/netty/InvocationBuilder.java create mode 100644 src/main/java/com/github/dockerjava/netty/MediaType.java create mode 100644 src/main/java/com/github/dockerjava/netty/WebTarget.java create mode 100644 src/main/java/com/github/dockerjava/netty/handler/AwaitObjectInboundHandler.java create mode 100644 src/main/java/com/github/dockerjava/netty/handler/DeserializeJsonInboundHandler.java create mode 100644 src/main/java/com/github/dockerjava/netty/handler/DockerRawStreamHandler.java create mode 100644 src/main/java/com/github/dockerjava/netty/handler/HijackHttpConnectionHandler.java create mode 100644 src/main/java/com/github/dockerjava/netty/handler/HttpRequestProvider.java create mode 100644 src/main/java/com/github/dockerjava/netty/handler/HttpResponseInboundHandler.java create mode 100644 src/main/java/com/github/dockerjava/netty/handler/HttpResponseStreamInboundHandler.java create mode 100644 src/main/java/com/github/dockerjava/netty/handler/SerializeJsonOutboundHandler.java diff --git a/pom.xml b/pom.xml index 60fac661f..27326fd6b 100644 --- a/pom.xml +++ b/pom.xml @@ -204,9 +204,25 @@ - org.asynchttpclient - async-http-client - 2.0.0-alpha22 + io.netty + netty-codec-http + 4.1.0.Beta7 + + + io.netty + netty-handler + 4.1.0.Beta7 + + + io.netty + netty-handler-proxy + 4.1.0.Beta7 + + + io.netty + netty-transport-native-epoll + 4.1.0.Beta7 + linux-x86_64 @@ -222,6 +238,13 @@ + + + kr.motd.maven + os-maven-plugin + 1.2.3.Final + + diff --git a/src/main/java/com/github/dockerjava/ahc/AttachContainerCmdExec.java b/src/main/java/com/github/dockerjava/ahc/AttachContainerCmdExec.java deleted file mode 100644 index 99be7d507..000000000 --- a/src/main/java/com/github/dockerjava/ahc/AttachContainerCmdExec.java +++ /dev/null @@ -1,101 +0,0 @@ -package com.github.dockerjava.ahc; - -import java.io.ByteArrayOutputStream; -import java.net.URL; -import java.util.concurrent.Future; - -import com.github.dockerjava.api.async.ResultCallback; -import com.github.dockerjava.api.command.AttachContainerCmd; -import com.github.dockerjava.api.model.Frame; -import com.github.dockerjava.core.DockerClientConfig; -import com.github.dockerjava.core.async.FrameStreamProcessor; -import com.github.dockerjava.jaxrs.async.AbstractCallbackNotifier; -import com.github.dockerjava.jaxrs.async.POSTCallbackNotifier; - -import org.asynchttpclient.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.ws.rs.client.WebTarget; - -public class AttachContainerCmdExec implements AttachContainerCmd.Exec { - - private static final Logger LOGGER = LoggerFactory.getLogger(AttachContainerCmdExec.class); - - private URL baseResource; - - private DockerClientConfig dockerClientConfig; - - public AttachContainerCmdExec(URL baseResource, DockerClientConfig dockerClientConfig) { - this.baseResource = baseResource; - this.dockerClientConfig = dockerClientConfig; - } - - // @Override - // protected AbstractCallbackNotifier callbackNotifier(AttachContainerCmd command, - // ResultCallback resultCallback) { - // - // WebTarget webTarget = getBaseResource().path("/containers/{id}/attach").resolveTemplate("id", - // command.getContainerId()); - // - // webTarget = booleanQueryParam(webTarget, "logs", command.hasLogsEnabled()); - // webTarget = booleanQueryParam(webTarget, "stdout", command.hasStdoutEnabled()); - // // webTarget = booleanQueryParam(webTarget, "stdin", command.hasStdinEnabled()); - // webTarget = booleanQueryParam(webTarget, "stderr", command.hasStderrEnabled()); - // webTarget = booleanQueryParam(webTarget, "stream", command.hasFollowStreamEnabled()); - // - // LOGGER.trace("POST: {}", webTarget); - // - // return new POSTCallbackNotifier(new FrameStreamProcessor(), resultCallback, webTarget.request(), null); - // } - - @Override - public Void exec(AttachContainerCmd command, ResultCallback resultCallback) { - AsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder().build(); - - AsyncHttpClient c = new DefaultAsyncHttpClient(config); - c.prepareGet("http://www.ning.com/").execute(new AsyncHandler() { - private ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - - @Override - public State onStatusReceived(HttpResponseStatus status) throws Exception { - int statusCode = status.getStatusCode(); - // The Status have been read - // If you don't want to read the headers,body or stop processing the response - if (statusCode >= 500) { - return State.ABORT; - } - - return State.CONTINUE; - } - - @Override - public State onHeadersReceived(HttpResponseHeaders h) throws Exception { - - // The headers have been read - // If you don't want to read the body, or stop processing the response - return State.CONTINUE; - } - - @Override - public State onBodyPartReceived(HttpResponseBodyPart bodyPart) throws Exception { - bytes.write(bodyPart.getBodyPartBytes()); - return State.CONTINUE; - } - - @Override - public String onCompleted() throws Exception { - // Will be invoked once the response has been fully read or a ResponseComplete exception - // has been thrown. - // NOTE: should probably use Content-Encoding from headers - return bytes.toString("UTF-8"); - } - - @Override - public void onThrowable(Throwable t) { - } - }); - - return null; - } -} diff --git a/src/main/java/com/github/dockerjava/api/command/ExecCreateCmdResponse.java b/src/main/java/com/github/dockerjava/api/command/ExecCreateCmdResponse.java index 391625cfa..08751f3e2 100644 --- a/src/main/java/com/github/dockerjava/api/command/ExecCreateCmdResponse.java +++ b/src/main/java/com/github/dockerjava/api/command/ExecCreateCmdResponse.java @@ -1,5 +1,8 @@ package com.github.dockerjava.api.command; +import org.apache.commons.lang.builder.ReflectionToStringBuilder; +import org.apache.commons.lang.builder.ToStringStyle; + import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; @@ -12,4 +15,9 @@ public class ExecCreateCmdResponse { public String getId() { return id; } + + @Override + public String toString() { + return ReflectionToStringBuilder.toString(this, ToStringStyle.SHORT_PREFIX_STYLE); + } } diff --git a/src/main/java/com/github/dockerjava/core/command/FrameReader.java b/src/main/java/com/github/dockerjava/core/command/FrameReader.java index 78db8359f..e7e4a4031 100644 --- a/src/main/java/com/github/dockerjava/core/command/FrameReader.java +++ b/src/main/java/com/github/dockerjava/core/command/FrameReader.java @@ -4,6 +4,8 @@ import java.io.InputStream; import java.util.Arrays; +import org.apache.commons.io.HexDump; + import com.github.dockerjava.api.model.Frame; import com.github.dockerjava.api.model.StreamType; @@ -16,11 +18,12 @@ public class FrameReader implements AutoCloseable { private static final int HEADER_SIZE = 8; + private final byte[] rawBuffer = new byte[1000]; + private final InputStream inputStream; private Boolean rawStreamDetected = false; - private final byte[] rawBuffer = new byte[1000]; public FrameReader(InputStream inputStream) { this.inputStream = inputStream; @@ -65,6 +68,8 @@ public Frame readFrame() throws IOException { actualHeaderSize += headerCount; } while (actualHeaderSize < HEADER_SIZE); + // HexDump.dump(header, 0, System.err, 0); + StreamType streamType = streamType(header[0]); if (streamType.equals(StreamType.RAW)) { @@ -74,6 +79,8 @@ public Frame readFrame() throws IOException { int payloadSize = ((header[4] & 0xff) << 24) + ((header[5] & 0xff) << 16) + ((header[6] & 0xff) << 8) + (header[7] & 0xff); + + //System.out.println("payload size: " + payloadSize); byte[] payload = new byte[payloadSize]; int actualPayloadSize = 0; diff --git a/src/main/java/com/github/dockerjava/netty/AbstrDockerCmdExec.java b/src/main/java/com/github/dockerjava/netty/AbstrDockerCmdExec.java new file mode 100644 index 000000000..3d0308d57 --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/AbstrDockerCmdExec.java @@ -0,0 +1,70 @@ +package com.github.dockerjava.netty; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.dockerjava.api.model.AuthConfig; +import com.github.dockerjava.api.model.AuthConfigurations; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.core.RemoteApiVersion; +import org.apache.commons.codec.binary.Base64; + +import java.io.IOException; + +import static com.google.common.base.Preconditions.checkNotNull; + +public abstract class AbstrDockerCmdExec { + + private final DockerClientConfig dockerClientConfig; + + private final WebTarget baseResource; + + public AbstrDockerCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + checkNotNull(baseResource, "baseResource was not specified"); + checkNotNull(dockerClientConfig, "dockerClientConfig was not specified"); + this.baseResource = baseResource; + this.dockerClientConfig = dockerClientConfig; + } + + protected WebTarget getBaseResource() { + return baseResource; + } + + protected AuthConfigurations getBuildAuthConfigs() { + return dockerClientConfig.getAuthConfigurations(); + } + + protected String registryAuth(AuthConfig authConfig) { + try { + return Base64.encodeBase64String(new ObjectMapper().writeValueAsString(authConfig).getBytes()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + protected String registryConfigs(AuthConfigurations authConfigs) { + try { + final String json; + if (dockerClientConfig.getVersion().isGreaterOrEqual(RemoteApiVersion.VERSION_1_19)) { + json = new ObjectMapper().writeValueAsString(authConfigs.getConfigs()); + } else { + json = new ObjectMapper().writeValueAsString(authConfigs); + } + + return Base64.encodeBase64String(json.getBytes()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + protected boolean bool(Boolean bool) { + return bool != null && bool; + } + + protected WebTarget booleanQueryParam(WebTarget webTarget, String name, Boolean value) { + if (bool(value)) { + webTarget = webTarget.queryParam(name, bool(value) + ""); + } + + return webTarget; + } + +} diff --git a/src/main/java/com/github/dockerjava/netty/AbstrSyncDockerCmdExec.java b/src/main/java/com/github/dockerjava/netty/AbstrSyncDockerCmdExec.java new file mode 100644 index 000000000..43854dbd1 --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/AbstrSyncDockerCmdExec.java @@ -0,0 +1,33 @@ +package com.github.dockerjava.netty; + +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.command.DockerCmd; +import com.github.dockerjava.api.command.DockerCmdSyncExec; +import com.github.dockerjava.core.DockerClientConfig; + + +public abstract class AbstrSyncDockerCmdExec, RES_T> extends AbstrDockerCmdExec + implements DockerCmdSyncExec { + + public AbstrSyncDockerCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + public RES_T exec(CMD_T command) { + // this hack works because of ResponseStatusExceptionFilter + try (CMD_T cmd = command) { + try { + return execute(cmd); + } catch (RuntimeException e) { + if (e.getCause() instanceof DockerException) { + throw (DockerException) e.getCause(); + } else { + throw e; + } + } + } + } + + protected abstract RES_T execute(CMD_T command); +} diff --git a/src/main/java/com/github/dockerjava/netty/ChannelProvider.java b/src/main/java/com/github/dockerjava/netty/ChannelProvider.java new file mode 100644 index 000000000..a868f1cd1 --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/ChannelProvider.java @@ -0,0 +1,7 @@ +package com.github.dockerjava.netty; + +import io.netty.channel.Channel; + +public interface ChannelProvider { + Channel getChannel(); +} diff --git a/src/main/java/com/github/dockerjava/netty/DockerCmdExecFactoryImpl.java b/src/main/java/com/github/dockerjava/netty/DockerCmdExecFactoryImpl.java new file mode 100644 index 000000000..3627038cd --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/DockerCmdExecFactoryImpl.java @@ -0,0 +1,484 @@ +package com.github.dockerjava.netty; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.IOException; +import java.io.InputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.net.UnknownHostException; + +import javax.net.ssl.SSLException; + +import org.apache.commons.io.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.command.AttachContainerCmd; +import com.github.dockerjava.api.command.AuthCmd; +import com.github.dockerjava.api.command.BuildImageCmd; +import com.github.dockerjava.api.command.CommitCmd; +import com.github.dockerjava.api.command.ContainerDiffCmd; +import com.github.dockerjava.api.command.CopyFileFromContainerCmd; +import com.github.dockerjava.api.command.CreateContainerCmd; +import com.github.dockerjava.api.command.CreateImageCmd; +import com.github.dockerjava.api.command.DockerCmdExecFactory; +import com.github.dockerjava.api.command.EventsCmd; +import com.github.dockerjava.api.command.ExecCreateCmd; +import com.github.dockerjava.api.command.ExecCreateCmdResponse; +import com.github.dockerjava.api.command.ExecStartCmd; +import com.github.dockerjava.api.command.InfoCmd; +import com.github.dockerjava.api.command.InspectContainerCmd; +import com.github.dockerjava.api.command.InspectExecCmd; +import com.github.dockerjava.api.command.InspectImageCmd; +import com.github.dockerjava.api.command.KillContainerCmd; +import com.github.dockerjava.api.command.ListContainersCmd; +import com.github.dockerjava.api.command.ListImagesCmd; +import com.github.dockerjava.api.command.LogContainerCmd; +import com.github.dockerjava.api.command.PauseContainerCmd; +import com.github.dockerjava.api.command.PingCmd; +import com.github.dockerjava.api.command.PullImageCmd; +import com.github.dockerjava.api.command.PushImageCmd; +import com.github.dockerjava.api.command.RemoveContainerCmd; +import com.github.dockerjava.api.command.RemoveImageCmd; +import com.github.dockerjava.api.command.RestartContainerCmd; +import com.github.dockerjava.api.command.SaveImageCmd; +import com.github.dockerjava.api.command.SearchImagesCmd; +import com.github.dockerjava.api.command.StartContainerCmd; +import com.github.dockerjava.api.command.StatsCmd; +import com.github.dockerjava.api.command.StopContainerCmd; +import com.github.dockerjava.api.command.TagImageCmd; +import com.github.dockerjava.api.command.TopContainerCmd; +import com.github.dockerjava.api.command.UnpauseContainerCmd; +import com.github.dockerjava.api.command.VersionCmd; +import com.github.dockerjava.api.command.WaitContainerCmd; +import com.github.dockerjava.api.model.Frame; +import com.github.dockerjava.api.model.Info; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.core.DockerClientConfig.DockerClientConfigBuilder; +import com.github.dockerjava.core.command.ExecCreateCmdImpl; +import com.github.dockerjava.core.command.ExecStartCmdImpl; +import com.github.dockerjava.core.command.FrameReader; +import com.github.dockerjava.core.command.InfoCmdImpl; +import com.github.dockerjava.netty.handler.HijackHttpConnectionHandler; +import com.github.dockerjava.netty.handler.HttpResponseInboundHandler; + +import io.netty.bootstrap.Bootstrap; +import io.netty.buffer.ByteBuf; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.channel.epoll.EpollDomainSocketChannel; +import io.netty.channel.epoll.EpollEventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.channel.unix.DomainSocketAddress; +import io.netty.channel.unix.UnixChannel; +import io.netty.handler.codec.http.HttpClientCodec; +import io.netty.handler.codec.http.HttpContent; +import io.netty.handler.codec.http.HttpObject; +import io.netty.handler.codec.http.HttpResponse; +import io.netty.handler.codec.http.LastHttpContent; +import io.netty.handler.codec.json.JsonObjectDecoder; +import io.netty.handler.logging.LogLevel; +import io.netty.handler.logging.LoggingHandler; +import io.netty.handler.proxy.HttpProxyHandler; +import io.netty.handler.ssl.SslContext; +import io.netty.handler.ssl.SslContextBuilder; +import io.netty.handler.ssl.util.InsecureTrustManagerFactory; + +/** + * http://stackoverflow.com/questions/33296749/netty-connect-to-unix-domain- + * socket-failed http://netty.io/wiki/native-transports.html + * https://github.com/netty/netty/blob/master/example/src/main/java/io/netty/ + * example/http/snoop/HttpSnoopClient.java + * + * @author marcus + * + */ +public class DockerCmdExecFactoryImpl implements DockerCmdExecFactory { + + private static final Logger LOGGER = LoggerFactory.getLogger(DockerCmdExecFactoryImpl.class.getName()); + + private DockerClientConfig dockerClientConfig; + + private Bootstrap bootstrap; + + private EventLoopGroup eventLoopGroup; + + private ChannelProvider channelProvider = new ChannelProvider() { + @Override + public Channel getChannel() { + return connect(); + } + }; + + @Override + public void init(DockerClientConfig dockerClientConfig) { + checkNotNull(dockerClientConfig, "config was not specified"); + this.dockerClientConfig = dockerClientConfig; + + bootstrap = new Bootstrap(); + + String scheme = dockerClientConfig.getUri().getScheme(); + + if ("unix".equals(scheme)) { + eventLoopGroup = new EpollEventLoopGroup(); + bootstrap.group(eventLoopGroup).channel(EpollDomainSocketChannel.class) + .handler(new ChannelInitializer() { + @Override + protected void initChannel(final UnixChannel channel) throws Exception { + channel.pipeline().addLast(new HttpClientCodec()); + } + }); + + } else if ("http".equals(scheme)) { + eventLoopGroup = new NioEventLoopGroup(); + + try { + final SslContext sslCtx = SslContextBuilder.forClient() + .trustManager(InsecureTrustManagerFactory.INSTANCE).build(); + + InetAddress addr = InetAddress.getLoopbackAddress(); + + final SocketAddress proxyAddress = new InetSocketAddress(addr, 8008); + + bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class) + .handler(new ChannelInitializer() { + @Override + protected void initChannel(final SocketChannel channel) throws Exception { + // channel.pipeline().addLast(new + // HttpProxyHandler(proxyAddress)); + + // channel.pipeline().addLast(sslCtx.newHandler(channel.alloc())); + HttpClientCodec httpClientCodec = new HttpClientCodec(); + + channel.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG)); + channel.pipeline().addLast(httpClientCodec); + + } + }); + + } catch (SSLException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + + } + + } + + private Channel connect() { + try { + return connect(bootstrap); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + private Channel connect(final Bootstrap bootstrap) throws InterruptedException { + + String scheme = dockerClientConfig.getUri().getScheme(); + + if ("unix".equals(scheme)) { + return bootstrap.connect(new DomainSocketAddress("/var/run/docker.sock")).sync().channel(); + } else if ("http".equals(scheme)) { + String host = dockerClientConfig.getUri().getHost(); + int port = dockerClientConfig.getUri().getPort(); + + if (port == -1) { + throw new RuntimeException("no port configured for " + host); + } + + return bootstrap.connect(host, port).sync().channel(); + } else { + throw new RuntimeException("unsupported protocol scheme: " + scheme); + } + + } + + protected DockerClientConfig getDockerClientConfig() { + checkNotNull(dockerClientConfig, + "Factor not initialized, dockerClientConfig not set. You probably forgot to call init()!"); + return dockerClientConfig; + } + + @Override + public AuthCmd.Exec createAuthCmdExec() { + return null; // new AuthCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public InfoCmd.Exec createInfoCmdExec() { + return new InfoCmdExec(new WebTarget(channelProvider), getDockerClientConfig()); + } + + @Override + public PingCmd.Exec createPingCmdExec() { + return null; // new PingCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public VersionCmd.Exec createVersionCmdExec() { + return null; // new VersionCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public PullImageCmd.Exec createPullImageCmdExec() { + return null; // new PullImageCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public PushImageCmd.Exec createPushImageCmdExec() { + return null; // new PushImageCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public SaveImageCmd.Exec createSaveImageCmdExec() { + return null; // new SaveImageCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public CreateImageCmd.Exec createCreateImageCmdExec() { + return null; // new CreateImageCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public SearchImagesCmd.Exec createSearchImagesCmdExec() { + return null; // new SearchImagesCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public RemoveImageCmd.Exec createRemoveImageCmdExec() { + return null; // new RemoveImageCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public ListImagesCmd.Exec createListImagesCmdExec() { + return null; // new ListImagesCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public InspectImageCmd.Exec createInspectImageCmdExec() { + return null; // new InspectImageCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public ListContainersCmd.Exec createListContainersCmdExec() { + return null; // new ListContainersCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public CreateContainerCmd.Exec createCreateContainerCmdExec() { + return null; // new CreateContainerCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public StartContainerCmd.Exec createStartContainerCmdExec() { + return null; // new StartContainerCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public InspectContainerCmd.Exec createInspectContainerCmdExec() { + return null; // new InspectContainerCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public ExecCreateCmd.Exec createExecCmdExec() { + return new ExecCreateCmdExec(new WebTarget(channelProvider), getDockerClientConfig()); + } + + @Override + public RemoveContainerCmd.Exec createRemoveContainerCmdExec() { + return null; // new RemoveContainerCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public WaitContainerCmd.Exec createWaitContainerCmdExec() { + return null; // new WaitContainerCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public AttachContainerCmd.Exec createAttachContainerCmdExec() { + return null; // new AttachContainerCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public ExecStartCmd.Exec createExecStartCmdExec() { + + return new ExecStartCmdExec(new WebTarget(channelProvider), getDockerClientConfig()); + } + + @Override + public InspectExecCmd.Exec createInspectExecCmdExec() { + return null; // new InspectExecCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public LogContainerCmd.Exec createLogContainerCmdExec() { + return null; // new LogContainerCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public CopyFileFromContainerCmd.Exec createCopyFileFromContainerCmdExec() { + return null; // new CopyFileFromContainerCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public StopContainerCmd.Exec createStopContainerCmdExec() { + return null; // new StopContainerCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public ContainerDiffCmd.Exec createContainerDiffCmdExec() { + return null; // new ContainerDiffCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public KillContainerCmd.Exec createKillContainerCmdExec() { + return null; // new KillContainerCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public RestartContainerCmd.Exec createRestartContainerCmdExec() { + return null; // new RestartContainerCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public CommitCmd.Exec createCommitCmdExec() { + return null; // new CommitCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public BuildImageCmd.Exec createBuildImageCmdExec() { + return null; // new BuildImageCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public TopContainerCmd.Exec createTopContainerCmdExec() { + return null; // new TopContainerCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public TagImageCmd.Exec createTagImageCmdExec() { + return null; // new TagImageCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public PauseContainerCmd.Exec createPauseContainerCmdExec() { + return null; // new PauseContainerCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public UnpauseContainerCmd.Exec createUnpauseContainerCmdExec() { + return null; // new UnpauseContainerCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public EventsCmd.Exec createEventsCmdExec() { + return null; // new EventsCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public StatsCmd.Exec createStatsCmdExec() { + return null; // new StatsCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public void close() throws IOException { + // checkNotNull(client, "Factory not initialized. You probably forgot to + // call init()!"); + // client.close(); + + eventLoopGroup.shutdownGracefully(); + } + + public static void main(String[] args) throws IOException { + DockerCmdExecFactory execFactory = new DockerCmdExecFactoryImpl(); + // execFactory.init(new + // DockerClientConfigBuilder().withUri("unix:///var/run/docker.sock").build()); + execFactory.init(new DockerClientConfigBuilder().withUri("http://localhost:2375").build()); + + // InfoCmd.Exec exec = execFactory.createInfoCmdExec(); + // + // InfoCmd infoCmd = new InfoCmdImpl(exec); + // + // Info info = infoCmd.exec(); + // + // System.out.println("result: " + info); + // + // infoCmd.close(); + + ExecCreateCmd.Exec execCreate = execFactory.createExecCmdExec(); + + ExecCreateCmd execCreateCmd = new ExecCreateCmdImpl(execCreate, "test").withCmd("/bin/bash") + .withAttachStdout(true).withAttachStderr(true).withAttachStdin(true); + + ExecCreateCmdResponse execCreateCmdResponse = execCreate.exec(execCreateCmd); + + System.out.println("result: " + execCreateCmdResponse); + + ExecStartCmd.Exec execStart = execFactory.createExecStartCmdExec(); + + ExecStartCmd execStartCmd = new ExecStartCmdImpl(execStart, execCreateCmdResponse.getId()).withDetach(false) + .withTty(true); + + InputStream response = execStart.exec(execStartCmd); + +// FrameReader frameReader = new FrameReader(response); +// +// System.out.println("read frame"); +// Frame frame = frameReader.readFrame(); +// +// while(frame != null) { +// System.out.println("frame: " + frame); +// frame = frameReader.readFrame(); +// } +// System.out.println("all frames read"); +// +// System.out.flush(); + + // System.out.println("response: " + IOUtils.toString(response, + // "UTF-8")); + + //execFactory.close(); + } + +} diff --git a/src/main/java/com/github/dockerjava/netty/ExecCreateCmdExec.java b/src/main/java/com/github/dockerjava/netty/ExecCreateCmdExec.java new file mode 100644 index 000000000..10e5ad9d8 --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/ExecCreateCmdExec.java @@ -0,0 +1,30 @@ +package com.github.dockerjava.netty; + +import com.github.dockerjava.api.command.ExecCreateCmd; +import com.github.dockerjava.api.command.ExecCreateCmdResponse; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.netty.AbstrSyncDockerCmdExec; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ExecCreateCmdExec extends AbstrSyncDockerCmdExec + implements ExecCreateCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(ExecCreateCmdExec.class); + + public ExecCreateCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + protected ExecCreateCmdResponse execute(ExecCreateCmd command) { + WebTarget webResource = getBaseResource().path("/containers/{id}/exec").resolveTemplate("id", + command.getContainerId()); + + LOGGER.trace("POST: {}", webResource); + + return webResource.request().accept(MediaType.APPLICATION_JSON) + .post(command, ExecCreateCmdResponse.class); + } +} diff --git a/src/main/java/com/github/dockerjava/netty/ExecStartCmdExec.java b/src/main/java/com/github/dockerjava/netty/ExecStartCmdExec.java new file mode 100644 index 000000000..b158bf4e2 --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/ExecStartCmdExec.java @@ -0,0 +1,29 @@ +package com.github.dockerjava.netty; + +import java.io.InputStream; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.command.ExecStartCmd; +import com.github.dockerjava.core.DockerClientConfig; + +public class ExecStartCmdExec extends AbstrSyncDockerCmdExec implements ExecStartCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(ExecStartCmdExec.class); + + public ExecStartCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + protected InputStream execute(ExecStartCmd command) { + WebTarget webResource = getBaseResource().path("/exec/{id}/start").resolveTemplate("id", command.getExecId()); + + LOGGER.trace("POST: {}", webResource); + + webResource.request().accept(MediaType.APPLICATION_JSON).post(command, System.out, null, System.in); + + return null; + } +} diff --git a/src/main/java/com/github/dockerjava/netty/InfoCmdExec.java b/src/main/java/com/github/dockerjava/netty/InfoCmdExec.java new file mode 100644 index 000000000..04abf103f --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/InfoCmdExec.java @@ -0,0 +1,42 @@ +package com.github.dockerjava.netty; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.command.InfoCmd; +import com.github.dockerjava.api.model.Info; +import com.github.dockerjava.core.DockerClientConfig; + +/** + * http://stackoverflow.com/questions/33296749/netty-connect-to-unix-domain- + * socket-failed http://netty.io/wiki/native-transports.html + * https://github.com/netty/netty/blob/master/example/src/main/java/io/netty/ + * example/http/snoop/HttpSnoopClient.java + * + * @author marcus + * + */ +public class InfoCmdExec implements InfoCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(InfoCmdExec.class); + + private WebTarget webResource; + + private DockerClientConfig dockerClientConfig; + + public InfoCmdExec(WebTarget webResource, DockerClientConfig dockerClientConfig) { + this.webResource = webResource; + this.dockerClientConfig = dockerClientConfig; + } + + + @Override + public Info exec(InfoCmd command) { + return webResource.path("info").request().get(Info.class); + } + + + + + +} diff --git a/src/main/java/com/github/dockerjava/netty/InvocationBuilder.java b/src/main/java/com/github/dockerjava/netty/InvocationBuilder.java new file mode 100644 index 000000000..a257f8837 --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/InvocationBuilder.java @@ -0,0 +1,313 @@ +package com.github.dockerjava.netty; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.io.HexDump; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.dockerjava.netty.handler.AwaitObjectInboundHandler; +import com.github.dockerjava.netty.handler.DeserializeJsonInboundHandler; +import com.github.dockerjava.netty.handler.DockerRawStreamHandler; +import com.github.dockerjava.netty.handler.HijackHttpConnectionHandler; +import com.github.dockerjava.netty.handler.HttpRequestProvider; +import com.github.dockerjava.netty.handler.HttpResponseInboundHandler; +import com.github.dockerjava.netty.handler.HttpResponseStreamInboundHandler; +import com.github.dockerjava.netty.handler.SerializeJsonOutboundHandler; + +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelHandler; +import io.netty.handler.codec.http.DefaultFullHttpRequest; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.HttpClientCodec; +import io.netty.handler.codec.http.HttpClientUpgradeHandler; +import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.handler.codec.http.HttpHeaderValues; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpVersion; +import io.netty.handler.codec.http.multipart.HttpPostRequestEncoder; +import io.netty.handler.codec.http.multipart.InterfaceHttpData; +import io.netty.handler.codec.json.JsonObjectDecoder; + +public class InvocationBuilder { + + private ChannelProvider channelProvider; + private String resource; + private Map headers = new HashMap(); + + public InvocationBuilder(ChannelProvider channelProvider, String resource) { + this.channelProvider = channelProvider; + this.resource = resource; + } + + public InvocationBuilder accept(MediaType mediaType) { + headers.put(HttpHeaderNames.ACCEPT.toString(), mediaType.getMediaType()); + return this; + } + + public T get(Class resultType) { + FullHttpRequest request = prepareGetRequest(resource); + + Channel channel = channelProvider.getChannel(); + + AwaitObjectInboundHandler awaitObject = new AwaitObjectInboundHandler(resultType); + + channel.pipeline().addLast(new HttpResponseInboundHandler(new HttpRequestProvider() { + @Override + public HttpRequest getHttpRequest(String uri) { + return prepareGetRequest(uri); + } + })); + channel.pipeline().addLast(new JsonObjectDecoder()); + channel.pipeline().addLast(new DeserializeJsonInboundHandler(resultType)); + channel.pipeline().addLast(awaitObject); + + System.out.println("resource: " + resource); + + channel.writeAndFlush(request); + + // Wait for the server to close the connection. + try { + // channel.closeFuture().sync(); + T response = awaitObject.await(); + + // channel.disconnect(); + + return response; + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + private FullHttpRequest prepareGetRequest(String uri) { + // Prepare the HTTP request. + FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri); + + setDefaultHeaders(request); + return request; + } + + private void setDefaultHeaders(HttpRequest request) { + request.headers().set(HttpHeaderNames.HOST, ""); + request.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE); + request.headers().set(HttpHeaderNames.ACCEPT_ENCODING, HttpHeaderValues.GZIP); + + for (Map.Entry entry : headers.entrySet()) { + request.headers().set((CharSequence) entry.getKey(), entry.getValue()); + } + } + + public T post(final Object entity, Class resultType) { + + HttpRequest request = preparePostRequest(resource, entity); + + AwaitObjectInboundHandler awaitObject = new AwaitObjectInboundHandler(resultType); + + Channel channel = channelProvider.getChannel(); + + channel.pipeline().addLast(new HttpResponseInboundHandler(new HttpRequestProvider() { + @Override + public HttpRequest getHttpRequest(String uri) { + return preparePostRequest(uri, entity); + } + })); + channel.pipeline().addLast(new JsonObjectDecoder()); + channel.pipeline().addLast(new DeserializeJsonInboundHandler(resultType)); + channel.pipeline().addLast(awaitObject); + + System.out.println("resource: " + resource); + System.out.println(channel.isActive()); + + channel.writeAndFlush(request); + + // Wait for the server to close the connection. + try { + // channel.closeFuture().sync(); + T response = awaitObject.await(); + + return response; + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + } + + public InputStream post(final Object entity, final InputStream stdin) { + + HttpRequest request = preparePostRequest(resource, entity); + + HttpResponseStreamInboundHandler streamHandler = new HttpResponseStreamInboundHandler(); + + final Channel channel = channelProvider.getChannel(); + + HijackHttpConnectionHandler hijackHandler = new HijackHttpConnectionHandler(); + channel.pipeline() + .addLast(new HttpClientUpgradeHandler(new HttpClientCodec(), hijackHandler, Integer.MAX_VALUE)); + channel.pipeline().addLast(new HttpResponseInboundHandler(new HttpRequestProvider() { + @Override + public HttpRequest getHttpRequest(String uri) { + return preparePostRequest(uri, entity); + } + })); + + channel.pipeline().addLast(streamHandler); + + System.out.println("resource: " + resource); + System.out.println(channel.isActive()); + + channel.writeAndFlush(request); + + // wait for successful http upgrade procedure + hijackHandler.await(); + +// Iterator> iterator = channel.pipeline().iterator(); +// +// while (iterator.hasNext()) { +// Entry next = iterator.next(); +// System.out.println(next.getValue()); +// } + + // start a new thread that reads from stdin and writes to the channel + new Thread(new Runnable() { + + @Override + public void run() { + + BufferedReader reader = new BufferedReader(new InputStreamReader(stdin)); + + int read = -1; + while((read = read(reader)) != -1) { + byte[] bytes = ByteBuffer.allocate(4).putInt(read).array(); + channel.writeAndFlush(Unpooled.copiedBuffer(bytes)); + } + } + + private int read(BufferedReader reader) { + try { + return reader.read(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }).start(); + + return streamHandler.getInputStream(); + } + + public void post(final Object entity, final OutputStream stdout, final OutputStream stderr, final InputStream stdin) { + + HttpRequest request = preparePostRequest(resource, entity); + + DockerRawStreamHandler streamHandler = new DockerRawStreamHandler(stdout, stderr); + + final Channel channel = channelProvider.getChannel(); + + HijackHttpConnectionHandler hijackHandler = new HijackHttpConnectionHandler(); + channel.pipeline() + .addLast(new HttpClientUpgradeHandler(new HttpClientCodec(), hijackHandler, Integer.MAX_VALUE)); + channel.pipeline().addLast(new HttpResponseInboundHandler(new HttpRequestProvider() { + @Override + public HttpRequest getHttpRequest(String uri) { + return preparePostRequest(uri, entity); + } + })); + + channel.pipeline().addLast(streamHandler); + + System.out.println("resource: " + resource); + System.out.println(channel.isActive()); + + channel.writeAndFlush(request); + + // wait for successful http upgrade procedure + hijackHandler.await(); + +// Iterator> iterator = channel.pipeline().iterator(); +// +// while (iterator.hasNext()) { +// Entry next = iterator.next(); +// System.out.println(next.getValue()); +// } + + // start a new thread that reads from stdin and writes to the channel + new Thread(new Runnable() { + + @Override + public void run() { + + BufferedReader reader = new BufferedReader(new InputStreamReader(stdin, Charset.forName("UTF-8"))); + + int read = -1; + while((read = read(reader)) != -1) { + byte[] bytes = ByteBuffer.allocate(4).putInt(read).array(); + try { + bytes = new String(bytes).getBytes("US-ASCII"); + + HexDump.dump(bytes, 0, System.err, 0); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } catch (ArrayIndexOutOfBoundsException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IllegalArgumentException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + channel.writeAndFlush(Unpooled.copiedBuffer(bytes)); + } + } + + private int read(BufferedReader reader) { + try { + return reader.read(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }).start(); + + + } + + + private HttpRequest preparePostRequest(String uri, Object entity) { + // Prepare the HTTP request. + FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, uri); + + setDefaultHeaders(request); + + byte[] bytes; + try { + bytes = new ObjectMapper().writeValueAsBytes(entity); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + + request.headers().set(HttpHeaderNames.CONTENT_TYPE, "application/json"); + request.content().clear().writeBytes(Unpooled.copiedBuffer(bytes)); + request.headers().set(HttpHeaderNames.CONTENT_LENGTH, bytes.length); + + return request; + } + +} diff --git a/src/main/java/com/github/dockerjava/netty/MediaType.java b/src/main/java/com/github/dockerjava/netty/MediaType.java new file mode 100644 index 000000000..347b9de72 --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/MediaType.java @@ -0,0 +1,16 @@ +package com.github.dockerjava.netty; + +public enum MediaType { + + APPLICATION_JSON("application/json"); + + private String mediaType; + + private MediaType(String mediaType) { + this.mediaType = mediaType; + } + + public String getMediaType() { + return mediaType; + } +} diff --git a/src/main/java/com/github/dockerjava/netty/WebTarget.java b/src/main/java/com/github/dockerjava/netty/WebTarget.java new file mode 100644 index 000000000..7c8970d33 --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/WebTarget.java @@ -0,0 +1,69 @@ +package com.github.dockerjava.netty; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang.StringUtils; + +import io.netty.channel.Channel; + +public class WebTarget { + + private ChannelProvider channelProvider; + + private List path = new ArrayList(); + + private Map queryParams = new HashMap(); + + private static String PATH_SEPARATOR = "/"; + + public WebTarget(ChannelProvider channelProvider) { + this.channelProvider = channelProvider; + } + + public WebTarget path(String... components) { + + for(String component : components) { + + path.addAll(Arrays.asList(StringUtils.split(component, PATH_SEPARATOR))); + } + + return this; + } + + public InvocationBuilder request() { + String resource = PATH_SEPARATOR + StringUtils.join(path, PATH_SEPARATOR); + + List params = new ArrayList(); + for(Map.Entry entry : queryParams.entrySet()) { + params.add(entry.getKey() + "=" + entry.getValue()); + } + + if(!params.isEmpty()) { + resource = resource + "?" + StringUtils.join(params, "&"); + } + + return new InvocationBuilder(channelProvider, resource); + } + + public WebTarget resolveTemplate(String name, Object value) { + List newPath = new ArrayList(); + for(String component : path) { + component = component.replaceAll("\\{" + name + "\\}", value.toString()); + newPath.add(component); + } + path = newPath; + return this; + } + + public WebTarget queryParam(String name, Object value) { + queryParams.put(name, value.toString()); + return this; + } + + + +} diff --git a/src/main/java/com/github/dockerjava/netty/handler/AwaitObjectInboundHandler.java b/src/main/java/com/github/dockerjava/netty/handler/AwaitObjectInboundHandler.java new file mode 100644 index 000000000..806e76d31 --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/handler/AwaitObjectInboundHandler.java @@ -0,0 +1,39 @@ +package com.github.dockerjava.netty.handler; + +import java.util.concurrent.Callable; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.FutureTask; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; + +public class AwaitObjectInboundHandler extends SimpleChannelInboundHandler { + + public AwaitObjectInboundHandler(Class clazz) { + super(clazz); + } + + private CountDownLatch countDownLatch = new CountDownLatch(1); + + private T object; + + @Override + protected void channelRead0(ChannelHandlerContext ctx, T msg) throws Exception { + object = msg; + countDownLatch.countDown(); + } + + public T await() throws InterruptedException { + countDownLatch.await(); + return object; + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + cause.printStackTrace(); + ctx.close(); + } + +} diff --git a/src/main/java/com/github/dockerjava/netty/handler/DeserializeJsonInboundHandler.java b/src/main/java/com/github/dockerjava/netty/handler/DeserializeJsonInboundHandler.java new file mode 100644 index 000000000..d2a23988d --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/handler/DeserializeJsonInboundHandler.java @@ -0,0 +1,31 @@ +package com.github.dockerjava.netty.handler; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; + +public class DeserializeJsonInboundHandler extends SimpleChannelInboundHandler{ + + private Class clazz; + + public DeserializeJsonInboundHandler(Class clazz) { + this.clazz = clazz; + } + + @Override + protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { + byte[] buffer = new byte[msg.readableBytes()]; + msg.readBytes(buffer); + + T object = null; + try { + object = new ObjectMapper().readValue(buffer, clazz); + } catch (Exception e) { + throw new RuntimeException(e); + } + + ctx.fireChannelRead(object); + } +} diff --git a/src/main/java/com/github/dockerjava/netty/handler/DockerRawStreamHandler.java b/src/main/java/com/github/dockerjava/netty/handler/DockerRawStreamHandler.java new file mode 100644 index 000000000..7d2e501f5 --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/handler/DockerRawStreamHandler.java @@ -0,0 +1,124 @@ +package com.github.dockerjava.netty.handler; + +import java.io.OutputStream; + +import com.github.dockerjava.api.model.Frame; +import com.github.dockerjava.api.model.StreamType; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; + +public class DockerRawStreamHandler extends SimpleChannelInboundHandler { + + private static final int HEADER_SIZE = 8; + + private final ByteBuf rawBuffer = Unpooled.buffer(1000); + + private OutputStream stdout, stderr; + + private byte[] header = new byte[HEADER_SIZE]; + + private int headerCnt = 0; + + private int payloadCnt = 0; + + public DockerRawStreamHandler(OutputStream stdout, OutputStream stderr) { + this.stdout = stdout; + this.stderr = stderr; + } + + @Override + protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { + + // System.out.println("msg: " + msg.readableBytes()); + rawBuffer.writeBytes(msg.copy()); + // System.out.println("readableBytes: " + rawBuffer.readableBytes()); + + Frame frame = null; + + do { + if (frame != null) { + switch (frame.getStreamType()) { + case STDOUT: + if (stdout != null) { + stdout.write(frame.getPayload()); + stdout.flush(); + } + break; + case STDERR: + if (stderr != null) { + stderr.write(frame.getPayload()); + stderr.flush(); + } + break; + } + headerCnt = 0; + payloadCnt = 0; + } + } while ((frame = decode()) != null); + + } + + private int read(byte[] buf, int offset, int length) { + length = Math.min(rawBuffer.readableBytes(), length); + rawBuffer.readBytes(buf, offset, length); + return length; + } + + private Frame decode() { + if (headerCnt < HEADER_SIZE) { + + int headerCount = read(header, headerCnt, HEADER_SIZE - headerCnt); + + if (headerCount == 0) { + return null; + } + headerCnt += headerCount; + + if (headerCnt < HEADER_SIZE) + return null; + } + + StreamType streamType = streamType(header[0]); + + int payloadSize = ((header[4] & 0xff) << 24) + ((header[5] & 0xff) << 16) + ((header[6] & 0xff) << 8) + + (header[7] & 0xff); + + byte[] payload = new byte[payloadSize]; + + int count = read(payload, payloadCnt, payloadSize - payloadCnt); + + if (count == 0) + return null; + + payloadCnt += count; + + if (payloadCnt < payloadSize) + return null; + + return new Frame(streamType, payload); + + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + cause.printStackTrace(); + ctx.close(); + } + + private static StreamType streamType(byte streamType) { + switch (streamType) { + case 0: + return StreamType.STDIN; + case 1: + return StreamType.STDOUT; + case 2: + return StreamType.STDERR; + default: + return StreamType.RAW; + } + } + +} diff --git a/src/main/java/com/github/dockerjava/netty/handler/HijackHttpConnectionHandler.java b/src/main/java/com/github/dockerjava/netty/handler/HijackHttpConnectionHandler.java new file mode 100644 index 000000000..1a4a6673b --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/handler/HijackHttpConnectionHandler.java @@ -0,0 +1,57 @@ +package com.github.dockerjava.netty.handler; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.Map.Entry; +import java.util.concurrent.CountDownLatch; + +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.FullHttpResponse; +import io.netty.handler.codec.http.HttpClientCodec; +import io.netty.handler.codec.http.HttpClientUpgradeHandler; +import io.netty.handler.codec.http.HttpRequest; + +public class HijackHttpConnectionHandler implements HttpClientUpgradeHandler.UpgradeCodec { + + private CountDownLatch latch = new CountDownLatch(1); + + @Override + public void upgradeTo(ChannelHandlerContext ctx, FullHttpResponse upgradeResponse) + throws Exception { + System.out.println("UPGRADED"); + latch.countDown(); +// ChannelHandler removed = ctx.pipeline().remove(HttpClientCodec.class); +// +// System.out.println("removed: " + removed); + + + + + + } + + @Override + public Collection setUpgradeHeaders(ChannelHandlerContext ctx, + HttpRequest upgradeRequest) { + return Collections.emptyList(); + } + + @Override + public CharSequence protocol() { + return "tcp"; + } + + public void await() { + try { + latch.await(); + System.out.println("upgrade awaited"); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + + +} diff --git a/src/main/java/com/github/dockerjava/netty/handler/HttpRequestProvider.java b/src/main/java/com/github/dockerjava/netty/handler/HttpRequestProvider.java new file mode 100644 index 000000000..9777a1098 --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/handler/HttpRequestProvider.java @@ -0,0 +1,9 @@ +package com.github.dockerjava.netty.handler; + +import io.netty.handler.codec.http.HttpRequest; + +public interface HttpRequestProvider { + + HttpRequest getHttpRequest(String uri); + +} diff --git a/src/main/java/com/github/dockerjava/netty/handler/HttpResponseInboundHandler.java b/src/main/java/com/github/dockerjava/netty/handler/HttpResponseInboundHandler.java new file mode 100644 index 000000000..143e5dcf5 --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/handler/HttpResponseInboundHandler.java @@ -0,0 +1,128 @@ +package com.github.dockerjava.netty.handler; + +import java.nio.charset.Charset; + +import com.github.dockerjava.api.BadRequestException; +import com.github.dockerjava.api.ConflictException; +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.InternalServerErrorException; +import com.github.dockerjava.api.NotAcceptableException; +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.NotModifiedException; +import com.github.dockerjava.api.UnauthorizedException; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.handler.codec.http.HttpContent; +import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.handler.codec.http.HttpObject; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import io.netty.handler.codec.http.LastHttpContent; + +public class HttpResponseInboundHandler extends SimpleChannelInboundHandler { + + private HttpRequestProvider requestProvider; + + private HttpResponse response; + + private ByteBuf body = Unpooled.buffer(); + + public HttpResponseInboundHandler(HttpRequestProvider requestProvider) { + super(false); + this.requestProvider = requestProvider; + } + + @Override + protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception { + if (msg instanceof HttpResponse) { + HttpResponse response = (HttpResponse) msg; + + System.err.println("STATUS: " + response.status()); + System.err.println("VERSION: " + response.protocolVersion()); + System.err.println(); + + this.response = response; + + if (!response.headers().isEmpty()) { + for (CharSequence name : response.headers().names()) { + for (CharSequence value : response.headers().getAll(name)) { + System.err.println("HEADER: " + name + " = " + value); + } + } + System.err.println(); + } + + } else if (msg instanceof HttpContent) { + //System.err.println("Got http content"); + + HttpContent content = (HttpContent) msg; + + ByteBuf byteBuf = content.content(); + + switch (response.status().code()) { + case 200: + case 201: + case 204: + ctx.fireChannelRead(byteBuf); + break; + default: + body.writeBytes(byteBuf); + } + + if (content instanceof LastHttpContent) { + + try { + switch (response.status().code()) { + case 200: + case 201: + case 204: + + break; + case 301: + case 302: + if (response.headers().contains(HttpHeaderNames.LOCATION)) { + String location = response.headers().get(HttpHeaderNames.LOCATION); + System.out.println("redirected to :" + location); + HttpRequest redirected = requestProvider.getHttpRequest(location); + + ctx.channel().writeAndFlush(redirected); + } else { + ctx.fireExceptionCaught(new RuntimeException("missing 'location' header for redirect")); + } + break; + case 304: + throw new NotModifiedException(getBodyAsMessage(body)); + case 400: + throw new BadRequestException(getBodyAsMessage(body)); + case 401: + throw new UnauthorizedException(getBodyAsMessage(body)); + case 404: + throw new NotFoundException(getBodyAsMessage(body)); + case 406: + throw new NotAcceptableException(getBodyAsMessage(body)); + case 409: + throw new ConflictException(getBodyAsMessage(body)); + case 500: + throw new InternalServerErrorException(getBodyAsMessage(body)); + default: + throw new DockerException(getBodyAsMessage(body), response.status().code()); + } + } finally { + //ctx.close(); + } + + } + } else { + System.err.println("UNKNOWN"); + } + + } + + private String getBodyAsMessage(ByteBuf body) { + return body.readBytes(body.readableBytes()).toString(Charset.forName("UTF-8")); + } + +} diff --git a/src/main/java/com/github/dockerjava/netty/handler/HttpResponseStreamInboundHandler.java b/src/main/java/com/github/dockerjava/netty/handler/HttpResponseStreamInboundHandler.java new file mode 100644 index 000000000..854cb7b2a --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/handler/HttpResponseStreamInboundHandler.java @@ -0,0 +1,93 @@ +package com.github.dockerjava.netty.handler; + +import java.io.IOException; +import java.io.InputStream; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.LinkedTransferQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.apache.commons.io.HexDump; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; + +public class HttpResponseStreamInboundHandler extends SimpleChannelInboundHandler { + + private CountDownLatch latch = new CountDownLatch(1); + private HttpResponseInputStream stream = new HttpResponseInputStream(); + + @Override + protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { + + latch.countDown(); + stream.write(msg.copy()); + + //System.out.println("got data: " + msg.readableBytes()); + + if (msg.readableBytes() == 0) { + stream.close(); + } + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + cause.printStackTrace(); + ctx.close(); + } + + public InputStream getInputStream() { + try { + latch.await(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + return stream; + } + + public static class HttpResponseInputStream extends InputStream { + + private AtomicBoolean closed = new AtomicBoolean(false); + + private LinkedTransferQueue queue = new LinkedTransferQueue(); + + private ByteBuf current = null; + + public void write(ByteBuf byteBuf) { + queue.put(byteBuf); + } + + @Override + public void close() throws IOException { + closed.set(true); + super.close(); + } + + @Override + public int read() throws IOException { + if (closed.get()) + return -1; + + if (current == null || current.readableBytes() == 0) { + try { + current = queue.poll(10, TimeUnit.SECONDS); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + if(current != null && current.readableBytes() > 0) { + return current.readByte(); + } else { + return read(); + } + + + + + } + + } + +} diff --git a/src/main/java/com/github/dockerjava/netty/handler/SerializeJsonOutboundHandler.java b/src/main/java/com/github/dockerjava/netty/handler/SerializeJsonOutboundHandler.java new file mode 100644 index 000000000..4505c02a9 --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/handler/SerializeJsonOutboundHandler.java @@ -0,0 +1,19 @@ +package com.github.dockerjava.netty.handler; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToByteEncoder; + +public class SerializeJsonOutboundHandler extends MessageToByteEncoder{ + + private ObjectMapper mapper = new ObjectMapper(); + + @Override + protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception { + byte[] serialized = mapper.writeValueAsBytes(msg); + System.out.println("serialized: " + new String(serialized)); + out.writeBytes(serialized); + } +} From 4a81097e4875c995b1922484f0eb39c77acd7b31 Mon Sep 17 00:00:00 2001 From: marcuslinke Date: Sat, 14 Nov 2015 16:35:08 +0100 Subject: [PATCH 03/13] Removed some debug output --- .../dockerjava/netty/InvocationBuilder.java | 75 ++++--------------- 1 file changed, 16 insertions(+), 59 deletions(-) diff --git a/src/main/java/com/github/dockerjava/netty/InvocationBuilder.java b/src/main/java/com/github/dockerjava/netty/InvocationBuilder.java index a257f8837..b4f0fb1ee 100644 --- a/src/main/java/com/github/dockerjava/netty/InvocationBuilder.java +++ b/src/main/java/com/github/dockerjava/netty/InvocationBuilder.java @@ -7,15 +7,9 @@ import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; -import java.nio.CharBuffer; import java.nio.charset.Charset; import java.util.HashMap; -import java.util.Iterator; import java.util.Map; -import java.util.Map.Entry; -import java.util.concurrent.TimeUnit; - -import org.apache.commons.io.HexDump; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; @@ -26,12 +20,9 @@ import com.github.dockerjava.netty.handler.HttpRequestProvider; import com.github.dockerjava.netty.handler.HttpResponseInboundHandler; import com.github.dockerjava.netty.handler.HttpResponseStreamInboundHandler; -import com.github.dockerjava.netty.handler.SerializeJsonOutboundHandler; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelHandler; import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.HttpClientCodec; @@ -41,8 +32,6 @@ import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.codec.http.multipart.HttpPostRequestEncoder; -import io.netty.handler.codec.http.multipart.InterfaceHttpData; import io.netty.handler.codec.json.JsonObjectDecoder; public class InvocationBuilder { @@ -155,7 +144,7 @@ public InputStream post(final Object entity, final InputStream stdin) { HttpResponseStreamInboundHandler streamHandler = new HttpResponseStreamInboundHandler(); final Channel channel = channelProvider.getChannel(); - + HijackHttpConnectionHandler hijackHandler = new HijackHttpConnectionHandler(); channel.pipeline() .addLast(new HttpClientUpgradeHandler(new HttpClientCodec(), hijackHandler, Integer.MAX_VALUE)); @@ -168,31 +157,21 @@ public HttpRequest getHttpRequest(String uri) { channel.pipeline().addLast(streamHandler); - System.out.println("resource: " + resource); - System.out.println(channel.isActive()); - channel.writeAndFlush(request); // wait for successful http upgrade procedure hijackHandler.await(); -// Iterator> iterator = channel.pipeline().iterator(); -// -// while (iterator.hasNext()) { -// Entry next = iterator.next(); -// System.out.println(next.getValue()); -// } - // start a new thread that reads from stdin and writes to the channel new Thread(new Runnable() { - + @Override public void run() { - + BufferedReader reader = new BufferedReader(new InputStreamReader(stdin)); - + int read = -1; - while((read = read(reader)) != -1) { + while ((read = read(reader)) != -1) { byte[] bytes = ByteBuffer.allocate(4).putInt(read).array(); channel.writeAndFlush(Unpooled.copiedBuffer(bytes)); } @@ -206,18 +185,19 @@ private int read(BufferedReader reader) { } } }).start(); - + return streamHandler.getInputStream(); } - - public void post(final Object entity, final OutputStream stdout, final OutputStream stderr, final InputStream stdin) { + + public void post(final Object entity, final OutputStream stdout, final OutputStream stderr, + final InputStream stdin) { HttpRequest request = preparePostRequest(resource, entity); DockerRawStreamHandler streamHandler = new DockerRawStreamHandler(stdout, stderr); final Channel channel = channelProvider.getChannel(); - + HijackHttpConnectionHandler hijackHandler = new HijackHttpConnectionHandler(); channel.pipeline() .addLast(new HttpClientUpgradeHandler(new HttpClientCodec(), hijackHandler, Integer.MAX_VALUE)); @@ -230,49 +210,28 @@ public HttpRequest getHttpRequest(String uri) { channel.pipeline().addLast(streamHandler); - System.out.println("resource: " + resource); - System.out.println(channel.isActive()); - channel.writeAndFlush(request); // wait for successful http upgrade procedure hijackHandler.await(); -// Iterator> iterator = channel.pipeline().iterator(); -// -// while (iterator.hasNext()) { -// Entry next = iterator.next(); -// System.out.println(next.getValue()); -// } - // start a new thread that reads from stdin and writes to the channel new Thread(new Runnable() { - + @Override public void run() { - + BufferedReader reader = new BufferedReader(new InputStreamReader(stdin, Charset.forName("UTF-8"))); - + int read = -1; - while((read = read(reader)) != -1) { + while ((read = read(reader)) != -1) { byte[] bytes = ByteBuffer.allocate(4).putInt(read).array(); try { bytes = new String(bytes).getBytes("US-ASCII"); - - HexDump.dump(bytes, 0, System.err, 0); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); - } catch (ArrayIndexOutOfBoundsException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (IllegalArgumentException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); } - + channel.writeAndFlush(Unpooled.copiedBuffer(bytes)); } } @@ -285,10 +244,8 @@ private int read(BufferedReader reader) { } } }).start(); - - - } + } private HttpRequest preparePostRequest(String uri, Object entity) { // Prepare the HTTP request. From e7f1724e8b0d069a1ce17531cf1bb92c751e986c Mon Sep 17 00:00:00 2001 From: marcuslinke Date: Sat, 14 Nov 2015 16:36:55 +0100 Subject: [PATCH 04/13] Fix regression in decode algorithm for docker stream --- .../netty/handler/DockerRawStreamHandler.java | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/github/dockerjava/netty/handler/DockerRawStreamHandler.java b/src/main/java/com/github/dockerjava/netty/handler/DockerRawStreamHandler.java index 7d2e501f5..e7f7e3377 100644 --- a/src/main/java/com/github/dockerjava/netty/handler/DockerRawStreamHandler.java +++ b/src/main/java/com/github/dockerjava/netty/handler/DockerRawStreamHandler.java @@ -21,6 +21,8 @@ public class DockerRawStreamHandler extends SimpleChannelInboundHandler private byte[] header = new byte[HEADER_SIZE]; private int headerCnt = 0; + + private byte[] payload = new byte[0]; private int payloadCnt = 0; @@ -32,13 +34,13 @@ public DockerRawStreamHandler(OutputStream stdout, OutputStream stderr) { @Override protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { - // System.out.println("msg: " + msg.readableBytes()); - rawBuffer.writeBytes(msg.copy()); - // System.out.println("readableBytes: " + rawBuffer.readableBytes()); + rawBuffer.writeBytes(msg.copy(), 0, msg.readableBytes()); Frame frame = null; do { + frame = decode(); + if (frame != null) { switch (frame.getStreamType()) { case STDOUT: @@ -53,11 +55,15 @@ protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Excep stderr.flush(); } break; + default: + System.err.println("unknown stream type"); } + headerCnt = 0; payloadCnt = 0; - } - } while ((frame = decode()) != null); + } + + } while (frame != null); } @@ -86,7 +92,8 @@ private Frame decode() { int payloadSize = ((header[4] & 0xff) << 24) + ((header[5] & 0xff) << 16) + ((header[6] & 0xff) << 8) + (header[7] & 0xff); - byte[] payload = new byte[payloadSize]; + if(payloadCnt == 0) + payload = new byte[payloadSize]; int count = read(payload, payloadCnt, payloadSize - payloadCnt); From f9c597218263a987f247da7d1f92e3ffe10bb2a9 Mon Sep 17 00:00:00 2001 From: Marcus Linke Date: Sun, 15 Nov 2015 22:39:17 +0100 Subject: [PATCH 05/13] Added basic SSL support --- .../dockerjava/core/DockerClientConfig.java | 15 +- .../netty/DockerCmdExecFactoryImpl.java | 848 +++++++++--------- .../core/DockerClientConfigTest.java | 2 +- .../dockerjava/core/DockerClientImplTest.java | 2 +- 4 files changed, 453 insertions(+), 414 deletions(-) diff --git a/src/main/java/com/github/dockerjava/core/DockerClientConfig.java b/src/main/java/com/github/dockerjava/core/DockerClientConfig.java index 6187be49c..3e7e20f79 100644 --- a/src/main/java/com/github/dockerjava/core/DockerClientConfig.java +++ b/src/main/java/com/github/dockerjava/core/DockerClientConfig.java @@ -73,16 +73,20 @@ public class DockerClientConfig implements Serializable { private final SSLConfig sslConfig; + private final String dockerCertPath; + DockerClientConfig(URI uri, String version, String username, String password, String email, String serverAddress, - String dockerCfgPath, SSLConfig sslConfig) { + String dockerCfgPath, String dockerCertPath, SSLConfig sslConfig) { this.uri = uri; this.version = RemoteApiVersion.parseConfigWithDefault(version); this.username = username; this.password = password; this.email = email; this.serverAddress = serverAddress; + this.dockerCfgPath = dockerCfgPath; this.sslConfig = sslConfig; + this.dockerCertPath = dockerCertPath; } private static Properties loadIncludedDockerProperties(Properties systemProperties) { @@ -240,6 +244,10 @@ public String getDockerCfgPath() { return dockerCfgPath; } + public String getDockerCertPath() { + return dockerCertPath; + } + private AuthConfig getAuthConfig() { AuthConfig authConfig = null; if (getUsername() != null && getPassword() != null && getEmail() != null && getServerAddress() != null) { @@ -346,7 +354,7 @@ public String toString() { public static class DockerClientConfigBuilder { private URI uri; - private String version, username, password, email, serverAddress, dockerCfgPath; + private String version, username, password, email, serverAddress, dockerCfgPath, dockerCertPath; private SSLConfig sslConfig; @@ -401,6 +409,7 @@ public DockerClientConfigBuilder withServerAddress(String serverAddress) { public final DockerClientConfigBuilder withDockerCertPath(String dockerCertPath) { if (dockerCertPath != null) { this.sslConfig = new LocalDirectorySSLConfig(dockerCertPath); + this.dockerCertPath = dockerCertPath; } return this; } @@ -416,7 +425,7 @@ public final DockerClientConfigBuilder withSSLConfig(SSLConfig config) { } public DockerClientConfig build() { - return new DockerClientConfig(uri, version, username, password, email, serverAddress, dockerCfgPath, + return new DockerClientConfig(uri, version, username, password, email, serverAddress, dockerCfgPath, dockerCertPath, sslConfig); } } diff --git a/src/main/java/com/github/dockerjava/netty/DockerCmdExecFactoryImpl.java b/src/main/java/com/github/dockerjava/netty/DockerCmdExecFactoryImpl.java index 3627038cd..fcbbe17b6 100644 --- a/src/main/java/com/github/dockerjava/netty/DockerCmdExecFactoryImpl.java +++ b/src/main/java/com/github/dockerjava/netty/DockerCmdExecFactoryImpl.java @@ -1,17 +1,33 @@ package com.github.dockerjava.netty; import static com.google.common.base.Preconditions.checkNotNull; +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.Channel; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.epoll.EpollDomainSocketChannel; +import io.netty.channel.epoll.EpollEventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.channel.unix.DomainSocketAddress; +import io.netty.channel.unix.UnixChannel; +import io.netty.handler.codec.http.HttpClientCodec; +import io.netty.handler.ssl.SslContext; +import io.netty.handler.ssl.SslContextBuilder; +import io.netty.handler.ssl.SslProvider; import java.io.IOException; import java.io.InputStream; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; -import java.net.UnknownHostException; +import java.security.Security; -import javax.net.ssl.SSLException; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.TrustManagerFactory; -import org.apache.commons.io.IOUtils; +import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -53,432 +69,446 @@ import com.github.dockerjava.api.command.UnpauseContainerCmd; import com.github.dockerjava.api.command.VersionCmd; import com.github.dockerjava.api.command.WaitContainerCmd; -import com.github.dockerjava.api.model.Frame; -import com.github.dockerjava.api.model.Info; +import com.github.dockerjava.core.CertificateUtils; import com.github.dockerjava.core.DockerClientConfig; -import com.github.dockerjava.core.DockerClientConfig.DockerClientConfigBuilder; import com.github.dockerjava.core.command.ExecCreateCmdImpl; import com.github.dockerjava.core.command.ExecStartCmdImpl; -import com.github.dockerjava.core.command.FrameReader; -import com.github.dockerjava.core.command.InfoCmdImpl; -import com.github.dockerjava.netty.handler.HijackHttpConnectionHandler; -import com.github.dockerjava.netty.handler.HttpResponseInboundHandler; - -import io.netty.bootstrap.Bootstrap; -import io.netty.buffer.ByteBuf; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.channel.epoll.EpollDomainSocketChannel; -import io.netty.channel.epoll.EpollEventLoopGroup; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.channel.socket.SocketChannel; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.channel.unix.DomainSocketAddress; -import io.netty.channel.unix.UnixChannel; -import io.netty.handler.codec.http.HttpClientCodec; -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.HttpObject; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.LastHttpContent; -import io.netty.handler.codec.json.JsonObjectDecoder; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; -import io.netty.handler.proxy.HttpProxyHandler; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.util.InsecureTrustManagerFactory; /** - * http://stackoverflow.com/questions/33296749/netty-connect-to-unix-domain- - * socket-failed http://netty.io/wiki/native-transports.html - * https://github.com/netty/netty/blob/master/example/src/main/java/io/netty/ - * example/http/snoop/HttpSnoopClient.java - * + * http://stackoverflow.com/questions/33296749/netty-connect-to-unix-domain- socket-failed + * http://netty.io/wiki/native-transports.html + * https://github.com/netty/netty/blob/master/example/src/main/java/io/netty/ example/http/snoop/HttpSnoopClient.java + * * @author marcus * */ public class DockerCmdExecFactoryImpl implements DockerCmdExecFactory { - private static final Logger LOGGER = LoggerFactory.getLogger(DockerCmdExecFactoryImpl.class.getName()); + private static final Logger LOGGER = LoggerFactory.getLogger(DockerCmdExecFactoryImpl.class.getName()); + + private DockerClientConfig dockerClientConfig; + + private Bootstrap bootstrap; + + private EventLoopGroup eventLoopGroup; + + private NettyInitializer nettyInitializer; + + private ChannelProvider channelProvider = new ChannelProvider() { + @Override + public Channel getChannel() { + return connect(); + } + }; + + @Override + public void init(DockerClientConfig dockerClientConfig) { + checkNotNull(dockerClientConfig, "config was not specified"); + this.dockerClientConfig = dockerClientConfig; + + bootstrap = new Bootstrap(); - private DockerClientConfig dockerClientConfig; + String scheme = dockerClientConfig.getUri().getScheme(); - private Bootstrap bootstrap; + if ("unix".equals(scheme)) { + nettyInitializer = new UnixDomainSocketInitializer(); + } else if (scheme.startsWith("http")) { + nettyInitializer = new InetSocketInitializer(); + } - private EventLoopGroup eventLoopGroup; + nettyInitializer.init(bootstrap, dockerClientConfig); + } - private ChannelProvider channelProvider = new ChannelProvider() { - @Override - public Channel getChannel() { - return connect(); - } - }; + private Channel connect() { + try { + return connect(bootstrap); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } - @Override - public void init(DockerClientConfig dockerClientConfig) { - checkNotNull(dockerClientConfig, "config was not specified"); - this.dockerClientConfig = dockerClientConfig; + private Channel connect(final Bootstrap bootstrap) throws InterruptedException { + return nettyInitializer.connect(bootstrap); + } - bootstrap = new Bootstrap(); + private static interface NettyInitializer { + EventLoopGroup init(final Bootstrap bootstrap, DockerClientConfig dockerClientConfig); + Channel connect(final Bootstrap bootstrap) throws InterruptedException; + } - String scheme = dockerClientConfig.getUri().getScheme(); - if ("unix".equals(scheme)) { - eventLoopGroup = new EpollEventLoopGroup(); - bootstrap.group(eventLoopGroup).channel(EpollDomainSocketChannel.class) - .handler(new ChannelInitializer() { - @Override - protected void initChannel(final UnixChannel channel) throws Exception { - channel.pipeline().addLast(new HttpClientCodec()); - } - }); + private class UnixDomainSocketInitializer implements NettyInitializer { + @Override + public EventLoopGroup init(Bootstrap bootstrap, DockerClientConfig dockerClientConfig) { + EventLoopGroup eventLoopGroup = new EpollEventLoopGroup(); + bootstrap.group(eventLoopGroup).channel(EpollDomainSocketChannel.class) + .handler(new ChannelInitializer() { + @Override + protected void initChannel(final UnixChannel channel) throws Exception { + channel.pipeline().addLast(new HttpClientCodec()); + } + }); + return eventLoopGroup; + } - } else if ("http".equals(scheme)) { - eventLoopGroup = new NioEventLoopGroup(); + @Override + public Channel connect(Bootstrap bootstrap) throws InterruptedException { + return bootstrap.connect(new DomainSocketAddress("/var/run/docker.sock")).sync().channel(); + } + } - try { - final SslContext sslCtx = SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE).build(); + private class InetSocketInitializer implements NettyInitializer { + @Override + public EventLoopGroup init(Bootstrap bootstrap, DockerClientConfig dockerClientConfig) { + EventLoopGroup eventLoopGroup = new NioEventLoopGroup(); - InetAddress addr = InetAddress.getLoopbackAddress(); - - final SocketAddress proxyAddress = new InetSocketAddress(addr, 8008); - - bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class) - .handler(new ChannelInitializer() { - @Override - protected void initChannel(final SocketChannel channel) throws Exception { - // channel.pipeline().addLast(new - // HttpProxyHandler(proxyAddress)); - - // channel.pipeline().addLast(sslCtx.newHandler(channel.alloc())); - HttpClientCodec httpClientCodec = new HttpClientCodec(); - - channel.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG)); - channel.pipeline().addLast(httpClientCodec); - - } - }); - - } catch (SSLException e1) { - // TODO Auto-generated catch block - e1.printStackTrace(); - } - - } - - } - - private Channel connect() { - try { - return connect(bootstrap); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } - - private Channel connect(final Bootstrap bootstrap) throws InterruptedException { - - String scheme = dockerClientConfig.getUri().getScheme(); - - if ("unix".equals(scheme)) { - return bootstrap.connect(new DomainSocketAddress("/var/run/docker.sock")).sync().channel(); - } else if ("http".equals(scheme)) { - String host = dockerClientConfig.getUri().getHost(); - int port = dockerClientConfig.getUri().getPort(); - - if (port == -1) { - throw new RuntimeException("no port configured for " + host); - } - - return bootstrap.connect(host, port).sync().channel(); - } else { - throw new RuntimeException("unsupported protocol scheme: " + scheme); - } - - } - - protected DockerClientConfig getDockerClientConfig() { - checkNotNull(dockerClientConfig, - "Factor not initialized, dockerClientConfig not set. You probably forgot to call init()!"); - return dockerClientConfig; - } - - @Override - public AuthCmd.Exec createAuthCmdExec() { - return null; // new AuthCmdExec(getBaseResource(), - // getDockerClientConfig()); - } - - @Override - public InfoCmd.Exec createInfoCmdExec() { - return new InfoCmdExec(new WebTarget(channelProvider), getDockerClientConfig()); - } - - @Override - public PingCmd.Exec createPingCmdExec() { - return null; // new PingCmdExec(getBaseResource(), - // getDockerClientConfig()); - } - - @Override - public VersionCmd.Exec createVersionCmdExec() { - return null; // new VersionCmdExec(getBaseResource(), - // getDockerClientConfig()); - } - - @Override - public PullImageCmd.Exec createPullImageCmdExec() { - return null; // new PullImageCmdExec(getBaseResource(), - // getDockerClientConfig()); - } - - @Override - public PushImageCmd.Exec createPushImageCmdExec() { - return null; // new PushImageCmdExec(getBaseResource(), - // getDockerClientConfig()); - } - - @Override - public SaveImageCmd.Exec createSaveImageCmdExec() { - return null; // new SaveImageCmdExec(getBaseResource(), - // getDockerClientConfig()); - } - - @Override - public CreateImageCmd.Exec createCreateImageCmdExec() { - return null; // new CreateImageCmdExec(getBaseResource(), - // getDockerClientConfig()); - } - - @Override - public SearchImagesCmd.Exec createSearchImagesCmdExec() { - return null; // new SearchImagesCmdExec(getBaseResource(), - // getDockerClientConfig()); - } - - @Override - public RemoveImageCmd.Exec createRemoveImageCmdExec() { - return null; // new RemoveImageCmdExec(getBaseResource(), - // getDockerClientConfig()); - } - - @Override - public ListImagesCmd.Exec createListImagesCmdExec() { - return null; // new ListImagesCmdExec(getBaseResource(), - // getDockerClientConfig()); - } - - @Override - public InspectImageCmd.Exec createInspectImageCmdExec() { - return null; // new InspectImageCmdExec(getBaseResource(), - // getDockerClientConfig()); - } - - @Override - public ListContainersCmd.Exec createListContainersCmdExec() { - return null; // new ListContainersCmdExec(getBaseResource(), - // getDockerClientConfig()); - } - - @Override - public CreateContainerCmd.Exec createCreateContainerCmdExec() { - return null; // new CreateContainerCmdExec(getBaseResource(), - // getDockerClientConfig()); - } - - @Override - public StartContainerCmd.Exec createStartContainerCmdExec() { - return null; // new StartContainerCmdExec(getBaseResource(), - // getDockerClientConfig()); - } - - @Override - public InspectContainerCmd.Exec createInspectContainerCmdExec() { - return null; // new InspectContainerCmdExec(getBaseResource(), - // getDockerClientConfig()); - } - - @Override - public ExecCreateCmd.Exec createExecCmdExec() { - return new ExecCreateCmdExec(new WebTarget(channelProvider), getDockerClientConfig()); - } - - @Override - public RemoveContainerCmd.Exec createRemoveContainerCmdExec() { - return null; // new RemoveContainerCmdExec(getBaseResource(), - // getDockerClientConfig()); - } - - @Override - public WaitContainerCmd.Exec createWaitContainerCmdExec() { - return null; // new WaitContainerCmdExec(getBaseResource(), - // getDockerClientConfig()); - } - - @Override - public AttachContainerCmd.Exec createAttachContainerCmdExec() { - return null; // new AttachContainerCmdExec(getBaseResource(), - // getDockerClientConfig()); - } - - @Override - public ExecStartCmd.Exec createExecStartCmdExec() { - - return new ExecStartCmdExec(new WebTarget(channelProvider), getDockerClientConfig()); - } - - @Override - public InspectExecCmd.Exec createInspectExecCmdExec() { - return null; // new InspectExecCmdExec(getBaseResource(), - // getDockerClientConfig()); - } - - @Override - public LogContainerCmd.Exec createLogContainerCmdExec() { - return null; // new LogContainerCmdExec(getBaseResource(), - // getDockerClientConfig()); - } - - @Override - public CopyFileFromContainerCmd.Exec createCopyFileFromContainerCmdExec() { - return null; // new CopyFileFromContainerCmdExec(getBaseResource(), - // getDockerClientConfig()); - } - - @Override - public StopContainerCmd.Exec createStopContainerCmdExec() { - return null; // new StopContainerCmdExec(getBaseResource(), - // getDockerClientConfig()); - } - - @Override - public ContainerDiffCmd.Exec createContainerDiffCmdExec() { - return null; // new ContainerDiffCmdExec(getBaseResource(), - // getDockerClientConfig()); - } - - @Override - public KillContainerCmd.Exec createKillContainerCmdExec() { - return null; // new KillContainerCmdExec(getBaseResource(), - // getDockerClientConfig()); - } - - @Override - public RestartContainerCmd.Exec createRestartContainerCmdExec() { - return null; // new RestartContainerCmdExec(getBaseResource(), - // getDockerClientConfig()); - } - - @Override - public CommitCmd.Exec createCommitCmdExec() { - return null; // new CommitCmdExec(getBaseResource(), - // getDockerClientConfig()); - } - - @Override - public BuildImageCmd.Exec createBuildImageCmdExec() { - return null; // new BuildImageCmdExec(getBaseResource(), - // getDockerClientConfig()); - } - - @Override - public TopContainerCmd.Exec createTopContainerCmdExec() { - return null; // new TopContainerCmdExec(getBaseResource(), - // getDockerClientConfig()); - } - - @Override - public TagImageCmd.Exec createTagImageCmdExec() { - return null; // new TagImageCmdExec(getBaseResource(), - // getDockerClientConfig()); - } - - @Override - public PauseContainerCmd.Exec createPauseContainerCmdExec() { - return null; // new PauseContainerCmdExec(getBaseResource(), - // getDockerClientConfig()); - } - - @Override - public UnpauseContainerCmd.Exec createUnpauseContainerCmdExec() { - return null; // new UnpauseContainerCmdExec(getBaseResource(), - // getDockerClientConfig()); - } - - @Override - public EventsCmd.Exec createEventsCmdExec() { - return null; // new EventsCmdExec(getBaseResource(), - // getDockerClientConfig()); - } - - @Override - public StatsCmd.Exec createStatsCmdExec() { - return null; // new StatsCmdExec(getBaseResource(), - // getDockerClientConfig()); - } - - @Override - public void close() throws IOException { - // checkNotNull(client, "Factory not initialized. You probably forgot to - // call init()!"); - // client.close(); - - eventLoopGroup.shutdownGracefully(); - } - - public static void main(String[] args) throws IOException { - DockerCmdExecFactory execFactory = new DockerCmdExecFactoryImpl(); - // execFactory.init(new - // DockerClientConfigBuilder().withUri("unix:///var/run/docker.sock").build()); - execFactory.init(new DockerClientConfigBuilder().withUri("http://localhost:2375").build()); - - // InfoCmd.Exec exec = execFactory.createInfoCmdExec(); - // - // InfoCmd infoCmd = new InfoCmdImpl(exec); - // - // Info info = infoCmd.exec(); - // - // System.out.println("result: " + info); - // - // infoCmd.close(); - - ExecCreateCmd.Exec execCreate = execFactory.createExecCmdExec(); - - ExecCreateCmd execCreateCmd = new ExecCreateCmdImpl(execCreate, "test").withCmd("/bin/bash") - .withAttachStdout(true).withAttachStderr(true).withAttachStdin(true); - - ExecCreateCmdResponse execCreateCmdResponse = execCreate.exec(execCreateCmd); - - System.out.println("result: " + execCreateCmdResponse); - - ExecStartCmd.Exec execStart = execFactory.createExecStartCmdExec(); - - ExecStartCmd execStartCmd = new ExecStartCmdImpl(execStart, execCreateCmdResponse.getId()).withDetach(false) - .withTty(true); - - InputStream response = execStart.exec(execStartCmd); - -// FrameReader frameReader = new FrameReader(response); -// -// System.out.println("read frame"); -// Frame frame = frameReader.readFrame(); -// -// while(frame != null) { -// System.out.println("frame: " + frame); -// frame = frameReader.readFrame(); -// } -// System.out.println("all frames read"); -// -// System.out.flush(); - - // System.out.println("response: " + IOUtils.toString(response, - // "UTF-8")); - - //execFactory.close(); - } + InetAddress addr = InetAddress.getLoopbackAddress(); + + final SocketAddress proxyAddress = new InetSocketAddress(addr, 8008); + + final SslContext sslCtx = initSslContext(dockerClientConfig); + + bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class) + .handler(new ChannelInitializer() { + @Override + protected void initChannel(final SocketChannel channel) throws Exception { + // channel.pipeline().addLast(new + // HttpProxyHandler(proxyAddress)); + + if (sslCtx != null) { + channel.pipeline().addLast(sslCtx.newHandler(channel.alloc())); + } + + channel.pipeline().addLast(new HttpClientCodec()); + } + }); + + return eventLoopGroup; + } + + @Override + public Channel connect(Bootstrap bootstrap) throws InterruptedException { + String host = dockerClientConfig.getUri().getHost(); + int port = dockerClientConfig.getUri().getPort(); + + if (port == -1) { + throw new RuntimeException("no port configured for " + host); + } + + return bootstrap.connect(host, port).sync().channel(); + } + + private SslContext initSslContext(DockerClientConfig dockerClientConfig) { + SslContext sslContext = null; + + String scheme = dockerClientConfig.getUri().getScheme(); + + if ("https".equals(scheme) && dockerClientConfig.getDockerCertPath() != null) { + + String dockerCertPath = dockerClientConfig.getDockerCertPath(); + + Security.addProvider(new BouncyCastleProvider()); + + try { + TrustManagerFactory tmFactory = TrustManagerFactory.getInstance("PKIX"); + tmFactory.init(CertificateUtils.createTrustStore(dockerCertPath)); + + KeyManagerFactory kmFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + kmFactory.init(CertificateUtils.createKeyStore(dockerCertPath), "docker".toCharArray()); + + sslContext = SslContextBuilder.forClient().sslProvider(SslProvider.JDK).keyManager(kmFactory) + .trustManager(tmFactory).build(); + } catch (Exception e) { + throw new RuntimeException(e); + } + + } + return sslContext; + } + } + + protected DockerClientConfig getDockerClientConfig() { + checkNotNull(dockerClientConfig, + "Factor not initialized, dockerClientConfig not set. You probably forgot to call init()!"); + return dockerClientConfig; + } + + @Override + public AuthCmd.Exec createAuthCmdExec() { + return null; // new AuthCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public InfoCmd.Exec createInfoCmdExec() { + return new InfoCmdExec(new WebTarget(channelProvider), getDockerClientConfig()); + } + + @Override + public PingCmd.Exec createPingCmdExec() { + return null; // new PingCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public VersionCmd.Exec createVersionCmdExec() { + return null; // new VersionCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public PullImageCmd.Exec createPullImageCmdExec() { + return null; // new PullImageCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public PushImageCmd.Exec createPushImageCmdExec() { + return null; // new PushImageCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public SaveImageCmd.Exec createSaveImageCmdExec() { + return null; // new SaveImageCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public CreateImageCmd.Exec createCreateImageCmdExec() { + return null; // new CreateImageCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public SearchImagesCmd.Exec createSearchImagesCmdExec() { + return null; // new SearchImagesCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public RemoveImageCmd.Exec createRemoveImageCmdExec() { + return null; // new RemoveImageCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public ListImagesCmd.Exec createListImagesCmdExec() { + return null; // new ListImagesCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public InspectImageCmd.Exec createInspectImageCmdExec() { + return null; // new InspectImageCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public ListContainersCmd.Exec createListContainersCmdExec() { + return null; // new ListContainersCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public CreateContainerCmd.Exec createCreateContainerCmdExec() { + return null; // new CreateContainerCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public StartContainerCmd.Exec createStartContainerCmdExec() { + return null; // new StartContainerCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public InspectContainerCmd.Exec createInspectContainerCmdExec() { + return null; // new InspectContainerCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public ExecCreateCmd.Exec createExecCmdExec() { + return new ExecCreateCmdExec(new WebTarget(channelProvider), getDockerClientConfig()); + } + + @Override + public RemoveContainerCmd.Exec createRemoveContainerCmdExec() { + return null; // new RemoveContainerCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public WaitContainerCmd.Exec createWaitContainerCmdExec() { + return null; // new WaitContainerCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public AttachContainerCmd.Exec createAttachContainerCmdExec() { + return null; // new AttachContainerCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public ExecStartCmd.Exec createExecStartCmdExec() { + + return new ExecStartCmdExec(new WebTarget(channelProvider), getDockerClientConfig()); + } + + @Override + public InspectExecCmd.Exec createInspectExecCmdExec() { + return null; // new InspectExecCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public LogContainerCmd.Exec createLogContainerCmdExec() { + return null; // new LogContainerCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public CopyFileFromContainerCmd.Exec createCopyFileFromContainerCmdExec() { + return null; // new CopyFileFromContainerCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public StopContainerCmd.Exec createStopContainerCmdExec() { + return null; // new StopContainerCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public ContainerDiffCmd.Exec createContainerDiffCmdExec() { + return null; // new ContainerDiffCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public KillContainerCmd.Exec createKillContainerCmdExec() { + return null; // new KillContainerCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public RestartContainerCmd.Exec createRestartContainerCmdExec() { + return null; // new RestartContainerCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public CommitCmd.Exec createCommitCmdExec() { + return null; // new CommitCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public BuildImageCmd.Exec createBuildImageCmdExec() { + return null; // new BuildImageCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public TopContainerCmd.Exec createTopContainerCmdExec() { + return null; // new TopContainerCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public TagImageCmd.Exec createTagImageCmdExec() { + return null; // new TagImageCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public PauseContainerCmd.Exec createPauseContainerCmdExec() { + return null; // new PauseContainerCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public UnpauseContainerCmd.Exec createUnpauseContainerCmdExec() { + return null; // new UnpauseContainerCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public EventsCmd.Exec createEventsCmdExec() { + return null; // new EventsCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public StatsCmd.Exec createStatsCmdExec() { + return null; // new StatsCmdExec(getBaseResource(), + // getDockerClientConfig()); + } + + @Override + public void close() throws IOException { + // checkNotNull(client, "Factory not initialized. You probably forgot to + // call init()!"); + // client.close(); + + eventLoopGroup.shutdownGracefully(); + } + + public static void main(String[] args) throws IOException { + DockerCmdExecFactory execFactory = new DockerCmdExecFactoryImpl(); + // execFactory.init(new + // DockerClientConfigBuilder().withUri("unix:///var/run/docker.sock").build()); + // execFactory.init(new DockerClientConfigBuilder().withUri("http://localhost:2375").build()); + + DockerClientConfig config = DockerClientConfig.createDefaultConfigBuilder().withUri("https://192.168.59.103:2376").build(); + + execFactory.init(config); + + // InfoCmd.Exec exec = execFactory.createInfoCmdExec(); + // + // InfoCmd infoCmd = new InfoCmdImpl(exec); + // + // Info info = infoCmd.exec(); + // + // System.out.println("result: " + info); + // + // infoCmd.close(); + + ExecCreateCmd.Exec execCreate = execFactory.createExecCmdExec(); + + ExecCreateCmd execCreateCmd = new ExecCreateCmdImpl(execCreate, "test").withCmd("/bin/bash") + .withAttachStdout(true).withAttachStderr(true).withAttachStdin(true); + + ExecCreateCmdResponse execCreateCmdResponse = execCreate.exec(execCreateCmd); + + System.out.println("result: " + execCreateCmdResponse); + + ExecStartCmd.Exec execStart = execFactory.createExecStartCmdExec(); + + ExecStartCmd execStartCmd = new ExecStartCmdImpl(execStart, execCreateCmdResponse.getId()).withDetach(false) + .withTty(true); + + InputStream response = execStart.exec(execStartCmd); + + // FrameReader frameReader = new FrameReader(response); + // + // System.out.println("read frame"); + // Frame frame = frameReader.readFrame(); + // + // while(frame != null) { + // System.out.println("frame: " + frame); + // frame = frameReader.readFrame(); + // } + // System.out.println("all frames read"); + // + // System.out.flush(); + + // System.out.println("response: " + IOUtils.toString(response, + // "UTF-8")); + + // execFactory.close(); + } } diff --git a/src/test/java/com/github/dockerjava/core/DockerClientConfigTest.java b/src/test/java/com/github/dockerjava/core/DockerClientConfigTest.java index e66cfa57d..cfcf7f381 100644 --- a/src/test/java/com/github/dockerjava/core/DockerClientConfigTest.java +++ b/src/test/java/com/github/dockerjava/core/DockerClientConfigTest.java @@ -16,7 +16,7 @@ public class DockerClientConfigTest { public static final DockerClientConfig EXAMPLE_CONFIG = newExampleConfig(); private static DockerClientConfig newExampleConfig() { - return new DockerClientConfig(URI.create("http://foo"), "bar", "baz", "qux", "blam", "wham", "flam", + return new DockerClientConfig(URI.create("http://foo"), "bar", "baz", "qux", "blam", "wham", "flam", "flum", new LocalDirectorySSLConfig("flim")); } diff --git a/src/test/java/com/github/dockerjava/core/DockerClientImplTest.java b/src/test/java/com/github/dockerjava/core/DockerClientImplTest.java index c5a1e841c..5f4ee3e67 100644 --- a/src/test/java/com/github/dockerjava/core/DockerClientImplTest.java +++ b/src/test/java/com/github/dockerjava/core/DockerClientImplTest.java @@ -10,7 +10,7 @@ public class DockerClientImplTest { @Test public void configuredInstanceAuthConfig() throws Exception { // given a config with null serverAddress - DockerClientConfig dockerClientConfig = new DockerClientConfig(null, null, "", "", "", null, null, null); + DockerClientConfig dockerClientConfig = new DockerClientConfig(null, null, "", "", "", null, null, null, null); DockerClientImpl dockerClient = DockerClientImpl.getInstance(dockerClientConfig); // when we get the auth config From 87e398aa15885b04c75e0532b00ae46ac6e04991 Mon Sep 17 00:00:00 2001 From: Marcus Linke Date: Fri, 20 Nov 2015 08:55:08 +0100 Subject: [PATCH 06/13] Migrated more command exec implementations and tests --- .../dockerjava/api/command/ExecStartCmd.java | 9 +- .../api/command/LogContainerCmd.java | 8 + .../dockerjava/core/DockerClientConfig.java | 12 +- .../core/command/ExecStartCmdImpl.java | 12 +- .../core/command/ExecStartResultCallback.java | 63 ++ .../dockerjava/jaxrs/ExecStartCmdExec.java | 22 +- .../netty/DockerCmdExecFactoryImpl.java | 158 ++--- .../dockerjava/netty/ExecStartCmdExec.java | 29 - .../dockerjava/netty/InvocationBuilder.java | 446 ++++++++------ .../github/dockerjava/netty/MediaType.java | 9 +- .../netty/exec/AbstrAsyncDockerCmdExec.java | 67 +++ .../netty/{ => exec}/AbstrDockerCmdExec.java | 4 +- .../{ => exec}/AbstrSyncDockerCmdExec.java | 3 +- .../dockerjava/netty/exec/AuthCmdExec.java | 34 ++ .../exec/CopyFileFromContainerCmdExec.java | 33 ++ .../netty/exec/CreateContainerCmdExec.java | 35 ++ .../netty/{ => exec}/ExecCreateCmdExec.java | 7 +- .../netty/exec/ExecStartCmdExec.java | 29 + .../netty/{ => exec}/InfoCmdExec.java | 11 +- .../netty/exec/InspectContainerCmdExec.java | 31 + .../netty/exec/InspectExecCmdExec.java | 26 + .../netty/exec/InspectImageCmdExec.java | 31 + .../netty/exec/KillContainerCmdExec.java | 35 ++ .../netty/exec/ListContainersCmdExec.java | 53 ++ .../netty/exec/LogContainerCmdExec.java | 48 ++ .../netty/exec/RemoveContainerCmdExec.java | 33 ++ .../netty/exec/StartContainerCmdExec.java | 33 ++ .../netty/exec/StopContainerCmdExec.java | 36 ++ .../netty/exec/WaitContainerCmdExec.java | 36 ++ .../handler/AwaitObjectInboundHandler.java | 62 +- .../DeserializeJsonInboundHandler.java | 7 +- ....java => FramedResponseStreamHandler.java} | 48 +- ....java => HttpConnectionHijackHandler.java} | 28 +- .../netty/handler/HttpResponseHandler.java | 83 +++ .../handler/HttpResponseInboundHandler.java | 128 ---- .../HttpResponseStreamInboundHandler.java | 176 +++--- .../core/DockerClientConfigTest.java | 2 +- .../dockerjava/core/DockerClientImplTest.java | 2 +- .../core/command/ExecStartCmdImplTest.java | 6 +- .../core/command/InspectExecCmdImplTest.java | 16 +- .../netty/AbstractDockerClientTest.java | 217 +++++++ .../netty/exec/AuthCmdExecTest.java | 57 ++ .../exec/CreateContainerCmdExecTest.java | 560 ++++++++++++++++++ .../netty/exec/ExecCreateCmdExecTest.java | 61 ++ .../netty/exec/ExecStartCmdExecTest.java | 97 +++ .../netty/exec/InfoCmdExecTest.java | 71 +++ .../netty/exec/InspectExecCmdExecTest.java | 96 +++ .../netty/exec/KillContainerCmdExecTest.java | 80 +++ .../netty/exec/LogContainerCmdExecTest.java | 171 ++++++ .../netty/exec/StopContainerCmdExecTest.java | 79 +++ .../netty/exec/WaitContainerCmdExecTest.java | 108 ++++ src/test/resources/logback.xml | 1 + 52 files changed, 2882 insertions(+), 627 deletions(-) create mode 100644 src/main/java/com/github/dockerjava/core/command/ExecStartResultCallback.java delete mode 100644 src/main/java/com/github/dockerjava/netty/ExecStartCmdExec.java create mode 100644 src/main/java/com/github/dockerjava/netty/exec/AbstrAsyncDockerCmdExec.java rename src/main/java/com/github/dockerjava/netty/{ => exec}/AbstrDockerCmdExec.java (96%) rename src/main/java/com/github/dockerjava/netty/{ => exec}/AbstrSyncDockerCmdExec.java (92%) create mode 100644 src/main/java/com/github/dockerjava/netty/exec/AuthCmdExec.java create mode 100644 src/main/java/com/github/dockerjava/netty/exec/CopyFileFromContainerCmdExec.java create mode 100644 src/main/java/com/github/dockerjava/netty/exec/CreateContainerCmdExec.java rename src/main/java/com/github/dockerjava/netty/{ => exec}/ExecCreateCmdExec.java (78%) create mode 100644 src/main/java/com/github/dockerjava/netty/exec/ExecStartCmdExec.java rename src/main/java/com/github/dockerjava/netty/{ => exec}/InfoCmdExec.java (91%) create mode 100644 src/main/java/com/github/dockerjava/netty/exec/InspectContainerCmdExec.java create mode 100644 src/main/java/com/github/dockerjava/netty/exec/InspectExecCmdExec.java create mode 100644 src/main/java/com/github/dockerjava/netty/exec/InspectImageCmdExec.java create mode 100644 src/main/java/com/github/dockerjava/netty/exec/KillContainerCmdExec.java create mode 100644 src/main/java/com/github/dockerjava/netty/exec/ListContainersCmdExec.java create mode 100644 src/main/java/com/github/dockerjava/netty/exec/LogContainerCmdExec.java create mode 100644 src/main/java/com/github/dockerjava/netty/exec/RemoveContainerCmdExec.java create mode 100644 src/main/java/com/github/dockerjava/netty/exec/StartContainerCmdExec.java create mode 100644 src/main/java/com/github/dockerjava/netty/exec/StopContainerCmdExec.java create mode 100644 src/main/java/com/github/dockerjava/netty/exec/WaitContainerCmdExec.java rename src/main/java/com/github/dockerjava/netty/handler/{DockerRawStreamHandler.java => FramedResponseStreamHandler.java} (76%) rename src/main/java/com/github/dockerjava/netty/handler/{HijackHttpConnectionHandler.java => HttpConnectionHijackHandler.java} (76%) create mode 100644 src/main/java/com/github/dockerjava/netty/handler/HttpResponseHandler.java delete mode 100644 src/main/java/com/github/dockerjava/netty/handler/HttpResponseInboundHandler.java create mode 100644 src/test/java/com/github/dockerjava/netty/AbstractDockerClientTest.java create mode 100644 src/test/java/com/github/dockerjava/netty/exec/AuthCmdExecTest.java create mode 100644 src/test/java/com/github/dockerjava/netty/exec/CreateContainerCmdExecTest.java create mode 100644 src/test/java/com/github/dockerjava/netty/exec/ExecCreateCmdExecTest.java create mode 100644 src/test/java/com/github/dockerjava/netty/exec/ExecStartCmdExecTest.java create mode 100644 src/test/java/com/github/dockerjava/netty/exec/InfoCmdExecTest.java create mode 100644 src/test/java/com/github/dockerjava/netty/exec/InspectExecCmdExecTest.java create mode 100644 src/test/java/com/github/dockerjava/netty/exec/KillContainerCmdExecTest.java create mode 100644 src/test/java/com/github/dockerjava/netty/exec/LogContainerCmdExecTest.java create mode 100644 src/test/java/com/github/dockerjava/netty/exec/StopContainerCmdExecTest.java create mode 100644 src/test/java/com/github/dockerjava/netty/exec/WaitContainerCmdExecTest.java diff --git a/src/main/java/com/github/dockerjava/api/command/ExecStartCmd.java b/src/main/java/com/github/dockerjava/api/command/ExecStartCmd.java index c9e0d3f00..2129bf40c 100644 --- a/src/main/java/com/github/dockerjava/api/command/ExecStartCmd.java +++ b/src/main/java/com/github/dockerjava/api/command/ExecStartCmd.java @@ -6,8 +6,10 @@ import javax.annotation.Nonnull; import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.async.ResultCallback; +import com.github.dockerjava.api.model.Frame; -public interface ExecStartCmd extends SyncDockerCmd { +public interface ExecStartCmd extends AsyncDockerCmd { @CheckForNull public String getExecId(); @@ -25,15 +27,14 @@ public interface ExecStartCmd extends SyncDockerCmd { public ExecStartCmd withTty(Boolean tty); /** - * Its the responsibility of the caller to consume and/or close the {@link InputStream} to prevent connection leaks. * * @throws com.github.dockerjava.api.NotFoundException * No such exec instance */ @Override - public InputStream exec() throws NotFoundException; + public > T exec(T resultCallback); - public static interface Exec extends DockerCmdSyncExec { + public static interface Exec extends DockerCmdAsyncExec { } } diff --git a/src/main/java/com/github/dockerjava/api/command/LogContainerCmd.java b/src/main/java/com/github/dockerjava/api/command/LogContainerCmd.java index f06a715cb..f4b5076c5 100644 --- a/src/main/java/com/github/dockerjava/api/command/LogContainerCmd.java +++ b/src/main/java/com/github/dockerjava/api/command/LogContainerCmd.java @@ -6,6 +6,7 @@ import javax.annotation.Nonnull; import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.async.ResultCallback; import com.github.dockerjava.api.model.Frame; /** @@ -69,6 +70,13 @@ public interface LogContainerCmd extends AsyncDockerCmd public LogContainerCmd withSince(Integer since); + /** + * @throws com.github.dockerjava.api.NotFoundException + * No such container + */ + @Override + public > T exec(T resultCallback); + public static interface Exec extends DockerCmdAsyncExec { } diff --git a/src/main/java/com/github/dockerjava/core/DockerClientConfig.java b/src/main/java/com/github/dockerjava/core/DockerClientConfig.java index 3e7e20f79..c3e12d91b 100644 --- a/src/main/java/com/github/dockerjava/core/DockerClientConfig.java +++ b/src/main/java/com/github/dockerjava/core/DockerClientConfig.java @@ -73,20 +73,16 @@ public class DockerClientConfig implements Serializable { private final SSLConfig sslConfig; - private final String dockerCertPath; - DockerClientConfig(URI uri, String version, String username, String password, String email, String serverAddress, - String dockerCfgPath, String dockerCertPath, SSLConfig sslConfig) { + String dockerCfgPath, SSLConfig sslConfig) { this.uri = uri; this.version = RemoteApiVersion.parseConfigWithDefault(version); this.username = username; this.password = password; this.email = email; this.serverAddress = serverAddress; - this.dockerCfgPath = dockerCfgPath; this.sslConfig = sslConfig; - this.dockerCertPath = dockerCertPath; } private static Properties loadIncludedDockerProperties(Properties systemProperties) { @@ -244,10 +240,6 @@ public String getDockerCfgPath() { return dockerCfgPath; } - public String getDockerCertPath() { - return dockerCertPath; - } - private AuthConfig getAuthConfig() { AuthConfig authConfig = null; if (getUsername() != null && getPassword() != null && getEmail() != null && getServerAddress() != null) { @@ -425,7 +417,7 @@ public final DockerClientConfigBuilder withSSLConfig(SSLConfig config) { } public DockerClientConfig build() { - return new DockerClientConfig(uri, version, username, password, email, serverAddress, dockerCfgPath, dockerCertPath, + return new DockerClientConfig(uri, version, username, password, email, serverAddress, dockerCfgPath, sslConfig); } } diff --git a/src/main/java/com/github/dockerjava/core/command/ExecStartCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/ExecStartCmdImpl.java index 72efd3052..0871fddfb 100644 --- a/src/main/java/com/github/dockerjava/core/command/ExecStartCmdImpl.java +++ b/src/main/java/com/github/dockerjava/core/command/ExecStartCmdImpl.java @@ -2,12 +2,11 @@ import static com.google.common.base.Preconditions.checkNotNull; -import java.io.InputStream; - -import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.async.ResultCallback; import com.github.dockerjava.api.command.ExecStartCmd; +import com.github.dockerjava.api.model.Frame; -public class ExecStartCmdImpl extends AbstrDockerCmd implements ExecStartCmd { +public class ExecStartCmdImpl extends AbstrAsyncDockerCmd implements ExecStartCmd { private String execId; @@ -57,8 +56,7 @@ public ExecStartCmd withTty(Boolean tty) { * No such exec instance */ @Override - public InputStream exec() throws NotFoundException { - return super.exec(); + public > T exec(T resultCallback) { + return super.exec(resultCallback); } - } diff --git a/src/main/java/com/github/dockerjava/core/command/ExecStartResultCallback.java b/src/main/java/com/github/dockerjava/core/command/ExecStartResultCallback.java new file mode 100644 index 000000000..dda336807 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/ExecStartResultCallback.java @@ -0,0 +1,63 @@ +/* + * Created on 21.07.2015 + */ +package com.github.dockerjava.core.command; + +import java.io.IOException; +import java.io.OutputStream; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.model.Frame; +import com.github.dockerjava.core.async.ResultCallbackTemplate; + +/** + * + * @author marcus + * + */ +public class ExecStartResultCallback extends ResultCallbackTemplate { + + private final static Logger LOGGER = LoggerFactory.getLogger(ExecStartResultCallback.class); + + private OutputStream stdout, stderr; + + public ExecStartResultCallback(OutputStream stdout, OutputStream stderr) { + this.stdout = stdout; + this.stderr = stderr; + } + + public ExecStartResultCallback() { + this(null, null); + } + + + @Override + public void onNext(Frame frame) { + if (frame != null) { + try { + switch (frame.getStreamType()) { + case STDOUT: + if (stdout != null) { + stdout.write(frame.getPayload()); + stdout.flush(); + } + break; + case STDERR: + if (stderr != null) { + stderr.write(frame.getPayload()); + stderr.flush(); + } + break; + default: + LOGGER.error("unknown stream type:" + frame.getStreamType()); + } + } catch (IOException e) { + onError(e); + } + + } + LOGGER.debug(frame.toString()); + } +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/ExecStartCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/ExecStartCmdExec.java index cf850d10c..0b1db4eb0 100644 --- a/src/main/java/com/github/dockerjava/jaxrs/ExecStartCmdExec.java +++ b/src/main/java/com/github/dockerjava/jaxrs/ExecStartCmdExec.java @@ -1,19 +1,26 @@ package com.github.dockerjava.jaxrs; +import com.github.dockerjava.api.async.ResultCallback; import com.github.dockerjava.api.command.ExecStartCmd; +import com.github.dockerjava.api.model.Frame; import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.core.async.FrameStreamProcessor; +import com.github.dockerjava.jaxrs.async.AbstractCallbackNotifier; +import com.github.dockerjava.jaxrs.async.POSTCallbackNotifier; import com.github.dockerjava.jaxrs.util.WrappedResponseInputStream; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.ws.rs.client.WebTarget; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; + import java.io.InputStream; import static javax.ws.rs.client.Entity.entity; -public class ExecStartCmdExec extends AbstrSyncDockerCmdExec implements ExecStartCmd.Exec { +public class ExecStartCmdExec extends AbstrAsyncDockerCmdExec implements ExecStartCmd.Exec { private static final Logger LOGGER = LoggerFactory.getLogger(ExecStartCmdExec.class); @@ -22,14 +29,13 @@ public ExecStartCmdExec(WebTarget baseResource, DockerClientConfig dockerClientC } @Override - protected InputStream execute(ExecStartCmd command) { - WebTarget webResource = getBaseResource().path("/exec/{id}/start").resolveTemplate("id", command.getExecId()); - - LOGGER.trace("POST: {}", webResource); + protected AbstractCallbackNotifier callbackNotifier(ExecStartCmd command, + ResultCallback resultCallback) { + WebTarget webTarget = getBaseResource().path("/exec/{id}/start").resolveTemplate("id", command.getExecId()); - Response response = webResource.request().accept(MediaType.APPLICATION_JSON) - .post(entity(command, MediaType.APPLICATION_JSON), Response.class); + LOGGER.trace("POST: {}", webTarget); - return new WrappedResponseInputStream(response); + return new POSTCallbackNotifier(new FrameStreamProcessor(), resultCallback, webTarget.request().accept( + MediaType.APPLICATION_JSON), null); } } diff --git a/src/main/java/com/github/dockerjava/netty/DockerCmdExecFactoryImpl.java b/src/main/java/com/github/dockerjava/netty/DockerCmdExecFactoryImpl.java index fcbbe17b6..6c97055fb 100644 --- a/src/main/java/com/github/dockerjava/netty/DockerCmdExecFactoryImpl.java +++ b/src/main/java/com/github/dockerjava/netty/DockerCmdExecFactoryImpl.java @@ -13,19 +13,18 @@ import io.netty.channel.unix.DomainSocketAddress; import io.netty.channel.unix.UnixChannel; import io.netty.handler.codec.http.HttpClientCodec; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.SslProvider; +import io.netty.handler.logging.LoggingHandler; +import io.netty.handler.ssl.SslHandler; import java.io.IOException; -import java.io.InputStream; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.security.Security; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLParameters; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.slf4j.Logger; @@ -69,10 +68,25 @@ import com.github.dockerjava.api.command.UnpauseContainerCmd; import com.github.dockerjava.api.command.VersionCmd; import com.github.dockerjava.api.command.WaitContainerCmd; -import com.github.dockerjava.core.CertificateUtils; import com.github.dockerjava.core.DockerClientConfig; import com.github.dockerjava.core.command.ExecCreateCmdImpl; import com.github.dockerjava.core.command.ExecStartCmdImpl; +import com.github.dockerjava.core.command.ExecStartResultCallback; +import com.github.dockerjava.netty.exec.AuthCmdExec; +import com.github.dockerjava.netty.exec.CopyFileFromContainerCmdExec; +import com.github.dockerjava.netty.exec.CreateContainerCmdExec; +import com.github.dockerjava.netty.exec.ExecCreateCmdExec; +import com.github.dockerjava.netty.exec.ExecStartCmdExec; +import com.github.dockerjava.netty.exec.InfoCmdExec; +import com.github.dockerjava.netty.exec.InspectContainerCmdExec; +import com.github.dockerjava.netty.exec.InspectExecCmdExec; +import com.github.dockerjava.netty.exec.InspectImageCmdExec; +import com.github.dockerjava.netty.exec.KillContainerCmdExec; +import com.github.dockerjava.netty.exec.LogContainerCmdExec; +import com.github.dockerjava.netty.exec.RemoveContainerCmdExec; +import com.github.dockerjava.netty.exec.StartContainerCmdExec; +import com.github.dockerjava.netty.exec.StopContainerCmdExec; +import com.github.dockerjava.netty.exec.WaitContainerCmdExec; /** * http://stackoverflow.com/questions/33296749/netty-connect-to-unix-domain- socket-failed @@ -97,7 +111,9 @@ public class DockerCmdExecFactoryImpl implements DockerCmdExecFactory { private ChannelProvider channelProvider = new ChannelProvider() { @Override public Channel getChannel() { - return connect(); + Channel channel = connect(); + channel.pipeline().addLast(new LoggingHandler(getClass())); + return channel; } }; @@ -128,15 +144,15 @@ private Channel connect() { } private Channel connect(final Bootstrap bootstrap) throws InterruptedException { - return nettyInitializer.connect(bootstrap); + return nettyInitializer.connect(bootstrap); } private static interface NettyInitializer { EventLoopGroup init(final Bootstrap bootstrap, DockerClientConfig dockerClientConfig); + Channel connect(final Bootstrap bootstrap) throws InterruptedException; } - private class UnixDomainSocketInitializer implements NettyInitializer { @Override public EventLoopGroup init(Bootstrap bootstrap, DockerClientConfig dockerClientConfig) { @@ -159,14 +175,14 @@ public Channel connect(Bootstrap bootstrap) throws InterruptedException { private class InetSocketInitializer implements NettyInitializer { @Override - public EventLoopGroup init(Bootstrap bootstrap, DockerClientConfig dockerClientConfig) { + public EventLoopGroup init(Bootstrap bootstrap, final DockerClientConfig dockerClientConfig) { EventLoopGroup eventLoopGroup = new NioEventLoopGroup(); InetAddress addr = InetAddress.getLoopbackAddress(); final SocketAddress proxyAddress = new InetSocketAddress(addr, 8008); - final SslContext sslCtx = initSslContext(dockerClientConfig); + Security.addProvider(new BouncyCastleProvider()); bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class) .handler(new ChannelInitializer() { @@ -174,11 +190,6 @@ public EventLoopGroup init(Bootstrap bootstrap, DockerClientConfig dockerClientC protected void initChannel(final SocketChannel channel) throws Exception { // channel.pipeline().addLast(new // HttpProxyHandler(proxyAddress)); - - if (sslCtx != null) { - channel.pipeline().addLast(sslCtx.newHandler(channel.alloc())); - } - channel.pipeline().addLast(new HttpClientCodec()); } }); @@ -195,35 +206,42 @@ public Channel connect(Bootstrap bootstrap) throws InterruptedException { throw new RuntimeException("no port configured for " + host); } - return bootstrap.connect(host, port).sync().channel(); - } + Channel channel = bootstrap.connect(host, port).sync().channel(); + + if ("https".equals(dockerClientConfig.getUri().getScheme())) { + final SslHandler ssl = initSsl(dockerClientConfig); - private SslContext initSslContext(DockerClientConfig dockerClientConfig) { - SslContext sslContext = null; + if (ssl != null) { + channel.pipeline().addFirst(ssl); + } + } - String scheme = dockerClientConfig.getUri().getScheme(); + return channel; + } - if ("https".equals(scheme) && dockerClientConfig.getDockerCertPath() != null) { + private SslHandler initSsl(DockerClientConfig dockerClientConfig) { + SslHandler ssl = null; - String dockerCertPath = dockerClientConfig.getDockerCertPath(); + try { + String host = dockerClientConfig.getUri().getHost(); + int port = dockerClientConfig.getUri().getPort(); - Security.addProvider(new BouncyCastleProvider()); + SSLContext sslContext = dockerClientConfig.getSslConfig().getSSLContext(); - try { - TrustManagerFactory tmFactory = TrustManagerFactory.getInstance("PKIX"); - tmFactory.init(CertificateUtils.createTrustStore(dockerCertPath)); + SSLEngine engine = sslContext.createSSLEngine(host, port); + engine.setUseClientMode(true); + engine.setSSLParameters(enableHostNameVerification(engine.getSSLParameters())); - KeyManagerFactory kmFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); - kmFactory.init(CertificateUtils.createKeyStore(dockerCertPath), "docker".toCharArray()); + // in the future we may use HostnameVerifier like here: + // https://github.com/AsyncHttpClient/async-http-client/blob/1.8.x/src/main/java/com/ning/http/client/providers/netty/NettyConnectListener.java#L76 - sslContext = SslContextBuilder.forClient().sslProvider(SslProvider.JDK).keyManager(kmFactory) - .trustManager(tmFactory).build(); - } catch (Exception e) { - throw new RuntimeException(e); - } + ssl = new SslHandler(engine); + } catch (Exception e) { + throw new RuntimeException(e); } - return sslContext; + + return ssl; } } @@ -233,15 +251,19 @@ protected DockerClientConfig getDockerClientConfig() { return dockerClientConfig; } + public SSLParameters enableHostNameVerification(SSLParameters sslParameters) { + sslParameters.setEndpointIdentificationAlgorithm("HTTPS"); + return sslParameters; + } + @Override public AuthCmd.Exec createAuthCmdExec() { - return null; // new AuthCmdExec(getBaseResource(), - // getDockerClientConfig()); + return new AuthCmdExec(getBaseResource(), getDockerClientConfig()); } @Override public InfoCmd.Exec createInfoCmdExec() { - return new InfoCmdExec(new WebTarget(channelProvider), getDockerClientConfig()); + return new InfoCmdExec(getBaseResource(), getDockerClientConfig()); } @Override @@ -300,8 +322,7 @@ public ListImagesCmd.Exec createListImagesCmdExec() { @Override public InspectImageCmd.Exec createInspectImageCmdExec() { - return null; // new InspectImageCmdExec(getBaseResource(), - // getDockerClientConfig()); + return new InspectImageCmdExec(getBaseResource(), getDockerClientConfig()); } @Override @@ -312,37 +333,32 @@ public ListContainersCmd.Exec createListContainersCmdExec() { @Override public CreateContainerCmd.Exec createCreateContainerCmdExec() { - return null; // new CreateContainerCmdExec(getBaseResource(), - // getDockerClientConfig()); + return new CreateContainerCmdExec(getBaseResource(), getDockerClientConfig()); } @Override public StartContainerCmd.Exec createStartContainerCmdExec() { - return null; // new StartContainerCmdExec(getBaseResource(), - // getDockerClientConfig()); + return new StartContainerCmdExec(getBaseResource(), getDockerClientConfig()); } @Override public InspectContainerCmd.Exec createInspectContainerCmdExec() { - return null; // new InspectContainerCmdExec(getBaseResource(), - // getDockerClientConfig()); + return new InspectContainerCmdExec(getBaseResource(), getDockerClientConfig()); } @Override public ExecCreateCmd.Exec createExecCmdExec() { - return new ExecCreateCmdExec(new WebTarget(channelProvider), getDockerClientConfig()); + return new ExecCreateCmdExec(getBaseResource(), getDockerClientConfig()); } @Override public RemoveContainerCmd.Exec createRemoveContainerCmdExec() { - return null; // new RemoveContainerCmdExec(getBaseResource(), - // getDockerClientConfig()); + return new RemoveContainerCmdExec(getBaseResource(), getDockerClientConfig()); } @Override public WaitContainerCmd.Exec createWaitContainerCmdExec() { - return null; // new WaitContainerCmdExec(getBaseResource(), - // getDockerClientConfig()); + return new WaitContainerCmdExec(getBaseResource(), getDockerClientConfig()); } @Override @@ -353,32 +369,27 @@ public AttachContainerCmd.Exec createAttachContainerCmdExec() { @Override public ExecStartCmd.Exec createExecStartCmdExec() { - - return new ExecStartCmdExec(new WebTarget(channelProvider), getDockerClientConfig()); + return new ExecStartCmdExec(getBaseResource(), getDockerClientConfig()); } @Override public InspectExecCmd.Exec createInspectExecCmdExec() { - return null; // new InspectExecCmdExec(getBaseResource(), - // getDockerClientConfig()); + return new InspectExecCmdExec(getBaseResource(), getDockerClientConfig()); } @Override public LogContainerCmd.Exec createLogContainerCmdExec() { - return null; // new LogContainerCmdExec(getBaseResource(), - // getDockerClientConfig()); + return new LogContainerCmdExec(getBaseResource(), getDockerClientConfig()); } @Override public CopyFileFromContainerCmd.Exec createCopyFileFromContainerCmdExec() { - return null; // new CopyFileFromContainerCmdExec(getBaseResource(), - // getDockerClientConfig()); + return new CopyFileFromContainerCmdExec(getBaseResource(), getDockerClientConfig()); } @Override public StopContainerCmd.Exec createStopContainerCmdExec() { - return null; // new StopContainerCmdExec(getBaseResource(), - // getDockerClientConfig()); + return new StopContainerCmdExec(getBaseResource(), getDockerClientConfig()); } @Override @@ -389,8 +400,7 @@ public ContainerDiffCmd.Exec createContainerDiffCmdExec() { @Override public KillContainerCmd.Exec createKillContainerCmdExec() { - return null; // new KillContainerCmdExec(getBaseResource(), - // getDockerClientConfig()); + return new KillContainerCmdExec(getBaseResource(), getDockerClientConfig()); } @Override @@ -456,13 +466,18 @@ public void close() throws IOException { eventLoopGroup.shutdownGracefully(); } + private WebTarget getBaseResource() { + return new WebTarget(channelProvider); + } + public static void main(String[] args) throws IOException { DockerCmdExecFactory execFactory = new DockerCmdExecFactoryImpl(); // execFactory.init(new // DockerClientConfigBuilder().withUri("unix:///var/run/docker.sock").build()); // execFactory.init(new DockerClientConfigBuilder().withUri("http://localhost:2375").build()); - DockerClientConfig config = DockerClientConfig.createDefaultConfigBuilder().withUri("https://192.168.59.103:2376").build(); + DockerClientConfig config = DockerClientConfig.createDefaultConfigBuilder() + .withUri("https://192.168.59.103:2376").build(); execFactory.init(config); @@ -490,7 +505,18 @@ public static void main(String[] args) throws IOException { ExecStartCmd execStartCmd = new ExecStartCmdImpl(execStart, execCreateCmdResponse.getId()).withDetach(false) .withTty(true); - InputStream response = execStart.exec(execStartCmd); + ExecStartResultCallback callback = new ExecStartResultCallback(System.out, System.err); + + execStart.exec(execStartCmd, callback); + + try { + callback.awaitCompletion(); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + System.err.println("main finished"); // FrameReader frameReader = new FrameReader(response); // diff --git a/src/main/java/com/github/dockerjava/netty/ExecStartCmdExec.java b/src/main/java/com/github/dockerjava/netty/ExecStartCmdExec.java deleted file mode 100644 index b158bf4e2..000000000 --- a/src/main/java/com/github/dockerjava/netty/ExecStartCmdExec.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.github.dockerjava.netty; - -import java.io.InputStream; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.github.dockerjava.api.command.ExecStartCmd; -import com.github.dockerjava.core.DockerClientConfig; - -public class ExecStartCmdExec extends AbstrSyncDockerCmdExec implements ExecStartCmd.Exec { - - private static final Logger LOGGER = LoggerFactory.getLogger(ExecStartCmdExec.class); - - public ExecStartCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { - super(baseResource, dockerClientConfig); - } - - @Override - protected InputStream execute(ExecStartCmd command) { - WebTarget webResource = getBaseResource().path("/exec/{id}/start").resolveTemplate("id", command.getExecId()); - - LOGGER.trace("POST: {}", webResource); - - webResource.request().accept(MediaType.APPLICATION_JSON).post(command, System.out, null, System.in); - - return null; - } -} diff --git a/src/main/java/com/github/dockerjava/netty/InvocationBuilder.java b/src/main/java/com/github/dockerjava/netty/InvocationBuilder.java index b4f0fb1ee..a2cfeb827 100644 --- a/src/main/java/com/github/dockerjava/netty/InvocationBuilder.java +++ b/src/main/java/com/github/dockerjava/netty/InvocationBuilder.java @@ -1,270 +1,340 @@ package com.github.dockerjava.netty; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import io.netty.handler.codec.http.DefaultFullHttpRequest; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.HttpClientCodec; +import io.netty.handler.codec.http.HttpClientUpgradeHandler; +import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.handler.codec.http.HttpHeaderValues; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import io.netty.handler.codec.http.HttpVersion; +import io.netty.handler.codec.json.JsonObjectDecoder; + import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.util.HashMap; +import java.util.List; import java.util.Map; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.dockerjava.api.BadRequestException; +import com.github.dockerjava.api.ConflictException; +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.InternalServerErrorException; +import com.github.dockerjava.api.NotAcceptableException; +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.NotModifiedException; +import com.github.dockerjava.api.UnauthorizedException; +import com.github.dockerjava.api.async.ResultCallback; +import com.github.dockerjava.api.model.Container; +import com.github.dockerjava.api.model.Frame; import com.github.dockerjava.netty.handler.AwaitObjectInboundHandler; import com.github.dockerjava.netty.handler.DeserializeJsonInboundHandler; -import com.github.dockerjava.netty.handler.DockerRawStreamHandler; -import com.github.dockerjava.netty.handler.HijackHttpConnectionHandler; +import com.github.dockerjava.netty.handler.FramedResponseStreamHandler; +import com.github.dockerjava.netty.handler.HttpConnectionHijackHandler; import com.github.dockerjava.netty.handler.HttpRequestProvider; -import com.github.dockerjava.netty.handler.HttpResponseInboundHandler; +import com.github.dockerjava.netty.handler.HttpResponseHandler; import com.github.dockerjava.netty.handler.HttpResponseStreamInboundHandler; -import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; -import io.netty.handler.codec.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.HttpClientCodec; -import io.netty.handler.codec.http.HttpClientUpgradeHandler; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.codec.json.JsonObjectDecoder; - public class InvocationBuilder { - private ChannelProvider channelProvider; - private String resource; - private Map headers = new HashMap(); + private ChannelProvider channelProvider; + + private String resource; + + private Map headers = new HashMap(); + + public InvocationBuilder(ChannelProvider channelProvider, String resource) { + this.channelProvider = channelProvider; + this.resource = resource; + } + + public InvocationBuilder accept(MediaType mediaType) { + headers.put(HttpHeaderNames.ACCEPT.toString(), mediaType.getMediaType()); + return this; + } + + public T get(Class resultType) { - public InvocationBuilder(ChannelProvider channelProvider, String resource) { - this.channelProvider = channelProvider; - this.resource = resource; - } + HttpRequestProvider requestProvider = httpGetRequestProvider(); - public InvocationBuilder accept(MediaType mediaType) { - headers.put(HttpHeaderNames.ACCEPT.toString(), mediaType.getMediaType()); - return this; - } + Channel channel = channelProvider.getChannel(); - public T get(Class resultType) { - FullHttpRequest request = prepareGetRequest(resource); + AwaitObjectInboundHandler awaitObject = new AwaitObjectInboundHandler(resultType); - Channel channel = channelProvider.getChannel(); + HttpResponseHandler responseHandler = new HttpResponseHandler(); - AwaitObjectInboundHandler awaitObject = new AwaitObjectInboundHandler(resultType); + channel.pipeline().addLast(responseHandler); + channel.pipeline().addLast(new JsonObjectDecoder()); + channel.pipeline().addLast(new DeserializeJsonInboundHandler(resultType)); + channel.pipeline().addLast(awaitObject); - channel.pipeline().addLast(new HttpResponseInboundHandler(new HttpRequestProvider() { - @Override - public HttpRequest getHttpRequest(String uri) { - return prepareGetRequest(uri); - } - })); - channel.pipeline().addLast(new JsonObjectDecoder()); - channel.pipeline().addLast(new DeserializeJsonInboundHandler(resultType)); - channel.pipeline().addLast(awaitObject); + sendRequestAndHandleResponse(requestProvider, channel, responseHandler); - System.out.println("resource: " + resource); + return awaitObject.await(); - channel.writeAndFlush(request); + } - // Wait for the server to close the connection. - try { - // channel.closeFuture().sync(); - T response = awaitObject.await(); + private void sendRequestAndHandleResponse(HttpRequestProvider requestProvider, Channel channel, + HttpResponseHandler responseHandler) { - // channel.disconnect(); + channel.writeAndFlush(requestProvider.getHttpRequest(resource)); - return response; - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } + handleResponse(channel, responseHandler, requestProvider); + } - private FullHttpRequest prepareGetRequest(String uri) { - // Prepare the HTTP request. - FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri); + private HttpRequestProvider httpGetRequestProvider() { + return new HttpRequestProvider() { + @Override + public HttpRequest getHttpRequest(String uri) { + return prepareGetRequest(uri); + } + }; + } - setDefaultHeaders(request); - return request; - } + public void get(ResultCallback resultCallback) { - private void setDefaultHeaders(HttpRequest request) { - request.headers().set(HttpHeaderNames.HOST, ""); - request.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE); - request.headers().set(HttpHeaderNames.ACCEPT_ENCODING, HttpHeaderValues.GZIP); + HttpRequestProvider requestProvider = httpGetRequestProvider(); - for (Map.Entry entry : headers.entrySet()) { - request.headers().set((CharSequence) entry.getKey(), entry.getValue()); - } - } + HttpResponseHandler responseHandler = new HttpResponseHandler(); - public T post(final Object entity, Class resultType) { + FramedResponseStreamHandler streamHandler = new FramedResponseStreamHandler(resultCallback); - HttpRequest request = preparePostRequest(resource, entity); + Channel channel = channelProvider.getChannel(); - AwaitObjectInboundHandler awaitObject = new AwaitObjectInboundHandler(resultType); + channel.pipeline().addLast(responseHandler); + channel.pipeline().addLast(streamHandler); - Channel channel = channelProvider.getChannel(); + sendRequestAndHandleResponse(requestProvider, channel, responseHandler); + } - channel.pipeline().addLast(new HttpResponseInboundHandler(new HttpRequestProvider() { - @Override - public HttpRequest getHttpRequest(String uri) { - return preparePostRequest(uri, entity); - } - })); - channel.pipeline().addLast(new JsonObjectDecoder()); - channel.pipeline().addLast(new DeserializeJsonInboundHandler(resultType)); - channel.pipeline().addLast(awaitObject); + private FullHttpRequest prepareGetRequest(String uri) { - System.out.println("resource: " + resource); - System.out.println(channel.isActive()); + FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri); - channel.writeAndFlush(request); + setDefaultHeaders(request); - // Wait for the server to close the connection. - try { - // channel.closeFuture().sync(); - T response = awaitObject.await(); + return request; + } - return response; - } catch (InterruptedException e) { - throw new RuntimeException(e); - } + private void setDefaultHeaders(HttpRequest request) { + request.headers().set(HttpHeaderNames.HOST, ""); + request.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE); + request.headers().set(HttpHeaderNames.ACCEPT_ENCODING, HttpHeaderValues.GZIP); - } + for (Map.Entry entry : headers.entrySet()) { + request.headers().set((CharSequence) entry.getKey(), entry.getValue()); + } + } - public InputStream post(final Object entity, final InputStream stdin) { + public T post(final Object entity, Class resultType) { - HttpRequest request = preparePostRequest(resource, entity); + HttpRequestProvider requestProvider = httpPostRequestProvider(entity); - HttpResponseStreamInboundHandler streamHandler = new HttpResponseStreamInboundHandler(); + AwaitObjectInboundHandler awaitObject = new AwaitObjectInboundHandler(resultType); - final Channel channel = channelProvider.getChannel(); + Channel channel = channelProvider.getChannel(); - HijackHttpConnectionHandler hijackHandler = new HijackHttpConnectionHandler(); - channel.pipeline() - .addLast(new HttpClientUpgradeHandler(new HttpClientCodec(), hijackHandler, Integer.MAX_VALUE)); - channel.pipeline().addLast(new HttpResponseInboundHandler(new HttpRequestProvider() { - @Override - public HttpRequest getHttpRequest(String uri) { - return preparePostRequest(uri, entity); - } - })); + HttpResponseHandler responseHandler = new HttpResponseHandler(); - channel.pipeline().addLast(streamHandler); + channel.pipeline().addLast(responseHandler); + channel.pipeline().addLast(new JsonObjectDecoder()); + channel.pipeline().addLast(new DeserializeJsonInboundHandler(resultType)); + channel.pipeline().addLast(awaitObject); - channel.writeAndFlush(request); + sendRequestAndHandleResponse(requestProvider, channel, responseHandler); - // wait for successful http upgrade procedure - hijackHandler.await(); + return awaitObject.await(); - // start a new thread that reads from stdin and writes to the channel - new Thread(new Runnable() { + } - @Override - public void run() { + public void post(final Object entity, final InputStream stdin, ResultCallback resultCallback) { - BufferedReader reader = new BufferedReader(new InputStreamReader(stdin)); + HttpRequestProvider requestProvider = httpPostRequestProvider(entity); - int read = -1; - while ((read = read(reader)) != -1) { - byte[] bytes = ByteBuffer.allocate(4).putInt(read).array(); - channel.writeAndFlush(Unpooled.copiedBuffer(bytes)); - } - } + FramedResponseStreamHandler streamHandler = new FramedResponseStreamHandler(resultCallback); - private int read(BufferedReader reader) { - try { - return reader.read(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - }).start(); + final Channel channel = channelProvider.getChannel(); - return streamHandler.getInputStream(); - } + HttpResponseHandler responseHandler = new HttpResponseHandler(); - public void post(final Object entity, final OutputStream stdout, final OutputStream stderr, - final InputStream stdin) { + HttpConnectionHijackHandler hijackHandler = new HttpConnectionHijackHandler(responseHandler); - HttpRequest request = preparePostRequest(resource, entity); + channel.pipeline().addLast( + new HttpClientUpgradeHandler(new HttpClientCodec(), hijackHandler, Integer.MAX_VALUE)); + channel.pipeline().addLast(streamHandler); - DockerRawStreamHandler streamHandler = new DockerRawStreamHandler(stdout, stderr); + sendRequestAndHandleResponse(requestProvider, channel, responseHandler); - final Channel channel = channelProvider.getChannel(); + // wait for successful http upgrade procedure + hijackHandler.await(); - HijackHttpConnectionHandler hijackHandler = new HijackHttpConnectionHandler(); - channel.pipeline() - .addLast(new HttpClientUpgradeHandler(new HttpClientCodec(), hijackHandler, Integer.MAX_VALUE)); - channel.pipeline().addLast(new HttpResponseInboundHandler(new HttpRequestProvider() { - @Override - public HttpRequest getHttpRequest(String uri) { - return preparePostRequest(uri, entity); - } - })); + // now we can start a new thread that reads from stdin and writes to the channel + new Thread(new Runnable() { - channel.pipeline().addLast(streamHandler); + @Override + public void run() { - channel.writeAndFlush(request); + BufferedReader reader = new BufferedReader(new InputStreamReader(stdin, Charset.forName("UTF-8"))); - // wait for successful http upgrade procedure - hijackHandler.await(); + int read = -1; + while ((read = read(reader)) != -1) { + byte[] bytes = ByteBuffer.allocate(4).putInt(read).array(); + try { + bytes = new String(bytes).getBytes("US-ASCII"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } - // start a new thread that reads from stdin and writes to the channel - new Thread(new Runnable() { + channel.writeAndFlush(Unpooled.copiedBuffer(bytes)); + } + } - @Override - public void run() { + private int read(BufferedReader reader) { + try { + return reader.read(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }).start(); - BufferedReader reader = new BufferedReader(new InputStreamReader(stdin, Charset.forName("UTF-8"))); + } - int read = -1; - while ((read = read(reader)) != -1) { - byte[] bytes = ByteBuffer.allocate(4).putInt(read).array(); - try { - bytes = new String(bytes).getBytes("US-ASCII"); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } + private void handleResponse(final Channel channel, HttpResponseHandler responseHandler, + HttpRequestProvider requestProvider) { + HttpResponse response = responseHandler.awaitResponse(); - channel.writeAndFlush(Unpooled.copiedBuffer(bytes)); - } - } + switch (response.status().code()) { + case 101: + case 200: + case 201: + case 204: + break; + case 301: + case 302: + if (response.headers().contains(HttpHeaderNames.LOCATION)) { + String location = response.headers().get(HttpHeaderNames.LOCATION); + System.out.println("redirected to :" + location); + HttpRequest redirected = requestProvider.getHttpRequest(location); - private int read(BufferedReader reader) { - try { - return reader.read(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - }).start(); + channel.writeAndFlush(redirected); + } + break; + case 304: + throw new NotModifiedException(responseHandler.awaitErrorBody()); + case 400: + throw new BadRequestException(responseHandler.awaitErrorBody()); + case 401: + throw new UnauthorizedException(responseHandler.awaitErrorBody()); + case 404: + throw new NotFoundException(responseHandler.awaitErrorBody()); + case 406: + throw new NotAcceptableException(responseHandler.awaitErrorBody()); + case 409: + throw new ConflictException(responseHandler.awaitErrorBody()); + case 500: + throw new InternalServerErrorException(responseHandler.awaitErrorBody()); + default: + throw new DockerException(responseHandler.awaitErrorBody(), response.status().code()); + } - } + } - private HttpRequest preparePostRequest(String uri, Object entity) { - // Prepare the HTTP request. - FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, uri); + private HttpRequest preparePostRequest(String uri, Object entity) { - setDefaultHeaders(request); + FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, uri); - byte[] bytes; - try { - bytes = new ObjectMapper().writeValueAsBytes(entity); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } + setDefaultHeaders(request); - request.headers().set(HttpHeaderNames.CONTENT_TYPE, "application/json"); - request.content().clear().writeBytes(Unpooled.copiedBuffer(bytes)); - request.headers().set(HttpHeaderNames.CONTENT_LENGTH, bytes.length); + if (entity != null) { + byte[] bytes; + try { + bytes = new ObjectMapper().writeValueAsBytes(entity); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + + request.headers().set(HttpHeaderNames.CONTENT_TYPE, "application/json"); + request.content().clear().writeBytes(Unpooled.copiedBuffer(bytes)); + request.headers().set(HttpHeaderNames.CONTENT_LENGTH, bytes.length); + } else { + request.headers().set(HttpHeaderNames.CONTENT_LENGTH, 0); + } + + return request; + } + + private HttpRequest prepareDeleteRequest(String uri) { + + FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.DELETE, uri); + + setDefaultHeaders(request); + + return request; + } + + public InputStream post(final Object entity) { + + HttpRequestProvider requestProvider = httpPostRequestProvider(entity); + + Channel channel = channelProvider.getChannel(); + + HttpResponseHandler responseHandler = new HttpResponseHandler(); + HttpResponseStreamInboundHandler streamHandler = new HttpResponseStreamInboundHandler(); + + channel.pipeline().addLast(responseHandler); + channel.pipeline().addLast(streamHandler); + + sendRequestAndHandleResponse(requestProvider, channel, responseHandler); + + return streamHandler.getInputStream(); + } + + private HttpRequestProvider httpPostRequestProvider(final Object entity) { + return new HttpRequestProvider() { + @Override + public HttpRequest getHttpRequest(String uri) { + return preparePostRequest(uri, entity); + } + }; + } + + public void delete() { + + HttpRequestProvider requestProvider = httpDeleteRequestProvider(); + + HttpResponseHandler responseHandler = new HttpResponseHandler(); + + Channel channel = channelProvider.getChannel(); + + channel.pipeline().addLast(responseHandler); + + sendRequestAndHandleResponse(requestProvider, channel, responseHandler); + } - return request; - } + private HttpRequestProvider httpDeleteRequestProvider() { + return new HttpRequestProvider() { + @Override + public HttpRequest getHttpRequest(String uri) { + return prepareDeleteRequest(uri); + } + }; + } + public T get(TypeReference typeReference) { + Class clazz = (Class) typeReference.getType().getClass(); + return null; + } } diff --git a/src/main/java/com/github/dockerjava/netty/MediaType.java b/src/main/java/com/github/dockerjava/netty/MediaType.java index 347b9de72..e3fe18c3f 100644 --- a/src/main/java/com/github/dockerjava/netty/MediaType.java +++ b/src/main/java/com/github/dockerjava/netty/MediaType.java @@ -2,14 +2,15 @@ public enum MediaType { - APPLICATION_JSON("application/json"); - + APPLICATION_JSON("application/json"), + APPLICATION_OCTET_STREAM("application/octet-stream"); + private String mediaType; - + private MediaType(String mediaType) { this.mediaType = mediaType; } - + public String getMediaType() { return mediaType; } diff --git a/src/main/java/com/github/dockerjava/netty/exec/AbstrAsyncDockerCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/AbstrAsyncDockerCmdExec.java new file mode 100644 index 000000000..cb2b523c4 --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/exec/AbstrAsyncDockerCmdExec.java @@ -0,0 +1,67 @@ +package com.github.dockerjava.netty.exec; + +import com.github.dockerjava.api.async.ResultCallback; +import com.github.dockerjava.api.command.AsyncDockerCmd; +import com.github.dockerjava.api.command.DockerCmdAsyncExec; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.jaxrs.async.AbstractCallbackNotifier; +import com.github.dockerjava.netty.WebTarget; + +import java.io.Closeable; +import java.io.IOException; + +public abstract class AbstrAsyncDockerCmdExec, A_RES_T> extends + AbstrDockerCmdExec implements DockerCmdAsyncExec { + + public AbstrAsyncDockerCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + public Void exec(CMD_T command, ResultCallback resultCallback) { + return execute(command, resultCallback); + } + + protected final Void execute(final CMD_T command, final ResultCallback resultCallback) { + + ResultCallback delegatingResultCallback = new ResultCallback() { + + @Override + public void close() throws IOException { + resultCallback.close(); + command.close(); + } + + @Override + public void onStart(Closeable closeable) { + resultCallback.onStart(closeable); + } + + @Override + public void onNext(A_RES_T object) { + resultCallback.onNext(object); + } + + @Override + public void onError(Throwable throwable) { + resultCallback.onError(throwable); + } + + @Override + public void onComplete() { + resultCallback.onComplete(); + command.close(); + } + }; + + execute0(command, delegatingResultCallback); + + return null; + } + + protected abstract Void execute0(final CMD_T command, final ResultCallback resultCallback); + + + + +} diff --git a/src/main/java/com/github/dockerjava/netty/AbstrDockerCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/AbstrDockerCmdExec.java similarity index 96% rename from src/main/java/com/github/dockerjava/netty/AbstrDockerCmdExec.java rename to src/main/java/com/github/dockerjava/netty/exec/AbstrDockerCmdExec.java index 3d0308d57..a1d54bd27 100644 --- a/src/main/java/com/github/dockerjava/netty/AbstrDockerCmdExec.java +++ b/src/main/java/com/github/dockerjava/netty/exec/AbstrDockerCmdExec.java @@ -1,10 +1,12 @@ -package com.github.dockerjava.netty; +package com.github.dockerjava.netty.exec; import com.fasterxml.jackson.databind.ObjectMapper; import com.github.dockerjava.api.model.AuthConfig; import com.github.dockerjava.api.model.AuthConfigurations; import com.github.dockerjava.core.DockerClientConfig; import com.github.dockerjava.core.RemoteApiVersion; +import com.github.dockerjava.netty.WebTarget; + import org.apache.commons.codec.binary.Base64; import java.io.IOException; diff --git a/src/main/java/com/github/dockerjava/netty/AbstrSyncDockerCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/AbstrSyncDockerCmdExec.java similarity index 92% rename from src/main/java/com/github/dockerjava/netty/AbstrSyncDockerCmdExec.java rename to src/main/java/com/github/dockerjava/netty/exec/AbstrSyncDockerCmdExec.java index 43854dbd1..b67eac37a 100644 --- a/src/main/java/com/github/dockerjava/netty/AbstrSyncDockerCmdExec.java +++ b/src/main/java/com/github/dockerjava/netty/exec/AbstrSyncDockerCmdExec.java @@ -1,9 +1,10 @@ -package com.github.dockerjava.netty; +package com.github.dockerjava.netty.exec; import com.github.dockerjava.api.DockerException; import com.github.dockerjava.api.command.DockerCmd; import com.github.dockerjava.api.command.DockerCmdSyncExec; import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.netty.WebTarget; public abstract class AbstrSyncDockerCmdExec, RES_T> extends AbstrDockerCmdExec diff --git a/src/main/java/com/github/dockerjava/netty/exec/AuthCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/AuthCmdExec.java new file mode 100644 index 000000000..ed6c9cb7a --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/exec/AuthCmdExec.java @@ -0,0 +1,34 @@ +package com.github.dockerjava.netty.exec; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.command.AuthCmd; +import com.github.dockerjava.api.model.AuthResponse; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.netty.MediaType; +import com.github.dockerjava.netty.WebTarget; + +public class AuthCmdExec extends AbstrSyncDockerCmdExec implements AuthCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(AuthCmdExec.class); + + public AuthCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + protected AuthResponse execute(AuthCmd command) { + WebTarget webResource = getBaseResource().path("/auth"); + LOGGER.trace("POST: {}", webResource); + return webResource.request().accept(MediaType.APPLICATION_JSON) + .post(command.getAuthConfig(), AuthResponse.class); + +// if (response.getStatus() == 401) { +// throw new UnauthorizedException("Unauthorized"); +// } +// +// return response.readEntity(AuthResponse.class); + } + +} diff --git a/src/main/java/com/github/dockerjava/netty/exec/CopyFileFromContainerCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/CopyFileFromContainerCmdExec.java new file mode 100644 index 000000000..338b9b973 --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/exec/CopyFileFromContainerCmdExec.java @@ -0,0 +1,33 @@ +package com.github.dockerjava.netty.exec; + +import java.io.InputStream; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.command.CopyFileFromContainerCmd; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.netty.MediaType; +import com.github.dockerjava.netty.WebTarget; + +public class CopyFileFromContainerCmdExec extends AbstrSyncDockerCmdExec + implements CopyFileFromContainerCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(CopyFileFromContainerCmdExec.class); + + public CopyFileFromContainerCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + protected InputStream execute(CopyFileFromContainerCmd command) { + WebTarget webResource = getBaseResource().path("/containers/{id}/copy").resolveTemplate("id", + command.getContainerId()); + + LOGGER.trace("POST: " + webResource.toString()); + + return webResource.request().accept(MediaType.APPLICATION_OCTET_STREAM) + .post(command); + } + +} diff --git a/src/main/java/com/github/dockerjava/netty/exec/CreateContainerCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/CreateContainerCmdExec.java new file mode 100644 index 000000000..bf858349b --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/exec/CreateContainerCmdExec.java @@ -0,0 +1,35 @@ +package com.github.dockerjava.netty.exec; + +import com.github.dockerjava.api.command.CreateContainerCmd; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.command.CreateContainerCmd.Exec; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.netty.MediaType; +import com.github.dockerjava.netty.WebTarget; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CreateContainerCmdExec extends AbstrSyncDockerCmdExec + implements CreateContainerCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(CreateContainerCmdExec.class); + + public CreateContainerCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + protected CreateContainerResponse execute(CreateContainerCmd command) { + WebTarget webResource = getBaseResource().path("/containers/create"); + + if (command.getName() != null) { + webResource = webResource.queryParam("name", command.getName()); + } + + LOGGER.trace("POST: {} ", webResource); + return webResource.request().accept(MediaType.APPLICATION_JSON) + .post(command, CreateContainerResponse.class); + } + +} diff --git a/src/main/java/com/github/dockerjava/netty/ExecCreateCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/ExecCreateCmdExec.java similarity index 78% rename from src/main/java/com/github/dockerjava/netty/ExecCreateCmdExec.java rename to src/main/java/com/github/dockerjava/netty/exec/ExecCreateCmdExec.java index 10e5ad9d8..78f788f3f 100644 --- a/src/main/java/com/github/dockerjava/netty/ExecCreateCmdExec.java +++ b/src/main/java/com/github/dockerjava/netty/exec/ExecCreateCmdExec.java @@ -1,9 +1,12 @@ -package com.github.dockerjava.netty; +package com.github.dockerjava.netty.exec; import com.github.dockerjava.api.command.ExecCreateCmd; import com.github.dockerjava.api.command.ExecCreateCmdResponse; +import com.github.dockerjava.api.command.ExecCreateCmd.Exec; import com.github.dockerjava.core.DockerClientConfig; -import com.github.dockerjava.netty.AbstrSyncDockerCmdExec; +import com.github.dockerjava.netty.MediaType; +import com.github.dockerjava.netty.WebTarget; +import com.github.dockerjava.netty.exec.AbstrSyncDockerCmdExec; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/com/github/dockerjava/netty/exec/ExecStartCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/ExecStartCmdExec.java new file mode 100644 index 000000000..9d684ca7b --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/exec/ExecStartCmdExec.java @@ -0,0 +1,29 @@ +package com.github.dockerjava.netty.exec; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.async.ResultCallback; +import com.github.dockerjava.api.command.ExecStartCmd; +import com.github.dockerjava.api.model.Frame; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.netty.MediaType; +import com.github.dockerjava.netty.WebTarget; + +public class ExecStartCmdExec extends AbstrAsyncDockerCmdExec implements ExecStartCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(ExecStartCmdExec.class); + + public ExecStartCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + protected Void execute0(ExecStartCmd command, ResultCallback resultCallback) { + WebTarget webTarget = getBaseResource().path("/exec/{id}/start").resolveTemplate("id", command.getExecId()); + + webTarget.request().accept(MediaType.APPLICATION_JSON).post(command, System.in, resultCallback); + + return null; + } +} diff --git a/src/main/java/com/github/dockerjava/netty/InfoCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/InfoCmdExec.java similarity index 91% rename from src/main/java/com/github/dockerjava/netty/InfoCmdExec.java rename to src/main/java/com/github/dockerjava/netty/exec/InfoCmdExec.java index 04abf103f..5a41073a4 100644 --- a/src/main/java/com/github/dockerjava/netty/InfoCmdExec.java +++ b/src/main/java/com/github/dockerjava/netty/exec/InfoCmdExec.java @@ -1,4 +1,4 @@ -package com.github.dockerjava.netty; +package com.github.dockerjava.netty.exec; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -6,13 +6,14 @@ import com.github.dockerjava.api.command.InfoCmd; import com.github.dockerjava.api.model.Info; import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.netty.WebTarget; /** * http://stackoverflow.com/questions/33296749/netty-connect-to-unix-domain- * socket-failed http://netty.io/wiki/native-transports.html * https://github.com/netty/netty/blob/master/example/src/main/java/io/netty/ * example/http/snoop/HttpSnoopClient.java - * + * * @author marcus * */ @@ -23,7 +24,7 @@ public class InfoCmdExec implements InfoCmd.Exec { private WebTarget webResource; private DockerClientConfig dockerClientConfig; - + public InfoCmdExec(WebTarget webResource, DockerClientConfig dockerClientConfig) { this.webResource = webResource; this.dockerClientConfig = dockerClientConfig; @@ -36,7 +37,7 @@ public Info exec(InfoCmd command) { } - - + + } diff --git a/src/main/java/com/github/dockerjava/netty/exec/InspectContainerCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/InspectContainerCmdExec.java new file mode 100644 index 000000000..6490ddd7f --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/exec/InspectContainerCmdExec.java @@ -0,0 +1,31 @@ +package com.github.dockerjava.netty.exec; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.github.dockerjava.api.command.InspectContainerCmd; +import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.netty.MediaType; +import com.github.dockerjava.netty.WebTarget; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class InspectContainerCmdExec extends AbstrSyncDockerCmdExec + implements InspectContainerCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(InspectContainerCmdExec.class); + + public InspectContainerCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + protected InspectContainerResponse execute(InspectContainerCmd command) { + WebTarget webResource = getBaseResource().path("/containers/{id}/json").resolveTemplate("id", + command.getContainerId()); + + LOGGER.debug("GET: {}", webResource); + return webResource.request().accept(MediaType.APPLICATION_JSON).get(InspectContainerResponse.class); + } + +} diff --git a/src/main/java/com/github/dockerjava/netty/exec/InspectExecCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/InspectExecCmdExec.java new file mode 100644 index 000000000..470df9c2c --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/exec/InspectExecCmdExec.java @@ -0,0 +1,26 @@ +package com.github.dockerjava.netty.exec; + +import com.github.dockerjava.api.command.InspectExecCmd; +import com.github.dockerjava.api.command.InspectExecResponse; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.netty.MediaType; +import com.github.dockerjava.netty.WebTarget; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class InspectExecCmdExec extends AbstrSyncDockerCmdExec implements + InspectExecCmd.Exec { + private static final Logger LOGGER = LoggerFactory.getLogger(InspectExecCmdExec.class); + + public InspectExecCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + protected InspectExecResponse execute(InspectExecCmd command) { + WebTarget webResource = getBaseResource().path("/exec/{id}/json").resolveTemplate("id", command.getExecId()); + LOGGER.debug("GET: {}", webResource); + return webResource.request().accept(MediaType.APPLICATION_JSON).get(InspectExecResponse.class); + } +} diff --git a/src/main/java/com/github/dockerjava/netty/exec/InspectImageCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/InspectImageCmdExec.java new file mode 100644 index 000000000..0092b5ff9 --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/exec/InspectImageCmdExec.java @@ -0,0 +1,31 @@ +package com.github.dockerjava.netty.exec; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.github.dockerjava.api.command.InspectImageCmd; +import com.github.dockerjava.api.command.InspectImageResponse; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.netty.MediaType; +import com.github.dockerjava.netty.WebTarget; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class InspectImageCmdExec extends AbstrSyncDockerCmdExec implements + InspectImageCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(InspectImageCmdExec.class); + + public InspectImageCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + protected InspectImageResponse execute(InspectImageCmd command) { + WebTarget webResource = getBaseResource().path("/images/{id}/json").resolveTemplate("id", command.getImageId()); + + LOGGER.trace("GET: {}", webResource); + return webResource.request().accept(MediaType.APPLICATION_JSON).get(new TypeReference() { + }); + } + +} diff --git a/src/main/java/com/github/dockerjava/netty/exec/KillContainerCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/KillContainerCmdExec.java new file mode 100644 index 000000000..bc56c51fc --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/exec/KillContainerCmdExec.java @@ -0,0 +1,35 @@ +package com.github.dockerjava.netty.exec; + +import com.github.dockerjava.api.command.KillContainerCmd; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.netty.MediaType; +import com.github.dockerjava.netty.WebTarget; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class KillContainerCmdExec extends AbstrSyncDockerCmdExec implements + KillContainerCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(KillContainerCmdExec.class); + + public KillContainerCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + protected Void execute(KillContainerCmd command) { + WebTarget webResource = getBaseResource().path("/containers/{id}/kill").resolveTemplate("id", + command.getContainerId()); + + if (command.getSignal() != null) { + webResource = webResource.queryParam("signal", command.getSignal()); + } + + LOGGER.trace("POST: {}", webResource); + webResource.request().accept(MediaType.APPLICATION_JSON).post(null); + + return null; + } + +} diff --git a/src/main/java/com/github/dockerjava/netty/exec/ListContainersCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/ListContainersCmdExec.java new file mode 100644 index 000000000..0f35c84e3 --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/exec/ListContainersCmdExec.java @@ -0,0 +1,53 @@ +package com.github.dockerjava.netty.exec; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.github.dockerjava.api.command.ListContainersCmd; +import com.github.dockerjava.api.model.Container; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.netty.MediaType; +import com.github.dockerjava.netty.WebTarget; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +import javax.ws.rs.core.GenericType; + +import static com.google.common.net.UrlEscapers.urlPathSegmentEscaper; + +public class ListContainersCmdExec extends AbstrSyncDockerCmdExec> implements + ListContainersCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(ListContainersCmdExec.class); + + public ListContainersCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + protected List execute(ListContainersCmd command) { + WebTarget webTarget = getBaseResource().path("/containers/json").queryParam("since", command.getSinceId()) + .queryParam("before", command.getBeforeId()); + + webTarget = booleanQueryParam(webTarget, "all", command.hasShowAllEnabled()); + webTarget = booleanQueryParam(webTarget, "size", command.hasShowSizeEnabled()); + + if (command.getLimit() != null && command.getLimit() >= 0) { + webTarget = webTarget.queryParam("limit", String.valueOf(command.getLimit())); + } + + if (command.getFilters() != null) { + webTarget = webTarget + .queryParam("filters", urlPathSegmentEscaper().escape(command.getFilters().toString())); + } + + LOGGER.trace("GET: {}", webTarget); + List containers = webTarget.request().accept(MediaType.APPLICATION_JSON) + .get(new TypeReference>() {}); + LOGGER.trace("Response: {}", containers); + + return containers; + } + +} diff --git a/src/main/java/com/github/dockerjava/netty/exec/LogContainerCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/LogContainerCmdExec.java new file mode 100644 index 000000000..4135f7308 --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/exec/LogContainerCmdExec.java @@ -0,0 +1,48 @@ +package com.github.dockerjava.netty.exec; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.async.ResultCallback; +import com.github.dockerjava.api.command.LogContainerCmd; +import com.github.dockerjava.api.model.Frame; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.netty.WebTarget; + + +public class LogContainerCmdExec extends AbstrAsyncDockerCmdExec implements + LogContainerCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(LogContainerCmdExec.class); + + public LogContainerCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + protected Void execute0(LogContainerCmd command, ResultCallback resultCallback) { + + WebTarget webTarget = getBaseResource().path("/containers/{id}/logs").resolveTemplate("id", + command.getContainerId()); + + if (command.getTail() != null) { + webTarget = webTarget.queryParam("tail", command.getTail()); + } + + if (command.getSince() != null) { + webTarget = webTarget.queryParam("since", command.getSince()); + } + + webTarget = booleanQueryParam(webTarget, "timestamps", command.hasTimestampsEnabled()); + webTarget = booleanQueryParam(webTarget, "stdout", command.hasStdoutEnabled()); + webTarget = booleanQueryParam(webTarget, "stderr", command.hasStderrEnabled()); + webTarget = booleanQueryParam(webTarget, "follow", command.hasFollowStreamEnabled()); + + LOGGER.trace("GET: {}", webTarget); + + webTarget.request().get(resultCallback); + + return null; + } + +} diff --git a/src/main/java/com/github/dockerjava/netty/exec/RemoveContainerCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/RemoveContainerCmdExec.java new file mode 100644 index 000000000..ff4a2240e --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/exec/RemoveContainerCmdExec.java @@ -0,0 +1,33 @@ +package com.github.dockerjava.netty.exec; + +import com.github.dockerjava.api.command.RemoveContainerCmd; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.netty.MediaType; +import com.github.dockerjava.netty.WebTarget; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RemoveContainerCmdExec extends AbstrSyncDockerCmdExec implements + RemoveContainerCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(RemoveContainerCmdExec.class); + + public RemoveContainerCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + protected Void execute(RemoveContainerCmd command) { + WebTarget webTarget = getBaseResource().path("/containers/" + command.getContainerId()); + + webTarget = booleanQueryParam(webTarget, "v", command.hasRemoveVolumesEnabled()); + webTarget = booleanQueryParam(webTarget, "force", command.hasForceEnabled()); + + LOGGER.trace("DELETE: {}", webTarget); + webTarget.request().accept(MediaType.APPLICATION_JSON).delete(); + + return null; + } + +} diff --git a/src/main/java/com/github/dockerjava/netty/exec/StartContainerCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/StartContainerCmdExec.java new file mode 100644 index 000000000..cfa7d28ce --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/exec/StartContainerCmdExec.java @@ -0,0 +1,33 @@ +package com.github.dockerjava.netty.exec; + +import com.github.dockerjava.api.command.StartContainerCmd; +import com.github.dockerjava.api.command.StartContainerCmd.Exec; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.netty.MediaType; +import com.github.dockerjava.netty.WebTarget; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +public class StartContainerCmdExec extends AbstrSyncDockerCmdExec implements + StartContainerCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(StartContainerCmdExec.class); + + public StartContainerCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + protected Void execute(StartContainerCmd command) { + WebTarget webResource = getBaseResource().path("/containers/{id}/start").resolveTemplate("id", + command.getContainerId()); + + LOGGER.trace("POST: {}", webResource); + webResource.request().accept(MediaType.APPLICATION_JSON).post(command); + + return null; + } + +} diff --git a/src/main/java/com/github/dockerjava/netty/exec/StopContainerCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/StopContainerCmdExec.java new file mode 100644 index 000000000..9e25d1c8d --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/exec/StopContainerCmdExec.java @@ -0,0 +1,36 @@ +package com.github.dockerjava.netty.exec; + +import com.github.dockerjava.api.command.StopContainerCmd; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.netty.MediaType; +import com.github.dockerjava.netty.WebTarget; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + + +public class StopContainerCmdExec extends AbstrSyncDockerCmdExec implements + StopContainerCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(StopContainerCmdExec.class); + + public StopContainerCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + protected Void execute(StopContainerCmd command) { + WebTarget webResource = getBaseResource().path("/containers/{id}/stop").resolveTemplate("id", + command.getContainerId()); + + if (command.getTimeout() != null) { + webResource = webResource.queryParam("t", String.valueOf(command.getTimeout())); + } + + LOGGER.trace("POST: {}", webResource); + webResource.request().accept(MediaType.APPLICATION_JSON).post(null); + + return null; + } +} diff --git a/src/main/java/com/github/dockerjava/netty/exec/WaitContainerCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/WaitContainerCmdExec.java new file mode 100644 index 000000000..f4f0076f3 --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/exec/WaitContainerCmdExec.java @@ -0,0 +1,36 @@ +package com.github.dockerjava.netty.exec; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.async.ResultCallback; +import com.github.dockerjava.api.command.WaitContainerCmd; +import com.github.dockerjava.api.model.WaitResponse; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.core.async.JsonStreamProcessor; +import com.github.dockerjava.netty.MediaType; +import com.github.dockerjava.netty.WebTarget; + + +public class WaitContainerCmdExec extends AbstrAsyncDockerCmdExec implements + WaitContainerCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(WaitContainerCmdExec.class); + + public WaitContainerCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + protected Void execute0(WaitContainerCmd command, ResultCallback resultCallback) { + WebTarget webTarget = getBaseResource().path("/containers/{id}/wait").resolveTemplate("id", + command.getContainerId()); + + LOGGER.trace("POST: {}", webTarget); + + webTarget.request().accept(MediaType.APPLICATION_JSON).post(null, WaitResponse.class); + + return null; + } + +} diff --git a/src/main/java/com/github/dockerjava/netty/handler/AwaitObjectInboundHandler.java b/src/main/java/com/github/dockerjava/netty/handler/AwaitObjectInboundHandler.java index 806e76d31..543fc095b 100644 --- a/src/main/java/com/github/dockerjava/netty/handler/AwaitObjectInboundHandler.java +++ b/src/main/java/com/github/dockerjava/netty/handler/AwaitObjectInboundHandler.java @@ -1,39 +1,39 @@ package com.github.dockerjava.netty.handler; -import java.util.concurrent.Callable; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.FutureTask; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.TimeUnit; - import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; +import java.util.concurrent.CountDownLatch; + public class AwaitObjectInboundHandler extends SimpleChannelInboundHandler { - - public AwaitObjectInboundHandler(Class clazz) { - super(clazz); - } - - private CountDownLatch countDownLatch = new CountDownLatch(1); - - private T object; - - @Override - protected void channelRead0(ChannelHandlerContext ctx, T msg) throws Exception { - object = msg; - countDownLatch.countDown(); - } - - public T await() throws InterruptedException { - countDownLatch.await(); - return object; - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - cause.printStackTrace(); - ctx.close(); - } + + public AwaitObjectInboundHandler(Class clazz) { + super(clazz); + } + + private CountDownLatch countDownLatch = new CountDownLatch(1); + + private T object; + + @Override + protected void channelRead0(ChannelHandlerContext ctx, T msg) throws Exception { + object = msg; + countDownLatch.countDown(); + } + + public T await() { + try { + countDownLatch.await(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + return object; + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + cause.printStackTrace(); + ctx.close(); + } } diff --git a/src/main/java/com/github/dockerjava/netty/handler/DeserializeJsonInboundHandler.java b/src/main/java/com/github/dockerjava/netty/handler/DeserializeJsonInboundHandler.java index d2a23988d..994169a36 100644 --- a/src/main/java/com/github/dockerjava/netty/handler/DeserializeJsonInboundHandler.java +++ b/src/main/java/com/github/dockerjava/netty/handler/DeserializeJsonInboundHandler.java @@ -1,5 +1,6 @@ package com.github.dockerjava.netty.handler; +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import io.netty.buffer.ByteBuf; @@ -7,13 +8,13 @@ import io.netty.channel.SimpleChannelInboundHandler; public class DeserializeJsonInboundHandler extends SimpleChannelInboundHandler{ - + private Class clazz; - + public DeserializeJsonInboundHandler(Class clazz) { this.clazz = clazz; } - + @Override protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { byte[] buffer = new byte[msg.readableBytes()]; diff --git a/src/main/java/com/github/dockerjava/netty/handler/DockerRawStreamHandler.java b/src/main/java/com/github/dockerjava/netty/handler/FramedResponseStreamHandler.java similarity index 76% rename from src/main/java/com/github/dockerjava/netty/handler/DockerRawStreamHandler.java rename to src/main/java/com/github/dockerjava/netty/handler/FramedResponseStreamHandler.java index e7f7e3377..7907a62b5 100644 --- a/src/main/java/com/github/dockerjava/netty/handler/DockerRawStreamHandler.java +++ b/src/main/java/com/github/dockerjava/netty/handler/FramedResponseStreamHandler.java @@ -1,34 +1,32 @@ package com.github.dockerjava.netty.handler; -import java.io.OutputStream; - -import com.github.dockerjava.api.model.Frame; -import com.github.dockerjava.api.model.StreamType; - import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; -public class DockerRawStreamHandler extends SimpleChannelInboundHandler { +import com.github.dockerjava.api.async.ResultCallback; +import com.github.dockerjava.api.model.Frame; +import com.github.dockerjava.api.model.StreamType; + +public class FramedResponseStreamHandler extends SimpleChannelInboundHandler { private static final int HEADER_SIZE = 8; private final ByteBuf rawBuffer = Unpooled.buffer(1000); - private OutputStream stdout, stderr; - private byte[] header = new byte[HEADER_SIZE]; private int headerCnt = 0; - + private byte[] payload = new byte[0]; private int payloadCnt = 0; - public DockerRawStreamHandler(OutputStream stdout, OutputStream stderr) { - this.stdout = stdout; - this.stderr = stderr; + private ResultCallback resultCallback; + + public FramedResponseStreamHandler(ResultCallback resultCallback) { + this.resultCallback = resultCallback; } @Override @@ -40,29 +38,13 @@ protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Excep do { frame = decode(); - + if (frame != null) { - switch (frame.getStreamType()) { - case STDOUT: - if (stdout != null) { - stdout.write(frame.getPayload()); - stdout.flush(); - } - break; - case STDERR: - if (stderr != null) { - stderr.write(frame.getPayload()); - stderr.flush(); - } - break; - default: - System.err.println("unknown stream type"); - } - + resultCallback.onNext(frame); headerCnt = 0; payloadCnt = 0; - } - + } + } while (frame != null); } @@ -111,7 +93,7 @@ private Frame decode() { @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - cause.printStackTrace(); + resultCallback.onError(cause); ctx.close(); } diff --git a/src/main/java/com/github/dockerjava/netty/handler/HijackHttpConnectionHandler.java b/src/main/java/com/github/dockerjava/netty/handler/HttpConnectionHijackHandler.java similarity index 76% rename from src/main/java/com/github/dockerjava/netty/handler/HijackHttpConnectionHandler.java rename to src/main/java/com/github/dockerjava/netty/handler/HttpConnectionHijackHandler.java index 1a4a6673b..1a59118fb 100644 --- a/src/main/java/com/github/dockerjava/netty/handler/HijackHttpConnectionHandler.java +++ b/src/main/java/com/github/dockerjava/netty/handler/HttpConnectionHijackHandler.java @@ -13,23 +13,23 @@ import io.netty.handler.codec.http.HttpClientUpgradeHandler; import io.netty.handler.codec.http.HttpRequest; -public class HijackHttpConnectionHandler implements HttpClientUpgradeHandler.UpgradeCodec { - +public class HttpConnectionHijackHandler implements HttpClientUpgradeHandler.UpgradeCodec { + private CountDownLatch latch = new CountDownLatch(1); - + + private HttpResponseHandler httpResponseHandler; + + public HttpConnectionHijackHandler(HttpResponseHandler httpResponseHandler) { + this.httpResponseHandler = httpResponseHandler; + } + @Override public void upgradeTo(ChannelHandlerContext ctx, FullHttpResponse upgradeResponse) throws Exception { System.out.println("UPGRADED"); + httpResponseHandler.channelRead(ctx, upgradeResponse); + ctx.pipeline().addLast(httpResponseHandler); latch.countDown(); -// ChannelHandler removed = ctx.pipeline().remove(HttpClientCodec.class); -// -// System.out.println("removed: " + removed); - - - - - } @Override @@ -42,7 +42,7 @@ public Collection setUpgradeHeaders(ChannelHandlerContext ctx, public CharSequence protocol() { return "tcp"; } - + public void await() { try { latch.await(); @@ -51,7 +51,7 @@ public void await() { throw new RuntimeException(e); } } - - + + } diff --git a/src/main/java/com/github/dockerjava/netty/handler/HttpResponseHandler.java b/src/main/java/com/github/dockerjava/netty/handler/HttpResponseHandler.java new file mode 100644 index 000000000..0b1007869 --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/handler/HttpResponseHandler.java @@ -0,0 +1,83 @@ +package com.github.dockerjava.netty.handler; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.handler.codec.http.HttpContent; +import io.netty.handler.codec.http.HttpObject; +import io.netty.handler.codec.http.HttpResponse; +import io.netty.handler.codec.http.LastHttpContent; + +import java.nio.charset.Charset; +import java.util.concurrent.CountDownLatch; + +public class HttpResponseHandler extends SimpleChannelInboundHandler { + + private HttpResponse response; + + private ByteBuf errorBody = Unpooled.buffer(); + + private CountDownLatch responseLatch = new CountDownLatch(1); + + private CountDownLatch errorBodyLatch = new CountDownLatch(1); + + public HttpResponseHandler() { + super(false); + } + + @Override + protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception { + if (msg instanceof HttpResponse) { + + response = (HttpResponse) msg; + responseLatch.countDown(); + + } else if (msg instanceof HttpContent) { + + HttpContent content = (HttpContent) msg; + + ByteBuf byteBuf = content.content(); + + switch (response.status().code()) { + case 200: + case 201: + case 204: + ctx.fireChannelRead(byteBuf); + break; + default: + errorBody.writeBytes(byteBuf); + } + + if (content instanceof LastHttpContent) { + errorBodyLatch.countDown(); + } + } else { + System.err.println("UNKNOWN"); + } + + } + + private String getBodyAsMessage(ByteBuf body) { + return body.readBytes(body.readableBytes()).toString(Charset.forName("UTF-8")); + } + + public HttpResponse awaitResponse() { + try { + responseLatch.await(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + return response; + } + + public String awaitErrorBody() { + try { + errorBodyLatch.await(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + return getBodyAsMessage(errorBody); + } + +} diff --git a/src/main/java/com/github/dockerjava/netty/handler/HttpResponseInboundHandler.java b/src/main/java/com/github/dockerjava/netty/handler/HttpResponseInboundHandler.java deleted file mode 100644 index 143e5dcf5..000000000 --- a/src/main/java/com/github/dockerjava/netty/handler/HttpResponseInboundHandler.java +++ /dev/null @@ -1,128 +0,0 @@ -package com.github.dockerjava.netty.handler; - -import java.nio.charset.Charset; - -import com.github.dockerjava.api.BadRequestException; -import com.github.dockerjava.api.ConflictException; -import com.github.dockerjava.api.DockerException; -import com.github.dockerjava.api.InternalServerErrorException; -import com.github.dockerjava.api.NotAcceptableException; -import com.github.dockerjava.api.NotFoundException; -import com.github.dockerjava.api.NotModifiedException; -import com.github.dockerjava.api.UnauthorizedException; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpObject; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.LastHttpContent; - -public class HttpResponseInboundHandler extends SimpleChannelInboundHandler { - - private HttpRequestProvider requestProvider; - - private HttpResponse response; - - private ByteBuf body = Unpooled.buffer(); - - public HttpResponseInboundHandler(HttpRequestProvider requestProvider) { - super(false); - this.requestProvider = requestProvider; - } - - @Override - protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception { - if (msg instanceof HttpResponse) { - HttpResponse response = (HttpResponse) msg; - - System.err.println("STATUS: " + response.status()); - System.err.println("VERSION: " + response.protocolVersion()); - System.err.println(); - - this.response = response; - - if (!response.headers().isEmpty()) { - for (CharSequence name : response.headers().names()) { - for (CharSequence value : response.headers().getAll(name)) { - System.err.println("HEADER: " + name + " = " + value); - } - } - System.err.println(); - } - - } else if (msg instanceof HttpContent) { - //System.err.println("Got http content"); - - HttpContent content = (HttpContent) msg; - - ByteBuf byteBuf = content.content(); - - switch (response.status().code()) { - case 200: - case 201: - case 204: - ctx.fireChannelRead(byteBuf); - break; - default: - body.writeBytes(byteBuf); - } - - if (content instanceof LastHttpContent) { - - try { - switch (response.status().code()) { - case 200: - case 201: - case 204: - - break; - case 301: - case 302: - if (response.headers().contains(HttpHeaderNames.LOCATION)) { - String location = response.headers().get(HttpHeaderNames.LOCATION); - System.out.println("redirected to :" + location); - HttpRequest redirected = requestProvider.getHttpRequest(location); - - ctx.channel().writeAndFlush(redirected); - } else { - ctx.fireExceptionCaught(new RuntimeException("missing 'location' header for redirect")); - } - break; - case 304: - throw new NotModifiedException(getBodyAsMessage(body)); - case 400: - throw new BadRequestException(getBodyAsMessage(body)); - case 401: - throw new UnauthorizedException(getBodyAsMessage(body)); - case 404: - throw new NotFoundException(getBodyAsMessage(body)); - case 406: - throw new NotAcceptableException(getBodyAsMessage(body)); - case 409: - throw new ConflictException(getBodyAsMessage(body)); - case 500: - throw new InternalServerErrorException(getBodyAsMessage(body)); - default: - throw new DockerException(getBodyAsMessage(body), response.status().code()); - } - } finally { - //ctx.close(); - } - - } - } else { - System.err.println("UNKNOWN"); - } - - } - - private String getBodyAsMessage(ByteBuf body) { - return body.readBytes(body.readableBytes()).toString(Charset.forName("UTF-8")); - } - -} diff --git a/src/main/java/com/github/dockerjava/netty/handler/HttpResponseStreamInboundHandler.java b/src/main/java/com/github/dockerjava/netty/handler/HttpResponseStreamInboundHandler.java index 854cb7b2a..2f9974c16 100644 --- a/src/main/java/com/github/dockerjava/netty/handler/HttpResponseStreamInboundHandler.java +++ b/src/main/java/com/github/dockerjava/netty/handler/HttpResponseStreamInboundHandler.java @@ -1,5 +1,9 @@ package com.github.dockerjava.netty.handler; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; + import java.io.IOException; import java.io.InputStream; import java.util.concurrent.CountDownLatch; @@ -7,87 +11,99 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -import org.apache.commons.io.HexDump; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; - public class HttpResponseStreamInboundHandler extends SimpleChannelInboundHandler { - private CountDownLatch latch = new CountDownLatch(1); - private HttpResponseInputStream stream = new HttpResponseInputStream(); - - @Override - protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { - - latch.countDown(); - stream.write(msg.copy()); - - //System.out.println("got data: " + msg.readableBytes()); - - if (msg.readableBytes() == 0) { - stream.close(); - } - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - cause.printStackTrace(); - ctx.close(); - } - - public InputStream getInputStream() { - try { - latch.await(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - return stream; - } - - public static class HttpResponseInputStream extends InputStream { - - private AtomicBoolean closed = new AtomicBoolean(false); - - private LinkedTransferQueue queue = new LinkedTransferQueue(); - - private ByteBuf current = null; - - public void write(ByteBuf byteBuf) { - queue.put(byteBuf); - } - - @Override - public void close() throws IOException { - closed.set(true); - super.close(); - } - - @Override - public int read() throws IOException { - if (closed.get()) - return -1; - - if (current == null || current.readableBytes() == 0) { - try { - current = queue.poll(10, TimeUnit.SECONDS); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } - - if(current != null && current.readableBytes() > 0) { - return current.readByte(); - } else { - return read(); - } - - - - - } - - } + private CountDownLatch latch = new CountDownLatch(1); + + private HttpResponseInputStream stream = new HttpResponseInputStream(); + + @Override + protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { + + latch.countDown(); + stream.write(msg.copy()); + } + + @Override + public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { + stream.close(); + super.channelReadComplete(ctx); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + cause.printStackTrace(); + ctx.close(); + } + + public InputStream getInputStream() { + try { + latch.await(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + return stream; + } + + public static class HttpResponseInputStream extends InputStream { + + private AtomicBoolean closed = new AtomicBoolean(false); + + private LinkedTransferQueue queue = new LinkedTransferQueue(); + + private ByteBuf current = null; + + public void write(ByteBuf byteBuf) { + queue.put(byteBuf); + } + + @Override + public void close() throws IOException { + closed.set(true); + super.close(); + } + + @Override + public int available() throws IOException { + poll(); + return readableBytes(); + } + + private int readableBytes() { + if (current != null) + return current.readableBytes(); + else + return 0; + } + + @Override + public int read() throws IOException { + + poll(); + + if (readableBytes() == 0) { + if (closed.get()) + return -1; + } + + if (current != null && current.readableBytes() > 0) { + return current.readByte(); + } else { + return read(); + } + + } + + private void poll() { + if (readableBytes() == 0) { + try { + current = queue.poll(50, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } + + } } diff --git a/src/test/java/com/github/dockerjava/core/DockerClientConfigTest.java b/src/test/java/com/github/dockerjava/core/DockerClientConfigTest.java index cfcf7f381..e66cfa57d 100644 --- a/src/test/java/com/github/dockerjava/core/DockerClientConfigTest.java +++ b/src/test/java/com/github/dockerjava/core/DockerClientConfigTest.java @@ -16,7 +16,7 @@ public class DockerClientConfigTest { public static final DockerClientConfig EXAMPLE_CONFIG = newExampleConfig(); private static DockerClientConfig newExampleConfig() { - return new DockerClientConfig(URI.create("http://foo"), "bar", "baz", "qux", "blam", "wham", "flam", "flum", + return new DockerClientConfig(URI.create("http://foo"), "bar", "baz", "qux", "blam", "wham", "flam", new LocalDirectorySSLConfig("flim")); } diff --git a/src/test/java/com/github/dockerjava/core/DockerClientImplTest.java b/src/test/java/com/github/dockerjava/core/DockerClientImplTest.java index 5f4ee3e67..c5a1e841c 100644 --- a/src/test/java/com/github/dockerjava/core/DockerClientImplTest.java +++ b/src/test/java/com/github/dockerjava/core/DockerClientImplTest.java @@ -10,7 +10,7 @@ public class DockerClientImplTest { @Test public void configuredInstanceAuthConfig() throws Exception { // given a config with null serverAddress - DockerClientConfig dockerClientConfig = new DockerClientConfig(null, null, "", "", "", null, null, null, null); + DockerClientConfig dockerClientConfig = new DockerClientConfig(null, null, "", "", "", null, null, null); DockerClientImpl dockerClient = DockerClientImpl.getInstance(dockerClientConfig); // when we get the auth config diff --git a/src/test/java/com/github/dockerjava/core/command/ExecStartCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/ExecStartCmdImplTest.java index 43aa49f0e..4ece3e8ed 100644 --- a/src/test/java/com/github/dockerjava/core/command/ExecStartCmdImplTest.java +++ b/src/test/java/com/github/dockerjava/core/command/ExecStartCmdImplTest.java @@ -54,7 +54,8 @@ public void execStart() throws Exception { ExecCreateCmdResponse execCreateCmdResponse = dockerClient.execCreateCmd(container.getId()) .withAttachStdout(true).withCmd("touch", "/execStartTest.log").exec(); - dockerClient.execStartCmd(execCreateCmdResponse.getId()).exec(); + dockerClient.execStartCmd(execCreateCmdResponse.getId()).exec( + new ExecStartResultCallback(System.out, System.err)); InputStream response = dockerClient.copyFileFromContainerCmd(container.getId(), "/execStartTest.log").exec(); Boolean bytesAvailable = response.available() > 0; @@ -79,7 +80,8 @@ public void execStartAttached() throws Exception { ExecCreateCmdResponse execCreateCmdResponse = dockerClient.execCreateCmd(container.getId()) .withAttachStdout(true).withCmd("touch", "/execStartTest.log").exec(); - dockerClient.execStartCmd(execCreateCmdResponse.getId()).withDetach(false).withTty(true).exec(); + dockerClient.execStartCmd(execCreateCmdResponse.getId()).withDetach(false).withTty(true) + .exec(new ExecStartResultCallback(System.out, System.err)); InputStream response = dockerClient.copyFileFromContainerCmd(container.getId(), "/execStartTest.log").exec(); Boolean bytesAvailable = response.available() > 0; diff --git a/src/test/java/com/github/dockerjava/core/command/InspectExecCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/InspectExecCmdImplTest.java index 744bbe82c..4f88b6317 100644 --- a/src/test/java/com/github/dockerjava/core/command/InspectExecCmdImplTest.java +++ b/src/test/java/com/github/dockerjava/core/command/InspectExecCmdImplTest.java @@ -67,25 +67,23 @@ public void inspectExecTest() throws IOException { assertThat(checkFileCmdCreateResponse.getId(), not(isEmptyString())); // Check that file does not exist - InputStream response1 = dockerClient.execStartCmd(container.getId()) - .withExecId(checkFileCmdCreateResponse.getId()).exec(); - asString(response1); // consume + dockerClient.execStartCmd(container.getId()) + .withExecId(checkFileCmdCreateResponse.getId()).exec(new ExecStartResultCallback(System.out, System.err)); + InspectExecResponse first = dockerClient.inspectExecCmd(checkFileCmdCreateResponse.getId()).exec(); assertThat(first.getExitCode(), is(1)); // Create the file - InputStream response2 = dockerClient.execStartCmd(container.getId()) - .withExecId(touchFileCmdCreateResponse.getId()).exec(); - asString(response2); + dockerClient.execStartCmd(container.getId()) + .withExecId(touchFileCmdCreateResponse.getId()).exec(new ExecStartResultCallback(System.out, System.err)); InspectExecResponse second = dockerClient.inspectExecCmd(touchFileCmdCreateResponse.getId()).exec(); assertThat(second.getExitCode(), is(0)); // Check that file does exist now - InputStream response3 = dockerClient.execStartCmd(container.getId()) - .withExecId(checkFileCmdCreateResponse.getId()).exec(); - asString(response3); + dockerClient.execStartCmd(container.getId()) + .withExecId(checkFileCmdCreateResponse.getId()).exec(new ExecStartResultCallback(System.out, System.err)); InspectExecResponse third = dockerClient.inspectExecCmd(checkFileCmdCreateResponse.getId()).exec(); assertThat(third.getExitCode(), is(0)); diff --git a/src/test/java/com/github/dockerjava/netty/AbstractDockerClientTest.java b/src/test/java/com/github/dockerjava/netty/AbstractDockerClientTest.java new file mode 100644 index 000000000..b9a914593 --- /dev/null +++ b/src/test/java/com/github/dockerjava/netty/AbstractDockerClientTest.java @@ -0,0 +1,217 @@ +package com.github.dockerjava.netty; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.StringWriter; +import java.lang.reflect.Method; +import java.net.DatagramSocket; +import java.net.ServerSocket; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.io.LineIterator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.Assert; +import org.testng.ITestResult; + +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.api.model.Frame; +import com.github.dockerjava.api.model.Volume; +import com.github.dockerjava.api.model.VolumeBind; +import com.github.dockerjava.core.DockerClientBuilder; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.core.TestDockerCmdExecFactory; +import com.github.dockerjava.core.command.BuildImageResultCallback; +import com.github.dockerjava.core.command.LogContainerResultCallback; +import com.github.dockerjava.core.command.PullImageResultCallback; +import com.google.common.base.Joiner; + +public abstract class AbstractDockerClientTest extends Assert { + + public static final Logger LOG = LoggerFactory.getLogger(AbstractDockerClientTest.class); + + private String apiVersion = "1.19"; + + protected DockerClient dockerClient; + + protected TestDockerCmdExecFactory dockerCmdExecFactory = new TestDockerCmdExecFactory( + new DockerCmdExecFactoryImpl()); + + public void beforeTest() throws Exception { + + LOG.info("======================= BEFORETEST ======================="); + LOG.info("Connecting to Docker server"); + dockerClient = DockerClientBuilder.getInstance(config()).withDockerCmdExecFactory(dockerCmdExecFactory).build(); + +// LOG.info("Pulling image 'busybox'"); +// +// // need to block until image is pulled completely +// dockerClient.pullImageCmd("busybox").withTag("latest").exec(new PullImageResultCallback()).awaitSuccess(); + + assertNotNull(dockerClient); + LOG.info("======================= END OF BEFORETEST =======================\n\n"); + } + + private DockerClientConfig config() { + return config(null); + } + + protected DockerClientConfig config(String password) { + DockerClientConfig.DockerClientConfigBuilder builder = DockerClientConfig.createDefaultConfigBuilder() + .withServerAddress("https://index.docker.io/v1/"); + if (password != null) { + builder = builder.withPassword(password); + } + + return builder.withVersion(apiVersion).build(); + } + + public void afterTest() { + LOG.info("======================= END OF AFTERTEST ======================="); + } + + public void beforeMethod(Method method) { + LOG.info(String.format("################################## STARTING %s ##################################", + method.getName())); + } + + public void afterMethod(ITestResult result) { + + for (String container : dockerCmdExecFactory.getContainerNames()) { + LOG.info("Cleaning up temporary container {}", container); + + try { + dockerClient.removeContainerCmd(container).withForce(true).exec(); + } catch (DockerException ignore) { + // ignore.printStackTrace(); + } + } + + for (String image : dockerCmdExecFactory.getImageNames()) { + LOG.info("Cleaning up temporary image with {}", image); + try { + dockerClient.removeImageCmd(image).withForce(true).exec(); + } catch (DockerException ignore) { + // ignore.printStackTrace(); + } + } + + LOG.info("################################## END OF {} ##################################\n", result.getName()); + } + + protected String asString(InputStream response) { + return consumeAsString(response); + } + + public static String consumeAsString(InputStream response) { + + StringWriter logwriter = new StringWriter(); + + try { + LineIterator itr = IOUtils.lineIterator(response, "UTF-8"); + + while (itr.hasNext()) { + String line = itr.next(); + logwriter.write(line + (itr.hasNext() ? "\n" : "")); + LOG.info("line: " + line); + } + response.close(); + + return logwriter.toString(); + } catch (IOException e) { + throw new RuntimeException(e); + } finally { + IOUtils.closeQuietly(response); + } + } + + // UTIL + + /** + * Checks to see if a specific port is available. + * + * @param port + * the port to check for availability + */ + public static Boolean available(int port) { + if (port < 1100 || port > 60000) { + throw new IllegalArgumentException("Invalid start port: " + port); + } + + ServerSocket ss = null; + DatagramSocket ds = null; + try { + ss = new ServerSocket(port); + ss.setReuseAddress(true); + ds = new DatagramSocket(port); + ds.setReuseAddress(true); + return true; + } catch (IOException ignored) { + } finally { + if (ds != null) { + ds.close(); + } + + if (ss != null) { + try { + ss.close(); + } catch (IOException e) { + /* should not be thrown */ + } + } + } + + return false; + } + + /** + * Asserts that {@link InspectContainerResponse#getVolumes()} (.Volumes) has {@link VolumeBind}s for + * the given {@link Volume}s + */ + public static void assertContainerHasVolumes(InspectContainerResponse inspectContainerResponse, + Volume... expectedVolumes) { + VolumeBind[] volumeBinds = inspectContainerResponse.getVolumes(); + LOG.info("Inspect .Volumes = [{}]", Joiner.on(", ").join(volumeBinds)); + + List volumes = new ArrayList(); + for (VolumeBind bind : volumeBinds) { + volumes.add(new Volume(bind.getContainerPath())); + } + assertThat(volumes, contains(expectedVolumes)); + } + + protected String containerLog(String containerId) throws Exception { + return dockerClient.logContainerCmd(containerId).withStdOut(true).exec(new LogContainerTestCallback()) + .awaitCompletion().toString(); + } + + public static class LogContainerTestCallback extends LogContainerResultCallback { + protected final StringBuffer log = new StringBuffer(); + + @Override + public void onNext(Frame frame) { + log.append(new String(frame.getPayload())); + super.onNext(frame); + } + + @Override + public String toString() { + return log.toString(); + } + } + + protected String buildImage(File baseDir) throws Exception { + + return dockerClient.buildImageCmd(baseDir).withNoCache(true).exec(new BuildImageResultCallback()) + .awaitImageId(); + } + +} diff --git a/src/test/java/com/github/dockerjava/netty/exec/AuthCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/AuthCmdExecTest.java new file mode 100644 index 000000000..3cadd6485 --- /dev/null +++ b/src/test/java/com/github/dockerjava/netty/exec/AuthCmdExecTest.java @@ -0,0 +1,57 @@ +package com.github.dockerjava.netty.exec; + +import java.lang.reflect.Method; + +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.UnauthorizedException; +import com.github.dockerjava.api.model.AuthResponse; +import com.github.dockerjava.core.DockerClientBuilder; +import com.github.dockerjava.netty.AbstractDockerClientTest; + +@Test(groups = "integration") +public class AuthCmdExecTest extends AbstractDockerClientTest { + + @BeforeTest + public void beforeTest() throws Exception { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test + public void testAuth() throws Exception { + AuthResponse response = dockerClient.authCmd().exec(); + + assertEquals(response.getStatus(), "Login Succeeded"); + } + + @Test() + public void testAuthInvalid() throws Exception { + + try { + DockerClientBuilder.getInstance(config("garbage")).build().authCmd().exec(); + fail("Expected a UnauthorizedException caused by a bad password."); + } catch (UnauthorizedException e) { + assertEquals(e.getMessage(), "Wrong login/password, please try again\n"); + } + } +} diff --git a/src/test/java/com/github/dockerjava/netty/exec/CreateContainerCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/CreateContainerCmdExecTest.java new file mode 100644 index 000000000..43400edfc --- /dev/null +++ b/src/test/java/com/github/dockerjava/netty/exec/CreateContainerCmdExecTest.java @@ -0,0 +1,560 @@ +package com.github.dockerjava.netty.exec; + +import static com.github.dockerjava.api.model.Capability.MKNOD; +import static com.github.dockerjava.api.model.Capability.NET_ADMIN; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasItemInArray; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.startsWith; + +import java.lang.reflect.Method; +import java.security.SecureRandom; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.ConflictException; +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.api.model.AccessMode; +import com.github.dockerjava.api.model.Bind; +import com.github.dockerjava.api.model.Device; +import com.github.dockerjava.api.model.ExposedPort; +import com.github.dockerjava.api.model.Link; +import com.github.dockerjava.api.model.LogConfig; +import com.github.dockerjava.api.model.Ports; +import com.github.dockerjava.api.model.RestartPolicy; +import com.github.dockerjava.api.model.Ulimit; +import com.github.dockerjava.api.model.Volume; +import com.github.dockerjava.api.model.VolumeRW; +import com.github.dockerjava.api.model.VolumesFrom; +import com.github.dockerjava.netty.AbstractDockerClientTest; + +@Test(groups = "integration") +public class CreateContainerCmdExecTest extends AbstractDockerClientTest { + + @BeforeTest + public void beforeTest() throws Exception { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test + public void createContainerWithExistingName() throws DockerException { + + String containerName = "generated_" + new SecureRandom().nextInt(); + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("env") + .withName(containerName).exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + try { + dockerClient.createContainerCmd("busybox").withCmd("env").withName(containerName).exec(); + fail("expected ConflictException"); + } catch (ConflictException e) { + } + } + + @Test + public void createContainerWithVolume() throws DockerException { + + Volume volume = new Volume("/var/log"); + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withVolumes(volume) + .withCmd("true").exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + + LOG.info("Inspect container {}", inspectContainerResponse.getConfig().getVolumes()); + + assertThat(inspectContainerResponse.getConfig().getVolumes().keySet(), contains("/var/log")); + + assertThat(inspectContainerResponse.getVolumesRW(), hasItemInArray(new VolumeRW(volume, AccessMode.rw))); + } + + @Test + public void createContainerWithReadOnlyVolume() throws DockerException { + + Volume volume = new Volume("/srv/test"); + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withVolumes(volume) + .withCmd("true").exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + + LOG.info("Inspect container {}", inspectContainerResponse.getConfig().getVolumes()); + + assertThat(inspectContainerResponse.getConfig().getVolumes().keySet(), contains("/srv/test")); + + assertThat(Arrays.asList(inspectContainerResponse.getVolumesRW()), contains(new VolumeRW(volume))); + } + + @Test + public void createContainerWithVolumesFrom() throws DockerException { + + Volume volume1 = new Volume("/opt/webapp1"); + Volume volume2 = new Volume("/opt/webapp2"); + + String container1Name = UUID.randomUUID().toString(); + + // create a running container with bind mounts + CreateContainerResponse container1 = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999") + .withName(container1Name) + .withBinds(new Bind("/src/webapp1", volume1), new Bind("/src/webapp2", volume2)).exec(); + LOG.info("Created container1 {}", container1.toString()); + + dockerClient.startContainerCmd(container1.getId()).exec(); + LOG.info("Started container1 {}", container1.toString()); + + InspectContainerResponse inspectContainerResponse1 = dockerClient.inspectContainerCmd(container1.getId()) + .exec(); + + assertContainerHasVolumes(inspectContainerResponse1, volume1, volume2); + + // create a second container with volumes from first container + CreateContainerResponse container2 = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999") + .withVolumesFrom(new VolumesFrom(container1Name)).exec(); + LOG.info("Created container2 {}", container2.toString()); + + InspectContainerResponse inspectContainerResponse2 = dockerClient.inspectContainerCmd(container2.getId()) + .exec(); + + // No volumes are created, the information is just stored in .HostConfig.VolumesFrom + assertThat(inspectContainerResponse2.getHostConfig().getVolumesFrom(), hasItemInArray(new VolumesFrom( + container1Name))); + + // To ensure that the information stored in VolumesFrom really is considered + // when starting the container, we start it and verify that it has the same + // bind mounts as the first container. + // This is somehow out of scope here, but it helped me to understand how the + // VolumesFrom feature really works. + dockerClient.startContainerCmd(container2.getId()).exec(); + LOG.info("Started container2 {}", container2.toString()); + + inspectContainerResponse2 = dockerClient.inspectContainerCmd(container2.getId()).exec(); + + assertThat(inspectContainerResponse2.getHostConfig().getVolumesFrom(), hasItemInArray(new VolumesFrom( + container1Name))); + assertContainerHasVolumes(inspectContainerResponse2, volume1, volume2); + } + + @Test + public void createContainerWithEnv() throws Exception { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withEnv("VARIABLE=success") + .withCmd("env").exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + + assertThat(Arrays.asList(inspectContainerResponse.getConfig().getEnv()), containsInAnyOrder("VARIABLE=success")); + + dockerClient.startContainerCmd(container.getId()).exec(); + + assertThat(containerLog(container.getId()), containsString("VARIABLE=success")); + } + + @Test + public void createContainerWithHostname() throws Exception { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withHostName("docker-java") + .withCmd("env").exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + + assertThat(inspectContainerResponse.getConfig().getHostName(), equalTo("docker-java")); + + dockerClient.startContainerCmd(container.getId()).exec(); + + assertThat(containerLog(container.getId()), containsString("HOSTNAME=docker-java")); + } + + @Test + public void createContainerWithName() throws DockerException { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withName("container") + .withCmd("env").exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + + assertThat(inspectContainerResponse.getName(), equalTo("/container")); + + try { + dockerClient.createContainerCmd("busybox").withName("container").withCmd("env").exec(); + fail("Expected ConflictException"); + } catch (ConflictException e) { + } + + } + + @Test + public void createContainerWithLink() throws DockerException { + + CreateContainerResponse container1 = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999") + .withName("container1").exec(); + LOG.info("Created container1 {}", container1.toString()); + assertThat(container1.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container1.getId()).exec(); + + InspectContainerResponse inspectContainerResponse1 = dockerClient.inspectContainerCmd(container1.getId()) + .exec(); + LOG.info("Container1 Inspect: {}", inspectContainerResponse1.toString()); + assertThat(inspectContainerResponse1.getState().isRunning(), is(true)); + + CreateContainerResponse container2 = dockerClient.createContainerCmd("busybox").withName("container2") + .withCmd("env").withLinks(new Link("container1", "container1Link")).exec(); + LOG.info("Created container {}", container2.toString()); + assertThat(container2.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse2 = dockerClient.inspectContainerCmd(container2.getId()) + .exec(); + assertThat(inspectContainerResponse2.getHostConfig().getLinks(), equalTo(new Link[] { new Link("container1", + "container1Link") })); + } + + @Test + public void createContainerWithCapAddAndCapDrop() throws DockerException { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCapAdd(NET_ADMIN) + .withCapDrop(MKNOD).exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + + assertThat(Arrays.asList(inspectContainerResponse.getHostConfig().getCapAdd()), contains(NET_ADMIN)); + + assertThat(Arrays.asList(inspectContainerResponse.getHostConfig().getCapDrop()), contains(MKNOD)); + } + + @Test + public void createContainerWithDns() throws DockerException { + + String aDnsServer = "8.8.8.8"; + String anotherDnsServer = "8.8.4.4"; + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("true") + .withDns(aDnsServer, anotherDnsServer).exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + + assertThat(Arrays.asList(inspectContainerResponse.getHostConfig().getDns()), + contains(aDnsServer, anotherDnsServer)); + } + + @Test + public void createContainerWithEntrypoint() throws DockerException { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withName("container") + .withEntrypoint("sleep", "9999").exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + + assertThat(Arrays.asList(inspectContainerResponse.getConfig().getEntrypoint()), contains("sleep", "9999")); + + } + + @Test + public void createContainerWithExtraHosts() throws DockerException { + + String[] extraHosts = { "dockerhost:127.0.0.1", "otherhost:10.0.0.1" }; + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withName("container") + .withExtraHosts(extraHosts).exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + + assertThat(Arrays.asList(inspectContainerResponse.getHostConfig().getExtraHosts()), + containsInAnyOrder("dockerhost:127.0.0.1", "otherhost:10.0.0.1")); + } + + @Test + public void createContainerWithDevices() throws DockerException { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999") + .withDevices(new Device("rwm", "/dev/nulo", "/dev/zero")).exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + + assertThat(Arrays.asList(inspectContainerResponse.getHostConfig().getDevices()), contains(new Device("rwm", + "/dev/nulo", "/dev/zero"))); + } + + @Test + public void createContainerWithPortBindings() throws DockerException { + + ExposedPort tcp22 = ExposedPort.tcp(22); + ExposedPort tcp23 = ExposedPort.tcp(23); + + Ports portBindings = new Ports(); + portBindings.bind(tcp22, Ports.Binding(11022)); + portBindings.bind(tcp23, Ports.Binding(11023)); + portBindings.bind(tcp23, Ports.Binding(11024)); + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("true") + .withExposedPorts(tcp22, tcp23).withPortBindings(portBindings).exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + + assertThat(Arrays.asList(inspectContainerResponse.getConfig().getExposedPorts()), contains(tcp22, tcp23)); + + assertThat(inspectContainerResponse.getHostConfig().getPortBindings().getBindings().get(tcp22)[0], + is(equalTo(Ports.Binding(11022)))); + + assertThat(inspectContainerResponse.getHostConfig().getPortBindings().getBindings().get(tcp23)[0], + is(equalTo(Ports.Binding(11023)))); + + assertThat(inspectContainerResponse.getHostConfig().getPortBindings().getBindings().get(tcp23)[1], + is(equalTo(Ports.Binding(11024)))); + + } + + @Test + public void createContainerWithLinking() throws DockerException { + + CreateContainerResponse container1 = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999") + .withName("container1").exec(); + + LOG.info("Created container1 {}", container1.toString()); + assertThat(container1.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container1.getId()).exec(); + + InspectContainerResponse inspectContainerResponse1 = dockerClient.inspectContainerCmd(container1.getId()) + .exec(); + LOG.info("Container1 Inspect: {}", inspectContainerResponse1.toString()); + + assertThat(inspectContainerResponse1.getConfig(), is(notNullValue())); + assertThat(inspectContainerResponse1.getId(), not(isEmptyString())); + assertThat(inspectContainerResponse1.getId(), startsWith(container1.getId())); + assertThat(inspectContainerResponse1.getName(), equalTo("/container1")); + assertThat(inspectContainerResponse1.getImageId(), not(isEmptyString())); + assertThat(inspectContainerResponse1.getState(), is(notNullValue())); + assertThat(inspectContainerResponse1.getState().isRunning(), is(true)); + + if (!inspectContainerResponse1.getState().isRunning()) { + assertThat(inspectContainerResponse1.getState().getExitCode(), is(equalTo(0))); + } + + CreateContainerResponse container2 = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999") + .withName("container2").withLinks(new Link("container1", "container1Link")).exec(); + + LOG.info("Created container2 {}", container2.toString()); + assertThat(container2.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse2 = dockerClient.inspectContainerCmd(container2.getId()) + .exec(); + LOG.info("Container2 Inspect: {}", inspectContainerResponse2.toString()); + + assertThat(inspectContainerResponse2.getConfig(), is(notNullValue())); + assertThat(inspectContainerResponse2.getId(), not(isEmptyString())); + assertThat(inspectContainerResponse2.getHostConfig(), is(notNullValue())); + assertThat(inspectContainerResponse2.getHostConfig().getLinks(), is(notNullValue())); + assertThat(inspectContainerResponse2.getHostConfig().getLinks(), equalTo(new Link[] { new Link("container1", + "container1Link") })); + assertThat(inspectContainerResponse2.getId(), startsWith(container2.getId())); + assertThat(inspectContainerResponse2.getName(), equalTo("/container2")); + assertThat(inspectContainerResponse2.getImageId(), not(isEmptyString())); + + } + + @Test + public void createContainerWithRestartPolicy() throws DockerException { + + RestartPolicy restartPolicy = RestartPolicy.onFailureRestart(5); + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999") + .withRestartPolicy(restartPolicy).exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + + assertThat(inspectContainerResponse.getHostConfig().getRestartPolicy(), is(equalTo(restartPolicy))); + } + + @Test + public void createContainerWithPidMode() throws DockerException { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("true") + .withPidMode("host").exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + + assertThat(inspectContainerResponse.getHostConfig().getPidMode(), is(equalTo("host"))); + } + + /** + * This tests support for --net option for the docker run command: --net="bridge" Set the Network mode for the + * container 'bridge': creates a new network stack for the container on the docker bridge 'none': no networking for + * this container 'container:': reuses another container network stack 'host': use the host network stack inside the + * container. Note: the host mode gives the container full access to local system services such as D-bus and is + * therefore considered insecure. + */ + @Test + public void createContainerWithNetworkMode() throws DockerException { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("true") + .withNetworkMode("host").exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + + assertThat(inspectContainerResponse.getHostConfig().getNetworkMode(), is(equalTo("host"))); + } + + @Test + public void createContainerWithMacAddress() throws DockerException { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox") + .withMacAddress("00:80:41:ae:fd:7e").withCmd("true").exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + + assertEquals(inspectContainerResponse.getConfig().getMacAddress(), "00:80:41:ae:fd:7e"); + } + + @Test(groups = "ignoreInCircleCi") + public void createContainerWithULimits() throws DockerException { + + Ulimit[] ulimits = { new Ulimit("nproc", 709, 1026), new Ulimit("nofile", 1024, 4096) }; + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withName("container") + .withUlimits(ulimits).exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + + assertThat(Arrays.asList(inspectContainerResponse.getHostConfig().getUlimits()), + containsInAnyOrder(new Ulimit("nproc", 709, 1026), new Ulimit("nofile", 1024, 4096))); + + } + + @Test(groups = "ignoreInCircleCi") + public void createContainerWithLabels() throws DockerException { + + Map labels = new HashMap(); + labels.put("com.github.dockerjava.null", null); + labels.put("com.github.dockerjava.Boolean", "true"); + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999") + .withLabels(labels).exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + + // null becomes empty string + labels.put("com.github.dockerjava.null", ""); + assertThat(inspectContainerResponse.getConfig().getLabels(), is(equalTo(labels))); + } + + @Test(groups = "ignoreInCircleCi") + public void createContainerWithLogConfig() throws DockerException { + + LogConfig logConfig = new LogConfig(LogConfig.LoggingType.NONE, null); + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withLogConfig(logConfig).exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + + // null becomes empty string + assertEquals(inspectContainerResponse.getHostConfig().getLogConfig().type, logConfig.type); + } +} diff --git a/src/test/java/com/github/dockerjava/netty/exec/ExecCreateCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/ExecCreateCmdExecTest.java new file mode 100644 index 000000000..9f95751c2 --- /dev/null +++ b/src/test/java/com/github/dockerjava/netty/exec/ExecCreateCmdExecTest.java @@ -0,0 +1,61 @@ +package com.github.dockerjava.netty.exec; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.Matchers.not; + +import java.lang.reflect.Method; +import java.security.SecureRandom; + +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.command.ExecCreateCmdResponse; +import com.github.dockerjava.netty.AbstractDockerClientTest; + +@Test(groups = "integration") +public class ExecCreateCmdExecTest extends AbstractDockerClientTest { + @BeforeTest + public void beforeTest() throws Exception { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test(groups = "ignoreInCircleCi") + public void execCreateTest() { + String containerName = "generated_" + new SecureRandom().nextInt(); + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("top") + .withName(containerName).exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container.getId()).exec(); + + ExecCreateCmdResponse execCreateCmdResponse = dockerClient.execCreateCmd(container.getId()) + .withCmd("touch", "file.log").exec(); + + assertThat(execCreateCmdResponse.getId(), not(isEmptyString())); + } +} diff --git a/src/test/java/com/github/dockerjava/netty/exec/ExecStartCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/ExecStartCmdExecTest.java new file mode 100644 index 000000000..3702a7102 --- /dev/null +++ b/src/test/java/com/github/dockerjava/netty/exec/ExecStartCmdExecTest.java @@ -0,0 +1,97 @@ +package com.github.dockerjava.netty.exec; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.Matchers.not; + +import java.io.InputStream; +import java.lang.reflect.Method; +import java.security.SecureRandom; + +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.command.ExecCreateCmdResponse; +import com.github.dockerjava.core.command.ExecStartResultCallback; +import com.github.dockerjava.netty.AbstractDockerClientTest; + +@Test(groups = "integration") +public class ExecStartCmdExecTest extends AbstractDockerClientTest { + @BeforeTest + public void beforeTest() throws Exception { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test(groups = "ignoreInCircleCi") + public void execStart() throws Exception { + String containerName = "generated_" + new SecureRandom().nextInt(); + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("top") + .withName(containerName).exec(); + LOG.info("Created container {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container.getId()).exec(); + + ExecCreateCmdResponse execCreateCmdResponse = dockerClient.execCreateCmd(container.getId()) + .withAttachStdout(true).withCmd("touch", "/execStartTest.log").exec(); + dockerClient.execStartCmd(execCreateCmdResponse.getId()).exec( + new ExecStartResultCallback(System.out, System.err)); + + InputStream response = dockerClient.copyFileFromContainerCmd(container.getId(), "/execStartTest.log").exec(); + + Boolean bytesAvailable = response.available() > 0; + assertTrue(bytesAvailable, "The file was not copied from the container."); + + // read the stream fully. Otherwise, the underlying stream will not be closed. + String responseAsString = asString(response); + assertNotNull(responseAsString); + assertTrue(responseAsString.length() > 0); + } + + @Test(groups = "ignoreInCircleCi") + public void execStartAttached() throws Exception { + String containerName = "generated_" + new SecureRandom().nextInt(); + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999") + .withName(containerName).exec(); + LOG.info("Created container {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container.getId()).exec(); + + ExecCreateCmdResponse execCreateCmdResponse = dockerClient.execCreateCmd(container.getId()) + .withAttachStdout(true).withCmd("touch", "/execStartTest.log").exec(); + dockerClient.execStartCmd(execCreateCmdResponse.getId()).withDetach(false).withTty(true) + .exec(new ExecStartResultCallback(System.out, System.err)); + + InputStream response = dockerClient.copyFileFromContainerCmd(container.getId(), "/execStartTest.log").exec(); + Boolean bytesAvailable = response.available() > 0; + assertTrue(bytesAvailable, "The file was not copied from the container."); + + // read the stream fully. Otherwise, the underlying stream will not be closed. + String responseAsString = asString(response); + assertNotNull(responseAsString); + assertTrue(responseAsString.length() > 0); + } +} diff --git a/src/test/java/com/github/dockerjava/netty/exec/InfoCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/InfoCmdExecTest.java new file mode 100644 index 000000000..db2e0ba96 --- /dev/null +++ b/src/test/java/com/github/dockerjava/netty/exec/InfoCmdExecTest.java @@ -0,0 +1,71 @@ +package com.github.dockerjava.netty.exec; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.isEmptyOrNullString; +import static org.hamcrest.Matchers.not; + +import java.lang.reflect.Method; + +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.model.Info; +import com.github.dockerjava.netty.AbstractDockerClientTest; + +@Test(groups = "integration") +public class InfoCmdExecTest extends AbstractDockerClientTest { + + @BeforeTest + public void beforeTest() throws Exception { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test + public void info() throws DockerException { + // Make sure that there is at least one container for the assertion + // TODO extract this into a shared method + if (dockerClient.listContainersCmd().withShowAll(true).exec().size() == 0) { + CreateContainerResponse container = dockerClient.createContainerCmd("busybox") + .withName("docker-java-itest-info").withCmd("touch", "/test").exec(); + + LOG.info("Created container: {}", container); + assertThat(container.getId(), not(isEmptyOrNullString())); + + dockerClient.startContainerCmd(container.getId()).exec(); + } + + Info dockerInfo = dockerClient.infoCmd().exec(); + LOG.info(dockerInfo.toString()); + + assertTrue(dockerInfo.toString().contains("containers")); + assertTrue(dockerInfo.toString().contains("images")); + assertTrue(dockerInfo.toString().contains("debug")); + + assertTrue(dockerInfo.getContainers() > 0); + assertTrue(dockerInfo.getImages() > 0); + assertTrue(dockerInfo.getNFd() > 0); + assertTrue(dockerInfo.getNGoroutines() > 0); + assertTrue(dockerInfo.getNCPU() > 0); + } +} diff --git a/src/test/java/com/github/dockerjava/netty/exec/InspectExecCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/InspectExecCmdExecTest.java new file mode 100644 index 000000000..c2fbc84a0 --- /dev/null +++ b/src/test/java/com/github/dockerjava/netty/exec/InspectExecCmdExecTest.java @@ -0,0 +1,96 @@ +package com.github.dockerjava.netty.exec; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.Matchers.not; + +import java.io.IOException; +import java.lang.reflect.Method; +import java.security.SecureRandom; + +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.command.ExecCreateCmdResponse; +import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.api.command.InspectExecResponse; +import com.github.dockerjava.core.command.ExecStartResultCallback; +import com.github.dockerjava.netty.AbstractDockerClientTest; +import com.github.dockerjava.test.serdes.JSONTestHelper; + +@Test(groups = "integration") +public class InspectExecCmdExecTest extends AbstractDockerClientTest { + + @BeforeTest + public void beforeTest() throws Exception { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test(groups = "ignoreInCircleCi") + public void inspectExecTest() throws IOException { + String containerName = "generated_" + new SecureRandom().nextInt(); + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("top") + .withName(containerName).exec(); + LOG.info("Created container {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container.getId()).exec(); + + ExecCreateCmdResponse touchFileCmdCreateResponse = dockerClient.execCreateCmd(container.getId()) + .withAttachStdout(true).withAttachStderr(true).withCmd("touch", "/marker").exec(); + LOG.info("Created exec {}", touchFileCmdCreateResponse.toString()); + assertThat(touchFileCmdCreateResponse.getId(), not(isEmptyString())); + ExecCreateCmdResponse checkFileCmdCreateResponse = dockerClient.execCreateCmd(container.getId()) + .withAttachStdout(true).withAttachStderr(true).withCmd("test", "-e", "/marker").exec(); + LOG.info("Created exec {}", checkFileCmdCreateResponse.toString()); + assertThat(checkFileCmdCreateResponse.getId(), not(isEmptyString())); + + // Check that file does not exist + dockerClient.execStartCmd(container.getId()) + .withExecId(checkFileCmdCreateResponse.getId()).exec(new ExecStartResultCallback(System.out, System.err)); + + InspectExecResponse first = dockerClient.inspectExecCmd(checkFileCmdCreateResponse.getId()).exec(); + assertThat(first.getExitCode(), is(1)); + + // Create the file + dockerClient.execStartCmd(container.getId()) + .withExecId(touchFileCmdCreateResponse.getId()).exec(new ExecStartResultCallback(System.out, System.err)); + + InspectExecResponse second = dockerClient.inspectExecCmd(touchFileCmdCreateResponse.getId()).exec(); + assertThat(second.getExitCode(), is(0)); + + // Check that file does exist now + dockerClient.execStartCmd(container.getId()) + .withExecId(checkFileCmdCreateResponse.getId()).exec(new ExecStartResultCallback(System.out, System.err)); + + InspectExecResponse third = dockerClient.inspectExecCmd(checkFileCmdCreateResponse.getId()).exec(); + assertThat(third.getExitCode(), is(0)); + + // Get container info and check its roundtrip to ensure the consistency + InspectContainerResponse containerInfo = dockerClient.inspectContainerCmd(container.getId()).exec(); + assertEquals(containerInfo.getId(), container.getId()); + JSONTestHelper.testRoundTrip(containerInfo); + } +} diff --git a/src/test/java/com/github/dockerjava/netty/exec/KillContainerCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/KillContainerCmdExecTest.java new file mode 100644 index 000000000..9ba2c278b --- /dev/null +++ b/src/test/java/com/github/dockerjava/netty/exec/KillContainerCmdExecTest.java @@ -0,0 +1,80 @@ +package com.github.dockerjava.netty.exec; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.Matchers.not; + +import java.lang.reflect.Method; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.netty.AbstractDockerClientTest; + +@Test(groups = "integration") +public class KillContainerCmdExecTest extends AbstractDockerClientTest { + + public static final Logger LOG = LoggerFactory.getLogger(KillContainerCmdExecTest.class); + + @BeforeTest + public void beforeTest() throws Exception { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test(groups = "ignoreInCircleCi") + public void killContainer() throws DockerException { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999").exec(); + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + dockerClient.startContainerCmd(container.getId()).exec(); + + LOG.info("Killing container: {}", container.getId()); + dockerClient.killContainerCmd(container.getId()).exec(); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + LOG.info("Container Inspect: {}", inspectContainerResponse.toString()); + + assertThat(inspectContainerResponse.getState().isRunning(), is(equalTo(false))); + assertThat(inspectContainerResponse.getState().getExitCode(), not(equalTo(0))); + + } + + @Test + public void killNonExistingContainer() throws DockerException { + + try { + dockerClient.killContainerCmd("non-existing").exec(); + fail("expected NotFoundException"); + } catch (NotFoundException e) { + } + } + +} diff --git a/src/test/java/com/github/dockerjava/netty/exec/LogContainerCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/LogContainerCmdExecTest.java new file mode 100644 index 000000000..a9a48dc58 --- /dev/null +++ b/src/test/java/com/github/dockerjava/netty/exec/LogContainerCmdExecTest.java @@ -0,0 +1,171 @@ +package com.github.dockerjava.netty.exec; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.Matchers.not; + +import java.io.IOException; +import java.lang.reflect.Method; + +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.client.AbstractDockerClientTest; +import com.github.dockerjava.core.command.WaitContainerResultCallback; + +@Test(groups = "integration") +public class LogContainerCmdExecTest extends AbstractDockerClientTest { + + @BeforeTest + public void beforeTest() throws Exception { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test + public void asyncLogContainer() throws Exception { + + String snippet = "hello world"; + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("/bin/echo", snippet) + .exec(); + + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container.getId()).exec(); + + int exitCode = dockerClient.waitContainerCmd(container.getId()).exec(new WaitContainerResultCallback()) + .awaitStatusCode(); + + assertThat(exitCode, equalTo(0)); + + LogContainerTestCallback loggingCallback = new LogContainerTestCallback(); + + // this essentially test the since=0 case + dockerClient.logContainerCmd(container.getId()).withStdErr(true).withStdOut(true).exec(loggingCallback); + + loggingCallback.awaitCompletion(); + + assertTrue(loggingCallback.toString().contains(snippet)); + } + + @Test + public void asyncLogNonExistingContainer() throws Exception { + + LogContainerTestCallback loggingCallback = new LogContainerTestCallback() { + @Override + public void onError(Throwable throwable) { + + assertEquals(throwable.getClass().getName(), NotFoundException.class.getName()); + + try { + // close the callback to prevent the call to onComplete + close(); + } catch (IOException e) { + throw new RuntimeException(); + } + + super.onError(throwable); + } + + public void onComplete() { + super.onComplete(); + fail("expected NotFoundException"); + }; + }; + + dockerClient.logContainerCmd("non-existing").withStdErr(true).withStdOut(true).exec(loggingCallback) + .awaitCompletion(); + } + + @Test + public void asyncMultipleLogContainer() throws Exception { + + String snippet = "hello world"; + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("/bin/echo", snippet) + .exec(); + + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container.getId()).exec(); + + int exitCode = dockerClient.waitContainerCmd(container.getId()).exec(new WaitContainerResultCallback()) + .awaitStatusCode(); + + assertThat(exitCode, equalTo(0)); + + LogContainerTestCallback loggingCallback = new LogContainerTestCallback(); + + dockerClient.logContainerCmd(container.getId()).withStdErr(true).withStdOut(true).exec(loggingCallback); + + loggingCallback.close(); + + loggingCallback = new LogContainerTestCallback(); + + dockerClient.logContainerCmd(container.getId()).withStdErr(true).withStdOut(true).exec(loggingCallback); + + loggingCallback.close(); + + loggingCallback = new LogContainerTestCallback(); + + dockerClient.logContainerCmd(container.getId()).withStdErr(true).withStdOut(true).exec(loggingCallback); + + loggingCallback.awaitCompletion(); + + assertTrue(loggingCallback.toString().contains(snippet)); + } + + @Test + public void asyncLogContainerWithSince() throws Exception { + String snippet = "hello world"; + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("/bin/echo", snippet) + .exec(); + + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + int timestamp = (int) (System.currentTimeMillis() / 1000); + + dockerClient.startContainerCmd(container.getId()).exec(); + + int exitCode = dockerClient.waitContainerCmd(container.getId()).exec(new WaitContainerResultCallback()) + .awaitStatusCode(); + + assertThat(exitCode, equalTo(0)); + + LogContainerTestCallback loggingCallback = new LogContainerTestCallback(); + + dockerClient.logContainerCmd(container.getId()).withStdErr(true).withStdOut(true).withSince(timestamp) + .exec(loggingCallback); + + loggingCallback.awaitCompletion(); + + assertFalse(loggingCallback.toString().contains(snippet)); + } + +} diff --git a/src/test/java/com/github/dockerjava/netty/exec/StopContainerCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/StopContainerCmdExecTest.java new file mode 100644 index 000000000..8f01b1957 --- /dev/null +++ b/src/test/java/com/github/dockerjava/netty/exec/StopContainerCmdExecTest.java @@ -0,0 +1,79 @@ +package com.github.dockerjava.netty.exec; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.Matchers.not; + +import java.lang.reflect.Method; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.netty.AbstractDockerClientTest; + +@Test(groups = "integration") +public class StopContainerCmdExecTest extends AbstractDockerClientTest { + + public static final Logger LOG = LoggerFactory.getLogger(StopContainerCmdExecTest.class); + + @BeforeTest + public void beforeTest() throws Exception { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test(groups = "ignoreInCircleCi") + public void testStopContainer() throws DockerException { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999").exec(); + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + dockerClient.startContainerCmd(container.getId()).exec(); + + LOG.info("Stopping container: {}", container.getId()); + dockerClient.stopContainerCmd(container.getId()).withTimeout(2).exec(); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + LOG.info("Container Inspect: {}", inspectContainerResponse.toString()); + + assertThat(inspectContainerResponse.getState().isRunning(), is(equalTo(false))); + assertThat(inspectContainerResponse.getState().getExitCode(), not(equalTo(0))); + } + + @Test + public void testStopNonExistingContainer() throws DockerException { + try { + dockerClient.stopContainerCmd("non-existing").withTimeout(2).exec(); + fail("expected NotFoundException"); + } catch (NotFoundException e) { + + } + } + +} diff --git a/src/test/java/com/github/dockerjava/netty/exec/WaitContainerCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/WaitContainerCmdExecTest.java new file mode 100644 index 000000000..ba1dd518e --- /dev/null +++ b/src/test/java/com/github/dockerjava/netty/exec/WaitContainerCmdExecTest.java @@ -0,0 +1,108 @@ +package com.github.dockerjava.netty.exec; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.Matchers.not; + +import java.lang.reflect.Method; + +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.api.model.WaitResponse; +import com.github.dockerjava.core.command.WaitContainerResultCallback; +import com.github.dockerjava.netty.AbstractDockerClientTest; + +@Test(groups = "integration") +public class WaitContainerCmdExecTest extends AbstractDockerClientTest { + + @BeforeTest + public void beforeTest() throws Exception { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test + public void testWaitContainer() throws DockerException { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("true").exec(); + + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container.getId()).exec(); + + int exitCode = dockerClient.waitContainerCmd(container.getId()).exec(new WaitContainerResultCallback()) + .awaitStatusCode(); + LOG.info("Container exit code: {}", exitCode); + + assertThat(exitCode, equalTo(0)); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + LOG.info("Container Inspect: {}", inspectContainerResponse.toString()); + + assertThat(inspectContainerResponse.getState().isRunning(), is(equalTo(false))); + assertThat(inspectContainerResponse.getState().getExitCode(), is(equalTo(exitCode))); + } + + @Test(expectedExceptions = NotFoundException.class) + public void testWaitNonExistingContainer() throws DockerException { + + WaitContainerResultCallback callback = new WaitContainerResultCallback() { + public void onNext(WaitResponse waitResponse) { + fail("expected NotFoundException"); + }; + }; + + dockerClient.waitContainerCmd("non-existing").exec(callback).awaitStatusCode(); + } + + @Test + public void testWaitContainerAbort() throws Exception { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999").exec(); + + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container.getId()).exec(); + + WaitContainerResultCallback callback = dockerClient.waitContainerCmd(container.getId()).exec( + new WaitContainerResultCallback()); + + Thread.sleep(5000); + + callback.close(); + + dockerClient.killContainerCmd(container.getId()).exec(); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + LOG.info("Container Inspect: {}", inspectContainerResponse.toString()); + + assertThat(inspectContainerResponse.getState().isRunning(), is(equalTo(false))); + } +} diff --git a/src/test/resources/logback.xml b/src/test/resources/logback.xml index c2d4557a4..b4309b868 100644 --- a/src/test/resources/logback.xml +++ b/src/test/resources/logback.xml @@ -8,6 +8,7 @@ + From e837b810592b2b39726148a7f0920b06f1b88ba2 Mon Sep 17 00:00:00 2001 From: Marcus Linke Date: Tue, 24 Nov 2015 19:44:30 +0100 Subject: [PATCH 07/13] WIP --- .../api/command/InspectContainerResponse.java | 2 +- .../netty/DockerCmdExecFactoryImpl.java | 24 +- .../dockerjava/netty/InvocationBuilder.java | 344 +++++++++--------- .../github/dockerjava/netty/WebTarget.java | 106 +++--- .../dockerjava/netty/exec/AuthCmdExec.java | 10 +- .../netty/exec/CreateContainerCmdExec.java | 4 +- .../netty/exec/ExecCreateCmdExec.java | 32 +- .../dockerjava/netty/exec/InfoCmdExec.java | 36 +- .../netty/exec/InspectContainerCmdExec.java | 10 +- .../netty/exec/InspectExecCmdExec.java | 12 +- .../netty/exec/InspectImageCmdExec.java | 9 +- .../netty/exec/ListContainersCmdExec.java | 21 +- .../netty/exec/RemoveContainerCmdExec.java | 4 +- .../netty/exec/WaitContainerCmdExec.java | 6 +- .../handler/AwaitObjectInboundHandler.java | 39 -- .../DeserializeJsonInboundHandler.java | 32 -- .../handler/FramedResponseStreamHandler.java | 14 + .../netty/handler/HttpResponseHandler.java | 82 +++-- .../HttpResponseStreamInboundHandler.java | 10 - ...ndHandler.java => JsonRequestHandler.java} | 2 +- .../handler/JsonResponseCallbackHandler.java | 47 +++ .../netty/handler/JsonResponseHandler.java | 62 ++++ .../client/AbstractDockerClientTest.java | 3 +- .../netty/AbstractDockerClientTest.java | 8 + .../exec/CreateContainerCmdExecTest.java | 7 - .../exec/InspectContainerCmdExecTest.java | 81 +++++ .../netty/exec/ListContainersCmdExecTest.java | 171 +++++++++ .../netty/exec/LogContainerCmdExecTest.java | 2 +- .../netty/exec/WaitContainerCmdExecTest.java | 2 + 29 files changed, 769 insertions(+), 413 deletions(-) delete mode 100644 src/main/java/com/github/dockerjava/netty/handler/AwaitObjectInboundHandler.java delete mode 100644 src/main/java/com/github/dockerjava/netty/handler/DeserializeJsonInboundHandler.java rename src/main/java/com/github/dockerjava/netty/handler/{SerializeJsonOutboundHandler.java => JsonRequestHandler.java} (87%) create mode 100644 src/main/java/com/github/dockerjava/netty/handler/JsonResponseCallbackHandler.java create mode 100644 src/main/java/com/github/dockerjava/netty/handler/JsonResponseHandler.java create mode 100644 src/test/java/com/github/dockerjava/netty/exec/InspectContainerCmdExecTest.java create mode 100644 src/test/java/com/github/dockerjava/netty/exec/ListContainersCmdExecTest.java diff --git a/src/main/java/com/github/dockerjava/api/command/InspectContainerResponse.java b/src/main/java/com/github/dockerjava/api/command/InspectContainerResponse.java index e37e28471..cedbc826e 100644 --- a/src/main/java/com/github/dockerjava/api/command/InspectContainerResponse.java +++ b/src/main/java/com/github/dockerjava/api/command/InspectContainerResponse.java @@ -126,7 +126,7 @@ public String getResolvConfPath() { @JsonIgnore public VolumeBind[] getVolumes() { - return volumes.getBinds(); + return volumes == null ? new VolumeBind[0]: volumes.getBinds(); } @JsonIgnore diff --git a/src/main/java/com/github/dockerjava/netty/DockerCmdExecFactoryImpl.java b/src/main/java/com/github/dockerjava/netty/DockerCmdExecFactoryImpl.java index 6c97055fb..37d2534c5 100644 --- a/src/main/java/com/github/dockerjava/netty/DockerCmdExecFactoryImpl.java +++ b/src/main/java/com/github/dockerjava/netty/DockerCmdExecFactoryImpl.java @@ -68,10 +68,12 @@ import com.github.dockerjava.api.command.UnpauseContainerCmd; import com.github.dockerjava.api.command.VersionCmd; import com.github.dockerjava.api.command.WaitContainerCmd; +import com.github.dockerjava.api.model.Info; import com.github.dockerjava.core.DockerClientConfig; import com.github.dockerjava.core.command.ExecCreateCmdImpl; import com.github.dockerjava.core.command.ExecStartCmdImpl; import com.github.dockerjava.core.command.ExecStartResultCallback; +import com.github.dockerjava.core.command.InfoCmdImpl; import com.github.dockerjava.netty.exec.AuthCmdExec; import com.github.dockerjava.netty.exec.CopyFileFromContainerCmdExec; import com.github.dockerjava.netty.exec.CreateContainerCmdExec; @@ -82,6 +84,7 @@ import com.github.dockerjava.netty.exec.InspectExecCmdExec; import com.github.dockerjava.netty.exec.InspectImageCmdExec; import com.github.dockerjava.netty.exec.KillContainerCmdExec; +import com.github.dockerjava.netty.exec.ListContainersCmdExec; import com.github.dockerjava.netty.exec.LogContainerCmdExec; import com.github.dockerjava.netty.exec.RemoveContainerCmdExec; import com.github.dockerjava.netty.exec.StartContainerCmdExec; @@ -327,8 +330,7 @@ public InspectImageCmd.Exec createInspectImageCmdExec() { @Override public ListContainersCmd.Exec createListContainersCmdExec() { - return null; // new ListContainersCmdExec(getBaseResource(), - // getDockerClientConfig()); + return new ListContainersCmdExec(getBaseResource(), getDockerClientConfig()); } @Override @@ -481,15 +483,15 @@ public static void main(String[] args) throws IOException { execFactory.init(config); - // InfoCmd.Exec exec = execFactory.createInfoCmdExec(); - // - // InfoCmd infoCmd = new InfoCmdImpl(exec); - // - // Info info = infoCmd.exec(); - // - // System.out.println("result: " + info); - // - // infoCmd.close(); + InfoCmd.Exec exec = execFactory.createInfoCmdExec(); + + InfoCmd infoCmd = new InfoCmdImpl(exec); + + Info info = infoCmd.exec(); + + System.out.println("result: " + info); + + infoCmd.close(); ExecCreateCmd.Exec execCreate = execFactory.createExecCmdExec(); diff --git a/src/main/java/com/github/dockerjava/netty/InvocationBuilder.java b/src/main/java/com/github/dockerjava/netty/InvocationBuilder.java index a2cfeb827..943875270 100644 --- a/src/main/java/com/github/dockerjava/netty/InvocationBuilder.java +++ b/src/main/java/com/github/dockerjava/netty/InvocationBuilder.java @@ -2,6 +2,8 @@ import io.netty.buffer.Unpooled; import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.HttpClientCodec; @@ -10,11 +12,11 @@ import io.netty.handler.codec.http.HttpHeaderValues; import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.json.JsonObjectDecoder; import java.io.BufferedReader; +import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -22,33 +24,42 @@ import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.util.HashMap; -import java.util.List; import java.util.Map; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.dockerjava.api.BadRequestException; -import com.github.dockerjava.api.ConflictException; -import com.github.dockerjava.api.DockerException; -import com.github.dockerjava.api.InternalServerErrorException; -import com.github.dockerjava.api.NotAcceptableException; -import com.github.dockerjava.api.NotFoundException; -import com.github.dockerjava.api.NotModifiedException; -import com.github.dockerjava.api.UnauthorizedException; import com.github.dockerjava.api.async.ResultCallback; -import com.github.dockerjava.api.model.Container; import com.github.dockerjava.api.model.Frame; -import com.github.dockerjava.netty.handler.AwaitObjectInboundHandler; -import com.github.dockerjava.netty.handler.DeserializeJsonInboundHandler; +import com.github.dockerjava.core.async.ResultCallbackTemplate; import com.github.dockerjava.netty.handler.FramedResponseStreamHandler; import com.github.dockerjava.netty.handler.HttpConnectionHijackHandler; import com.github.dockerjava.netty.handler.HttpRequestProvider; import com.github.dockerjava.netty.handler.HttpResponseHandler; import com.github.dockerjava.netty.handler.HttpResponseStreamInboundHandler; +import com.github.dockerjava.netty.handler.JsonResponseCallbackHandler; public class InvocationBuilder { + public class ResponseCallback extends ResultCallbackTemplate, T> { + + private T result = null; + + public T awaitResult() { + try { + awaitCompletion(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + return result; + } + + @Override + public void onNext(T object) { + result = object; + } + } + private ChannelProvider channelProvider; private String resource; @@ -65,98 +76,120 @@ public InvocationBuilder accept(MediaType mediaType) { return this; } - public T get(Class resultType) { + public ResponseCallback delete() { - HttpRequestProvider requestProvider = httpGetRequestProvider(); + HttpRequestProvider requestProvider = httpDeleteRequestProvider(); - Channel channel = channelProvider.getChannel(); + ResponseCallback callback = new ResponseCallback(); - AwaitObjectInboundHandler awaitObject = new AwaitObjectInboundHandler(resultType); + HttpResponseHandler responseHandler = new HttpResponseHandler(requestProvider, callback); - HttpResponseHandler responseHandler = new HttpResponseHandler(); + Channel channel = getChannel(); channel.pipeline().addLast(responseHandler); - channel.pipeline().addLast(new JsonObjectDecoder()); - channel.pipeline().addLast(new DeserializeJsonInboundHandler(resultType)); - channel.pipeline().addLast(awaitObject); - - sendRequestAndHandleResponse(requestProvider, channel, responseHandler); - return awaitObject.await(); + sendRequestAndHandleResponse(requestProvider, channel); + return callback; } - private void sendRequestAndHandleResponse(HttpRequestProvider requestProvider, Channel channel, - HttpResponseHandler responseHandler) { + public void get(ResultCallback resultCallback) { + + HttpRequestProvider requestProvider = httpGetRequestProvider(); + + HttpResponseHandler responseHandler = new HttpResponseHandler(requestProvider, resultCallback); + + FramedResponseStreamHandler streamHandler = new FramedResponseStreamHandler(resultCallback); + + Channel channel = getChannel(); - channel.writeAndFlush(requestProvider.getHttpRequest(resource)); + channel.pipeline().addLast(responseHandler); + channel.pipeline().addLast(streamHandler); - handleResponse(channel, responseHandler, requestProvider); + sendRequestAndHandleResponse(requestProvider, channel); } - private HttpRequestProvider httpGetRequestProvider() { - return new HttpRequestProvider() { - @Override - public HttpRequest getHttpRequest(String uri) { - return prepareGetRequest(uri); - } - }; + public ResponseCallback get(TypeReference typeReference) { + + ResponseCallback callback = new ResponseCallback(); + + get(typeReference, callback); + + return callback; } - public void get(ResultCallback resultCallback) { + public void get(TypeReference typeReference, ResultCallback resultCallback) { HttpRequestProvider requestProvider = httpGetRequestProvider(); - HttpResponseHandler responseHandler = new HttpResponseHandler(); + Channel channel = getChannel(); - FramedResponseStreamHandler streamHandler = new FramedResponseStreamHandler(resultCallback); + resultCallbackOnStart(channel, resultCallback); - Channel channel = channelProvider.getChannel(); + JsonResponseCallbackHandler jsonResponseHandler = new JsonResponseCallbackHandler(typeReference, + resultCallback); + + HttpResponseHandler responseHandler = new HttpResponseHandler(requestProvider, resultCallback); channel.pipeline().addLast(responseHandler); - channel.pipeline().addLast(streamHandler); + channel.pipeline().addLast(new JsonObjectDecoder()); + channel.pipeline().addLast(jsonResponseHandler); - sendRequestAndHandleResponse(requestProvider, channel, responseHandler); - } + sendRequestAndHandleResponse(requestProvider, channel); - private FullHttpRequest prepareGetRequest(String uri) { + return; + } - FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri); + private Channel getChannel() { + Channel channel = channelProvider.getChannel(); - setDefaultHeaders(request); + return channel; + } - return request; + private HttpRequestProvider httpDeleteRequestProvider() { + return new HttpRequestProvider() { + @Override + public HttpRequest getHttpRequest(String uri) { + return prepareDeleteRequest(uri); + } + }; } - private void setDefaultHeaders(HttpRequest request) { - request.headers().set(HttpHeaderNames.HOST, ""); - request.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE); - request.headers().set(HttpHeaderNames.ACCEPT_ENCODING, HttpHeaderValues.GZIP); + private HttpRequestProvider httpGetRequestProvider() { + return new HttpRequestProvider() { + @Override + public HttpRequest getHttpRequest(String uri) { + return prepareGetRequest(uri); + } + }; + } - for (Map.Entry entry : headers.entrySet()) { - request.headers().set((CharSequence) entry.getKey(), entry.getValue()); - } + private HttpRequestProvider httpPostRequestProvider(final Object entity) { + return new HttpRequestProvider() { + @Override + public HttpRequest getHttpRequest(String uri) { + return preparePostRequest(uri, entity); + } + }; } - public T post(final Object entity, Class resultType) { + public InputStream post(final Object entity) { HttpRequestProvider requestProvider = httpPostRequestProvider(entity); - AwaitObjectInboundHandler awaitObject = new AwaitObjectInboundHandler(resultType); + Channel channel = getChannel(); - Channel channel = channelProvider.getChannel(); + ResponseCallback callback = new ResponseCallback(); - HttpResponseHandler responseHandler = new HttpResponseHandler(); + HttpResponseHandler responseHandler = new HttpResponseHandler(requestProvider, callback); + HttpResponseStreamInboundHandler streamHandler = new HttpResponseStreamInboundHandler(); channel.pipeline().addLast(responseHandler); - channel.pipeline().addLast(new JsonObjectDecoder()); - channel.pipeline().addLast(new DeserializeJsonInboundHandler(resultType)); - channel.pipeline().addLast(awaitObject); - - sendRequestAndHandleResponse(requestProvider, channel, responseHandler); + channel.pipeline().addLast(streamHandler); - return awaitObject.await(); + sendRequestAndHandleResponse(requestProvider, channel); + return streamHandler.getInputStream(); } public void post(final Object entity, final InputStream stdin, ResultCallback resultCallback) { @@ -165,9 +198,9 @@ public void post(final Object entity, final InputStream stdin, ResultCallback T post(final Object entity, TypeReference typeReference) { + + ResponseCallback callback = new ResponseCallback(); + + post(entity, typeReference, callback); + + return callback.awaitResult(); + } + + public void post(final Object entity, TypeReference typeReference, final ResultCallback resultCallback) { + + HttpRequestProvider requestProvider = httpPostRequestProvider(entity); + + Channel channel = getChannel(); + + resultCallbackOnStart(channel, resultCallback); + + JsonResponseCallbackHandler jsonResponseHandler = new JsonResponseCallbackHandler(typeReference, + resultCallback); + + HttpResponseHandler responseHandler = new HttpResponseHandler(requestProvider, resultCallback); + + channel.pipeline().addLast(responseHandler); + channel.pipeline().addLast(new JsonObjectDecoder()); + channel.pipeline().addLast(jsonResponseHandler); + + sendRequestAndHandleResponse(requestProvider, channel); + + return; + } + + private void resultCallbackOnStart(final Channel channel, final ResultCallback resultCallback) { + Closeable closeable = new Closeable() { + @Override + public void close() throws IOException { try { - return reader.read(); - } catch (IOException e) { - throw new RuntimeException(e); + System.err.println("closing channel"); + channel.close().sync(); + } catch (InterruptedException e) { + resultCallback.onError(e); } } - }).start(); + }; + resultCallback.onStart(closeable); } - private void handleResponse(final Channel channel, HttpResponseHandler responseHandler, - HttpRequestProvider requestProvider) { - HttpResponse response = responseHandler.awaitResponse(); - - switch (response.status().code()) { - case 101: - case 200: - case 201: - case 204: - break; - case 301: - case 302: - if (response.headers().contains(HttpHeaderNames.LOCATION)) { - String location = response.headers().get(HttpHeaderNames.LOCATION); - System.out.println("redirected to :" + location); - HttpRequest redirected = requestProvider.getHttpRequest(location); - - channel.writeAndFlush(redirected); - } - break; - case 304: - throw new NotModifiedException(responseHandler.awaitErrorBody()); - case 400: - throw new BadRequestException(responseHandler.awaitErrorBody()); - case 401: - throw new UnauthorizedException(responseHandler.awaitErrorBody()); - case 404: - throw new NotFoundException(responseHandler.awaitErrorBody()); - case 406: - throw new NotAcceptableException(responseHandler.awaitErrorBody()); - case 409: - throw new ConflictException(responseHandler.awaitErrorBody()); - case 500: - throw new InternalServerErrorException(responseHandler.awaitErrorBody()); - default: - throw new DockerException(responseHandler.awaitErrorBody(), response.status().code()); - } + private HttpRequest prepareDeleteRequest(String uri) { + + FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.DELETE, uri); + + setDefaultHeaders(request); + + return request; + } + + private FullHttpRequest prepareGetRequest(String uri) { + + FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri); + + setDefaultHeaders(request); + return request; } private HttpRequest preparePostRequest(String uri, Object entity) { @@ -276,65 +333,28 @@ private HttpRequest preparePostRequest(String uri, Object entity) { return request; } - private HttpRequest prepareDeleteRequest(String uri) { - - FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.DELETE, uri); - - setDefaultHeaders(request); + private void sendRequestAndHandleResponse(HttpRequestProvider requestProvider, Channel channel) { - return request; - } + ChannelFuture channelFuture = channel.writeAndFlush(requestProvider.getHttpRequest(resource)); - public InputStream post(final Object entity) { - - HttpRequestProvider requestProvider = httpPostRequestProvider(entity); - - Channel channel = channelProvider.getChannel(); - - HttpResponseHandler responseHandler = new HttpResponseHandler(); - HttpResponseStreamInboundHandler streamHandler = new HttpResponseStreamInboundHandler(); - - channel.pipeline().addLast(responseHandler); - channel.pipeline().addLast(streamHandler); - - sendRequestAndHandleResponse(requestProvider, channel, responseHandler); - - return streamHandler.getInputStream(); - } - - private HttpRequestProvider httpPostRequestProvider(final Object entity) { - return new HttpRequestProvider() { + channelFuture.addListener(new ChannelFutureListener() { @Override - public HttpRequest getHttpRequest(String uri) { - return preparePostRequest(uri, entity); - } - }; - } - - public void delete() { - - HttpRequestProvider requestProvider = httpDeleteRequestProvider(); - - HttpResponseHandler responseHandler = new HttpResponseHandler(); - - Channel channel = channelProvider.getChannel(); - - channel.pipeline().addLast(responseHandler); - - sendRequestAndHandleResponse(requestProvider, channel, responseHandler); - } + public void operationComplete(ChannelFuture future) throws Exception { + if (future.isSuccess()) { + System.err.println("Request success"); + } - private HttpRequestProvider httpDeleteRequestProvider() { - return new HttpRequestProvider() { - @Override - public HttpRequest getHttpRequest(String uri) { - return prepareDeleteRequest(uri); } - }; + }); } - public T get(TypeReference typeReference) { - Class clazz = (Class) typeReference.getType().getClass(); - return null; - } + private void setDefaultHeaders(HttpRequest request) { + request.headers().set(HttpHeaderNames.HOST, ""); + request.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE); + request.headers().set(HttpHeaderNames.ACCEPT_ENCODING, HttpHeaderValues.GZIP); + + for (Map.Entry entry : headers.entrySet()) { + request.headers().set((CharSequence) entry.getKey(), entry.getValue()); + } + }; } diff --git a/src/main/java/com/github/dockerjava/netty/WebTarget.java b/src/main/java/com/github/dockerjava/netty/WebTarget.java index 7c8970d33..d4af90786 100644 --- a/src/main/java/com/github/dockerjava/netty/WebTarget.java +++ b/src/main/java/com/github/dockerjava/netty/WebTarget.java @@ -11,59 +11,59 @@ import io.netty.channel.Channel; public class WebTarget { - - private ChannelProvider channelProvider; - - private List path = new ArrayList(); - - private Map queryParams = new HashMap(); - - private static String PATH_SEPARATOR = "/"; - - public WebTarget(ChannelProvider channelProvider) { - this.channelProvider = channelProvider; - } - - public WebTarget path(String... components) { - - for(String component : components) { - - path.addAll(Arrays.asList(StringUtils.split(component, PATH_SEPARATOR))); - } - - return this; - } - - public InvocationBuilder request() { - String resource = PATH_SEPARATOR + StringUtils.join(path, PATH_SEPARATOR); - - List params = new ArrayList(); - for(Map.Entry entry : queryParams.entrySet()) { - params.add(entry.getKey() + "=" + entry.getValue()); - } - - if(!params.isEmpty()) { - resource = resource + "?" + StringUtils.join(params, "&"); - } - - return new InvocationBuilder(channelProvider, resource); - } - - public WebTarget resolveTemplate(String name, Object value) { - List newPath = new ArrayList(); - for(String component : path) { - component = component.replaceAll("\\{" + name + "\\}", value.toString()); - newPath.add(component); - } - path = newPath; - return this; - } - - public WebTarget queryParam(String name, Object value) { - queryParams.put(name, value.toString()); - return this; - } - + private ChannelProvider channelProvider; + + private List path = new ArrayList(); + + private Map queryParams = new HashMap(); + + private static String PATH_SEPARATOR = "/"; + + public WebTarget(ChannelProvider channelProvider) { + this.channelProvider = channelProvider; + } + + public WebTarget path(String... components) { + + for (String component : components) { + + path.addAll(Arrays.asList(StringUtils.split(component, PATH_SEPARATOR))); + } + + return this; + } + + public InvocationBuilder request() { + String resource = PATH_SEPARATOR + StringUtils.join(path, PATH_SEPARATOR); + + List params = new ArrayList(); + for (Map.Entry entry : queryParams.entrySet()) { + params.add(entry.getKey() + "=" + entry.getValue()); + } + + if (!params.isEmpty()) { + resource = resource + "?" + StringUtils.join(params, "&"); + } + + return new InvocationBuilder(channelProvider, resource); + } + + public WebTarget resolveTemplate(String name, Object value) { + List newPath = new ArrayList(); + for (String component : path) { + component = component.replaceAll("\\{" + name + "\\}", value.toString()); + newPath.add(component); + } + path = newPath; + return this; + } + + public WebTarget queryParam(String name, Object value) { + if (value != null) { + queryParams.put(name, value.toString()); + } + return this; + } } diff --git a/src/main/java/com/github/dockerjava/netty/exec/AuthCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/AuthCmdExec.java index ed6c9cb7a..b6f004940 100644 --- a/src/main/java/com/github/dockerjava/netty/exec/AuthCmdExec.java +++ b/src/main/java/com/github/dockerjava/netty/exec/AuthCmdExec.java @@ -3,6 +3,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.fasterxml.jackson.core.type.TypeReference; import com.github.dockerjava.api.command.AuthCmd; import com.github.dockerjava.api.model.AuthResponse; import com.github.dockerjava.core.DockerClientConfig; @@ -22,13 +23,8 @@ protected AuthResponse execute(AuthCmd command) { WebTarget webResource = getBaseResource().path("/auth"); LOGGER.trace("POST: {}", webResource); return webResource.request().accept(MediaType.APPLICATION_JSON) - .post(command.getAuthConfig(), AuthResponse.class); - -// if (response.getStatus() == 401) { -// throw new UnauthorizedException("Unauthorized"); -// } -// -// return response.readEntity(AuthResponse.class); + .post(command.getAuthConfig(), new TypeReference() { + }); } } diff --git a/src/main/java/com/github/dockerjava/netty/exec/CreateContainerCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/CreateContainerCmdExec.java index bf858349b..3a5e75d60 100644 --- a/src/main/java/com/github/dockerjava/netty/exec/CreateContainerCmdExec.java +++ b/src/main/java/com/github/dockerjava/netty/exec/CreateContainerCmdExec.java @@ -1,5 +1,6 @@ package com.github.dockerjava.netty.exec; +import com.fasterxml.jackson.core.type.TypeReference; import com.github.dockerjava.api.command.CreateContainerCmd; import com.github.dockerjava.api.command.CreateContainerResponse; import com.github.dockerjava.api.command.CreateContainerCmd.Exec; @@ -29,7 +30,8 @@ protected CreateContainerResponse execute(CreateContainerCmd command) { LOGGER.trace("POST: {} ", webResource); return webResource.request().accept(MediaType.APPLICATION_JSON) - .post(command, CreateContainerResponse.class); + .post(command, new TypeReference() { + }); } } diff --git a/src/main/java/com/github/dockerjava/netty/exec/ExecCreateCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/ExecCreateCmdExec.java index 78f788f3f..17af370a9 100644 --- a/src/main/java/com/github/dockerjava/netty/exec/ExecCreateCmdExec.java +++ b/src/main/java/com/github/dockerjava/netty/exec/ExecCreateCmdExec.java @@ -1,5 +1,6 @@ package com.github.dockerjava.netty.exec; +import com.fasterxml.jackson.core.type.TypeReference; import com.github.dockerjava.api.command.ExecCreateCmd; import com.github.dockerjava.api.command.ExecCreateCmdResponse; import com.github.dockerjava.api.command.ExecCreateCmd.Exec; @@ -11,23 +12,24 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class ExecCreateCmdExec extends AbstrSyncDockerCmdExec - implements ExecCreateCmd.Exec { - - private static final Logger LOGGER = LoggerFactory.getLogger(ExecCreateCmdExec.class); +public class ExecCreateCmdExec extends AbstrSyncDockerCmdExec implements + ExecCreateCmd.Exec { - public ExecCreateCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { - super(baseResource, dockerClientConfig); - } + private static final Logger LOGGER = LoggerFactory.getLogger(ExecCreateCmdExec.class); - @Override - protected ExecCreateCmdResponse execute(ExecCreateCmd command) { - WebTarget webResource = getBaseResource().path("/containers/{id}/exec").resolveTemplate("id", - command.getContainerId()); + public ExecCreateCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } - LOGGER.trace("POST: {}", webResource); + @Override + protected ExecCreateCmdResponse execute(ExecCreateCmd command) { + WebTarget webResource = getBaseResource().path("/containers/{id}/exec").resolveTemplate("id", + command.getContainerId()); - return webResource.request().accept(MediaType.APPLICATION_JSON) - .post(command, ExecCreateCmdResponse.class); - } + LOGGER.trace("POST: {}", webResource); + + return webResource.request().accept(MediaType.APPLICATION_JSON) + .post(command, new TypeReference() { + }); + } } diff --git a/src/main/java/com/github/dockerjava/netty/exec/InfoCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/InfoCmdExec.java index 5a41073a4..1db090ae6 100644 --- a/src/main/java/com/github/dockerjava/netty/exec/InfoCmdExec.java +++ b/src/main/java/com/github/dockerjava/netty/exec/InfoCmdExec.java @@ -3,41 +3,37 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.fasterxml.jackson.core.type.TypeReference; import com.github.dockerjava.api.command.InfoCmd; import com.github.dockerjava.api.model.Info; import com.github.dockerjava.core.DockerClientConfig; import com.github.dockerjava.netty.WebTarget; /** - * http://stackoverflow.com/questions/33296749/netty-connect-to-unix-domain- - * socket-failed http://netty.io/wiki/native-transports.html - * https://github.com/netty/netty/blob/master/example/src/main/java/io/netty/ - * example/http/snoop/HttpSnoopClient.java + * http://stackoverflow.com/questions/33296749/netty-connect-to-unix-domain- socket-failed + * http://netty.io/wiki/native-transports.html + * https://github.com/netty/netty/blob/master/example/src/main/java/io/netty/ example/http/snoop/HttpSnoopClient.java * * @author marcus * */ public class InfoCmdExec implements InfoCmd.Exec { - private static final Logger LOGGER = LoggerFactory.getLogger(InfoCmdExec.class); - - private WebTarget webResource; - - private DockerClientConfig dockerClientConfig; - - public InfoCmdExec(WebTarget webResource, DockerClientConfig dockerClientConfig) { - this.webResource = webResource; - this.dockerClientConfig = dockerClientConfig; - } - - - @Override - public Info exec(InfoCmd command) { - return webResource.path("info").request().get(Info.class); - } + private static final Logger LOGGER = LoggerFactory.getLogger(InfoCmdExec.class); + private WebTarget webResource; + private DockerClientConfig dockerClientConfig; + public InfoCmdExec(WebTarget webResource, DockerClientConfig dockerClientConfig) { + this.webResource = webResource; + this.dockerClientConfig = dockerClientConfig; + } + @Override + public Info exec(InfoCmd command) { + return webResource.path("info").request().get(new TypeReference() { + }).awaitResult(); + } } diff --git a/src/main/java/com/github/dockerjava/netty/exec/InspectContainerCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/InspectContainerCmdExec.java index 6490ddd7f..85b391a20 100644 --- a/src/main/java/com/github/dockerjava/netty/exec/InspectContainerCmdExec.java +++ b/src/main/java/com/github/dockerjava/netty/exec/InspectContainerCmdExec.java @@ -1,5 +1,8 @@ package com.github.dockerjava.netty.exec; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.fasterxml.jackson.core.type.TypeReference; import com.github.dockerjava.api.command.InspectContainerCmd; import com.github.dockerjava.api.command.InspectContainerResponse; @@ -7,9 +10,6 @@ import com.github.dockerjava.netty.MediaType; import com.github.dockerjava.netty.WebTarget; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - public class InspectContainerCmdExec extends AbstrSyncDockerCmdExec implements InspectContainerCmd.Exec { @@ -25,7 +25,9 @@ protected InspectContainerResponse execute(InspectContainerCmd command) { command.getContainerId()); LOGGER.debug("GET: {}", webResource); - return webResource.request().accept(MediaType.APPLICATION_JSON).get(InspectContainerResponse.class); + return webResource.request().accept(MediaType.APPLICATION_JSON) + .get(new TypeReference() { + }).awaitResult(); } } diff --git a/src/main/java/com/github/dockerjava/netty/exec/InspectExecCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/InspectExecCmdExec.java index 470df9c2c..aac395d32 100644 --- a/src/main/java/com/github/dockerjava/netty/exec/InspectExecCmdExec.java +++ b/src/main/java/com/github/dockerjava/netty/exec/InspectExecCmdExec.java @@ -1,14 +1,15 @@ package com.github.dockerjava.netty.exec; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.core.type.TypeReference; import com.github.dockerjava.api.command.InspectExecCmd; import com.github.dockerjava.api.command.InspectExecResponse; import com.github.dockerjava.core.DockerClientConfig; import com.github.dockerjava.netty.MediaType; import com.github.dockerjava.netty.WebTarget; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - public class InspectExecCmdExec extends AbstrSyncDockerCmdExec implements InspectExecCmd.Exec { private static final Logger LOGGER = LoggerFactory.getLogger(InspectExecCmdExec.class); @@ -20,7 +21,10 @@ public InspectExecCmdExec(WebTarget baseResource, DockerClientConfig dockerClien @Override protected InspectExecResponse execute(InspectExecCmd command) { WebTarget webResource = getBaseResource().path("/exec/{id}/json").resolveTemplate("id", command.getExecId()); + LOGGER.debug("GET: {}", webResource); - return webResource.request().accept(MediaType.APPLICATION_JSON).get(InspectExecResponse.class); + + return webResource.request().accept(MediaType.APPLICATION_JSON).get(new TypeReference() { + }).awaitResult(); } } diff --git a/src/main/java/com/github/dockerjava/netty/exec/InspectImageCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/InspectImageCmdExec.java index 0092b5ff9..b6f42414d 100644 --- a/src/main/java/com/github/dockerjava/netty/exec/InspectImageCmdExec.java +++ b/src/main/java/com/github/dockerjava/netty/exec/InspectImageCmdExec.java @@ -1,5 +1,8 @@ package com.github.dockerjava.netty.exec; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.fasterxml.jackson.core.type.TypeReference; import com.github.dockerjava.api.command.InspectImageCmd; import com.github.dockerjava.api.command.InspectImageResponse; @@ -7,9 +10,6 @@ import com.github.dockerjava.netty.MediaType; import com.github.dockerjava.netty.WebTarget; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - public class InspectImageCmdExec extends AbstrSyncDockerCmdExec implements InspectImageCmd.Exec { @@ -24,8 +24,9 @@ protected InspectImageResponse execute(InspectImageCmd command) { WebTarget webResource = getBaseResource().path("/images/{id}/json").resolveTemplate("id", command.getImageId()); LOGGER.trace("GET: {}", webResource); + return webResource.request().accept(MediaType.APPLICATION_JSON).get(new TypeReference() { - }); + }).awaitResult(); } } diff --git a/src/main/java/com/github/dockerjava/netty/exec/ListContainersCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/ListContainersCmdExec.java index 0f35c84e3..fe08c507d 100644 --- a/src/main/java/com/github/dockerjava/netty/exec/ListContainersCmdExec.java +++ b/src/main/java/com/github/dockerjava/netty/exec/ListContainersCmdExec.java @@ -1,5 +1,12 @@ package com.github.dockerjava.netty.exec; +import static com.google.common.net.UrlEscapers.urlPathSegmentEscaper; + +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.fasterxml.jackson.core.type.TypeReference; import com.github.dockerjava.api.command.ListContainersCmd; import com.github.dockerjava.api.model.Container; @@ -7,15 +14,6 @@ import com.github.dockerjava.netty.MediaType; import com.github.dockerjava.netty.WebTarget; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.List; - -import javax.ws.rs.core.GenericType; - -import static com.google.common.net.UrlEscapers.urlPathSegmentEscaper; - public class ListContainersCmdExec extends AbstrSyncDockerCmdExec> implements ListContainersCmd.Exec { @@ -43,8 +41,11 @@ protected List execute(ListContainersCmd command) { } LOGGER.trace("GET: {}", webTarget); + List containers = webTarget.request().accept(MediaType.APPLICATION_JSON) - .get(new TypeReference>() {}); + .get(new TypeReference>() { + }).awaitResult(); + LOGGER.trace("Response: {}", containers); return containers; diff --git a/src/main/java/com/github/dockerjava/netty/exec/RemoveContainerCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/RemoveContainerCmdExec.java index ff4a2240e..46f8e5d21 100644 --- a/src/main/java/com/github/dockerjava/netty/exec/RemoveContainerCmdExec.java +++ b/src/main/java/com/github/dockerjava/netty/exec/RemoveContainerCmdExec.java @@ -25,9 +25,7 @@ protected Void execute(RemoveContainerCmd command) { webTarget = booleanQueryParam(webTarget, "force", command.hasForceEnabled()); LOGGER.trace("DELETE: {}", webTarget); - webTarget.request().accept(MediaType.APPLICATION_JSON).delete(); - - return null; + return webTarget.request().accept(MediaType.APPLICATION_JSON).delete().awaitResult(); } } diff --git a/src/main/java/com/github/dockerjava/netty/exec/WaitContainerCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/WaitContainerCmdExec.java index f4f0076f3..d6e5eb0f6 100644 --- a/src/main/java/com/github/dockerjava/netty/exec/WaitContainerCmdExec.java +++ b/src/main/java/com/github/dockerjava/netty/exec/WaitContainerCmdExec.java @@ -3,15 +3,14 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.fasterxml.jackson.core.type.TypeReference; import com.github.dockerjava.api.async.ResultCallback; import com.github.dockerjava.api.command.WaitContainerCmd; import com.github.dockerjava.api.model.WaitResponse; import com.github.dockerjava.core.DockerClientConfig; -import com.github.dockerjava.core.async.JsonStreamProcessor; import com.github.dockerjava.netty.MediaType; import com.github.dockerjava.netty.WebTarget; - public class WaitContainerCmdExec extends AbstrAsyncDockerCmdExec implements WaitContainerCmd.Exec { @@ -28,7 +27,8 @@ protected Void execute0(WaitContainerCmd command, ResultCallback r LOGGER.trace("POST: {}", webTarget); - webTarget.request().accept(MediaType.APPLICATION_JSON).post(null, WaitResponse.class); + webTarget.request().accept(MediaType.APPLICATION_JSON).post(null, new TypeReference() { + }, resultCallback); return null; } diff --git a/src/main/java/com/github/dockerjava/netty/handler/AwaitObjectInboundHandler.java b/src/main/java/com/github/dockerjava/netty/handler/AwaitObjectInboundHandler.java deleted file mode 100644 index 543fc095b..000000000 --- a/src/main/java/com/github/dockerjava/netty/handler/AwaitObjectInboundHandler.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.github.dockerjava.netty.handler; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; - -import java.util.concurrent.CountDownLatch; - -public class AwaitObjectInboundHandler extends SimpleChannelInboundHandler { - - public AwaitObjectInboundHandler(Class clazz) { - super(clazz); - } - - private CountDownLatch countDownLatch = new CountDownLatch(1); - - private T object; - - @Override - protected void channelRead0(ChannelHandlerContext ctx, T msg) throws Exception { - object = msg; - countDownLatch.countDown(); - } - - public T await() { - try { - countDownLatch.await(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - return object; - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - cause.printStackTrace(); - ctx.close(); - } - -} diff --git a/src/main/java/com/github/dockerjava/netty/handler/DeserializeJsonInboundHandler.java b/src/main/java/com/github/dockerjava/netty/handler/DeserializeJsonInboundHandler.java deleted file mode 100644 index 994169a36..000000000 --- a/src/main/java/com/github/dockerjava/netty/handler/DeserializeJsonInboundHandler.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.github.dockerjava.netty.handler; - -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; - -public class DeserializeJsonInboundHandler extends SimpleChannelInboundHandler{ - - private Class clazz; - - public DeserializeJsonInboundHandler(Class clazz) { - this.clazz = clazz; - } - - @Override - protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { - byte[] buffer = new byte[msg.readableBytes()]; - msg.readBytes(buffer); - - T object = null; - try { - object = new ObjectMapper().readValue(buffer, clazz); - } catch (Exception e) { - throw new RuntimeException(e); - } - - ctx.fireChannelRead(object); - } -} diff --git a/src/main/java/com/github/dockerjava/netty/handler/FramedResponseStreamHandler.java b/src/main/java/com/github/dockerjava/netty/handler/FramedResponseStreamHandler.java index 7907a62b5..8a3a7f1d3 100644 --- a/src/main/java/com/github/dockerjava/netty/handler/FramedResponseStreamHandler.java +++ b/src/main/java/com/github/dockerjava/netty/handler/FramedResponseStreamHandler.java @@ -2,8 +2,10 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.util.concurrent.GenericFutureListener; import com.github.dockerjava.api.async.ResultCallback; import com.github.dockerjava.api.model.Frame; @@ -47,8 +49,20 @@ protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Excep } while (frame != null); + +// ctx.channel().closeFuture().addListener(new GenericFutureListener() { +// +// @Override +// public void operationComplete(ChannelFuture future) throws Exception { +// System.err.println("channel closed"); +// resultCallback.onComplete(); +// } +// }); + } + + private int read(byte[] buf, int offset, int length) { length = Math.min(rawBuffer.readableBytes(), length); rawBuffer.readBytes(buf, offset, length); diff --git a/src/main/java/com/github/dockerjava/netty/handler/HttpResponseHandler.java b/src/main/java/com/github/dockerjava/netty/handler/HttpResponseHandler.java index 0b1007869..548c13697 100644 --- a/src/main/java/com/github/dockerjava/netty/handler/HttpResponseHandler.java +++ b/src/main/java/com/github/dockerjava/netty/handler/HttpResponseHandler.java @@ -5,25 +5,39 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.http.HttpContent; +import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpObject; +import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.LastHttpContent; import java.nio.charset.Charset; import java.util.concurrent.CountDownLatch; +import com.github.dockerjava.api.BadRequestException; +import com.github.dockerjava.api.ConflictException; +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.InternalServerErrorException; +import com.github.dockerjava.api.NotAcceptableException; +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.NotModifiedException; +import com.github.dockerjava.api.UnauthorizedException; +import com.github.dockerjava.api.async.ResultCallback; + public class HttpResponseHandler extends SimpleChannelInboundHandler { private HttpResponse response; private ByteBuf errorBody = Unpooled.buffer(); - private CountDownLatch responseLatch = new CountDownLatch(1); + private HttpRequestProvider requestProvider; - private CountDownLatch errorBodyLatch = new CountDownLatch(1); + private ResultCallback resultCallback; - public HttpResponseHandler() { + public HttpResponseHandler(HttpRequestProvider requestProvider, ResultCallback resultCallback) { super(false); + this.requestProvider = requestProvider; + this.resultCallback = resultCallback; } @Override @@ -31,8 +45,6 @@ protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Ex if (msg instanceof HttpResponse) { response = (HttpResponse) msg; - responseLatch.countDown(); - } else if (msg instanceof HttpContent) { HttpContent content = (HttpContent) msg; @@ -50,7 +62,47 @@ protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Ex } if (content instanceof LastHttpContent) { - errorBodyLatch.countDown(); + try { + + switch (response.status().code()) { + case 101: + case 200: + case 201: + case 204: + break; + case 301: + case 302: + if (response.headers().contains(HttpHeaderNames.LOCATION)) { + String location = response.headers().get(HttpHeaderNames.LOCATION); + System.out.println("redirected to :" + location); + HttpRequest redirected = requestProvider.getHttpRequest(location); + + ctx.channel().writeAndFlush(redirected); + } + break; + case 304: + throw new NotModifiedException(getBodyAsMessage(errorBody)); + case 400: + throw new BadRequestException(getBodyAsMessage(errorBody)); + case 401: + throw new UnauthorizedException(getBodyAsMessage(errorBody)); + case 404: + throw new NotFoundException(getBodyAsMessage(errorBody)); + case 406: + throw new NotAcceptableException(getBodyAsMessage(errorBody)); + case 409: + throw new ConflictException(getBodyAsMessage(errorBody)); + case 500: + throw new InternalServerErrorException(getBodyAsMessage(errorBody)); + default: + throw new DockerException(getBodyAsMessage(errorBody), response.status().code()); + } + } catch (Throwable e) { + resultCallback.onError(e); + } finally { + System.err.println("LastHttpContent"); + resultCallback.onComplete(); + } } } else { System.err.println("UNKNOWN"); @@ -62,22 +114,4 @@ private String getBodyAsMessage(ByteBuf body) { return body.readBytes(body.readableBytes()).toString(Charset.forName("UTF-8")); } - public HttpResponse awaitResponse() { - try { - responseLatch.await(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - return response; - } - - public String awaitErrorBody() { - try { - errorBodyLatch.await(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - return getBodyAsMessage(errorBody); - } - } diff --git a/src/main/java/com/github/dockerjava/netty/handler/HttpResponseStreamInboundHandler.java b/src/main/java/com/github/dockerjava/netty/handler/HttpResponseStreamInboundHandler.java index 2f9974c16..f2a66c202 100644 --- a/src/main/java/com/github/dockerjava/netty/handler/HttpResponseStreamInboundHandler.java +++ b/src/main/java/com/github/dockerjava/netty/handler/HttpResponseStreamInboundHandler.java @@ -6,21 +6,16 @@ import java.io.IOException; import java.io.InputStream; -import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedTransferQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; public class HttpResponseStreamInboundHandler extends SimpleChannelInboundHandler { - private CountDownLatch latch = new CountDownLatch(1); - private HttpResponseInputStream stream = new HttpResponseInputStream(); @Override protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { - - latch.countDown(); stream.write(msg.copy()); } @@ -37,11 +32,6 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws E } public InputStream getInputStream() { - try { - latch.await(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } return stream; } diff --git a/src/main/java/com/github/dockerjava/netty/handler/SerializeJsonOutboundHandler.java b/src/main/java/com/github/dockerjava/netty/handler/JsonRequestHandler.java similarity index 87% rename from src/main/java/com/github/dockerjava/netty/handler/SerializeJsonOutboundHandler.java rename to src/main/java/com/github/dockerjava/netty/handler/JsonRequestHandler.java index 4505c02a9..d797ba188 100644 --- a/src/main/java/com/github/dockerjava/netty/handler/SerializeJsonOutboundHandler.java +++ b/src/main/java/com/github/dockerjava/netty/handler/JsonRequestHandler.java @@ -6,7 +6,7 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; -public class SerializeJsonOutboundHandler extends MessageToByteEncoder{ +public class JsonRequestHandler extends MessageToByteEncoder{ private ObjectMapper mapper = new ObjectMapper(); diff --git a/src/main/java/com/github/dockerjava/netty/handler/JsonResponseCallbackHandler.java b/src/main/java/com/github/dockerjava/netty/handler/JsonResponseCallbackHandler.java new file mode 100644 index 000000000..e6b1ef3a8 --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/handler/JsonResponseCallbackHandler.java @@ -0,0 +1,47 @@ +package com.github.dockerjava.netty.handler; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.dockerjava.api.async.ResultCallback; + +public class JsonResponseCallbackHandler extends SimpleChannelInboundHandler { + + private static ObjectMapper objectMapper = new ObjectMapper(); + + private TypeReference typeReference; + + private ResultCallback callback; + + public JsonResponseCallbackHandler(TypeReference typeReference, ResultCallback callback) { + this.typeReference = typeReference; + this.callback = callback; + } + + @Override + protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { + byte[] buffer = new byte[msg.readableBytes()]; + msg.readBytes(buffer); + + T object = null; + + try { + object = objectMapper.readValue(buffer, typeReference); + } catch (Exception e) { + callback.onError(e); + throw new RuntimeException(e); + } + + callback.onNext(object); + callback.close(); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + callback.onError(cause); + ctx.close(); + } +} diff --git a/src/main/java/com/github/dockerjava/netty/handler/JsonResponseHandler.java b/src/main/java/com/github/dockerjava/netty/handler/JsonResponseHandler.java new file mode 100644 index 000000000..efbd74f1f --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/handler/JsonResponseHandler.java @@ -0,0 +1,62 @@ +package com.github.dockerjava.netty.handler; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; + +import java.util.concurrent.CountDownLatch; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class JsonResponseHandler extends SimpleChannelInboundHandler { + + private static ObjectMapper objectMapper = new ObjectMapper(); + + private TypeReference typeReference; + + private CountDownLatch countDownLatch = new CountDownLatch(1); + + private T object; + + public JsonResponseHandler(TypeReference typeReference) { + this.typeReference = typeReference; + } + + @Override + protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { + System.err.println("AwaitObjectInboundHandler: channelRead0: " + msg); + byte[] buffer = new byte[msg.readableBytes()]; + msg.readBytes(buffer); + + try { + object = objectMapper.readValue(buffer, typeReference); + } catch (Exception e) { + throw new RuntimeException(e); + } + + countDownLatch.countDown(); + } + + @Override + public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { + System.err.println("AwaitObjectInboundHandler: channelReadComplete"); + super.channelReadComplete(ctx); + } + + public T await() { + try { + countDownLatch.await(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + return object; + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + cause.printStackTrace(); + ctx.close(); + } + +} diff --git a/src/test/java/com/github/dockerjava/client/AbstractDockerClientTest.java b/src/test/java/com/github/dockerjava/client/AbstractDockerClientTest.java index ec4aa82be..81149c741 100644 --- a/src/test/java/com/github/dockerjava/client/AbstractDockerClientTest.java +++ b/src/test/java/com/github/dockerjava/client/AbstractDockerClientTest.java @@ -199,7 +199,8 @@ public static class LogContainerTestCallback extends LogContainerResultCallback @Override public void onNext(Frame frame) { log.append(new String(frame.getPayload())); - super.onNext(frame); + System.err.println("LogContainerTestCallback: " + log.toString()); + //super.onNext(frame); } @Override diff --git a/src/test/java/com/github/dockerjava/netty/AbstractDockerClientTest.java b/src/test/java/com/github/dockerjava/netty/AbstractDockerClientTest.java index b9a914593..e178362a2 100644 --- a/src/test/java/com/github/dockerjava/netty/AbstractDockerClientTest.java +++ b/src/test/java/com/github/dockerjava/netty/AbstractDockerClientTest.java @@ -206,6 +206,14 @@ public void onNext(Frame frame) { public String toString() { return log.toString(); } + + @Override + public LogContainerResultCallback awaitCompletion() throws InterruptedException { + LogContainerResultCallback result = super.awaitCompletion(); + System.err.println("awaitCompletion returned"); + + return result; + } } protected String buildImage(File baseDir) throws Exception { diff --git a/src/test/java/com/github/dockerjava/netty/exec/CreateContainerCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/CreateContainerCmdExecTest.java index 43400edfc..2660aff5a 100644 --- a/src/test/java/com/github/dockerjava/netty/exec/CreateContainerCmdExecTest.java +++ b/src/test/java/com/github/dockerjava/netty/exec/CreateContainerCmdExecTest.java @@ -230,13 +230,6 @@ public void createContainerWithName() throws DockerException { InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); assertThat(inspectContainerResponse.getName(), equalTo("/container")); - - try { - dockerClient.createContainerCmd("busybox").withName("container").withCmd("env").exec(); - fail("Expected ConflictException"); - } catch (ConflictException e) { - } - } @Test diff --git a/src/test/java/com/github/dockerjava/netty/exec/InspectContainerCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/InspectContainerCmdExecTest.java new file mode 100644 index 000000000..dde8b3ab9 --- /dev/null +++ b/src/test/java/com/github/dockerjava/netty/exec/InspectContainerCmdExecTest.java @@ -0,0 +1,81 @@ +package com.github.dockerjava.netty.exec; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.Matchers.not; + +import java.lang.reflect.Method; +import java.security.SecureRandom; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.command.ExecCreateCmdResponse; +import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.api.command.InspectExecResponse; +import com.github.dockerjava.core.command.ExecStartResultCallback; +import com.github.dockerjava.netty.AbstractDockerClientTest; +import com.github.dockerjava.test.serdes.JSONTestHelper; + +@Test(groups = "integration") +public class InspectContainerCmdExecTest extends AbstractDockerClientTest { + + public static final Logger LOG = LoggerFactory.getLogger(InspectContainerCmdExecTest.class); + + @BeforeTest + public void beforeTest() throws Exception { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test() + public void inspectContainer() throws DockerException { + + String containerName = "generated_" + new SecureRandom().nextInt(); + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("top") + .withName(containerName).exec(); + LOG.info("Created container {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse containerInfo = dockerClient.inspectContainerCmd(container.getId()).exec(); + assertEquals(containerInfo.getId(), container.getId()); + + } + + @Test + public void inspectNonExistingContainer() throws DockerException { + + try { + dockerClient.inspectContainerCmd("non-existing").exec(); + fail("expected NotFoundException"); + } catch (NotFoundException e) { + } + } + +} diff --git a/src/test/java/com/github/dockerjava/netty/exec/ListContainersCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/ListContainersCmdExecTest.java new file mode 100644 index 000000000..3f97aa69d --- /dev/null +++ b/src/test/java/com/github/dockerjava/netty/exec/ListContainersCmdExecTest.java @@ -0,0 +1,171 @@ +package com.github.dockerjava.netty.exec; + +import static ch.lambdaj.Lambda.filter; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.startsWith; +import static org.testinfected.hamcrest.jpa.HasFieldWithValue.hasField; + +import java.lang.reflect.Method; +import java.util.List; +import java.util.Map; + +import org.hamcrest.Matcher; +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.api.model.Container; +import com.github.dockerjava.api.model.Filters; +import com.github.dockerjava.core.command.PullImageResultCallback; +import com.github.dockerjava.netty.AbstractDockerClientTest; +import com.google.common.collect.ImmutableMap; + +@Test(groups = "integration") +public class ListContainersCmdExecTest extends AbstractDockerClientTest { + + @BeforeTest + public void beforeTest() throws Exception { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + public void testListContainers() throws Exception { + + String testImage = "busybox"; + +// // need to block until image is pulled completely +// dockerClient.pullImageCmd(testImage).exec(new PullImageResultCallback()).awaitSuccess(); + + List containers = dockerClient.listContainersCmd().withShowAll(true).exec(); + assertThat(containers, notNullValue()); + LOG.info("Container List: {}", containers); + + int size = containers.size(); + + CreateContainerResponse container1 = dockerClient.createContainerCmd(testImage).withCmd("echo").exec(); + + assertThat(container1.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container1.getId()).exec(); + + assertThat(inspectContainerResponse.getConfig().getImage(), is(equalTo(testImage))); + + dockerClient.startContainerCmd(container1.getId()).exec(); + + LOG.info("container id: " + container1.getId()); + + List containers2 = dockerClient.listContainersCmd().withShowAll(true).exec(); + + for (Container container : containers2) { + LOG.info("listContainer: id=" + container.getId() + " image=" + container.getImage()); + } + + assertThat(size + 1, is(equalTo(containers2.size()))); + Matcher matcher = hasItem(hasField("id", startsWith(container1.getId()))); + assertThat(containers2, matcher); + + List filteredContainers = filter(hasField("id", startsWith(container1.getId())), containers2); + assertThat(filteredContainers.size(), is(equalTo(1))); + + for (Container container : filteredContainers) { + LOG.info("filteredContainer: " + container); + } + + Container container2 = filteredContainers.get(0); + assertThat(container2.getCommand(), not(isEmptyString())); + assertThat(container2.getImage(), startsWith(testImage)); + } + + @Test + public void testListContainersWithLabelsFilter() throws Exception { + + String testImage = "busybox"; + + // need to block until image is pulled completely + dockerClient.pullImageCmd(testImage).exec(new PullImageResultCallback()).awaitCompletion(); + + List containers = dockerClient.listContainersCmd().withShowAll(true).exec(); + assertThat(containers, notNullValue()); + LOG.info("Container List: {}", containers); + + int size = containers.size(); + + CreateContainerResponse container1 = dockerClient.createContainerCmd(testImage).withCmd("echo").exec(); + + assertThat(container1.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container1.getId()).exec(); + + assertThat(inspectContainerResponse.getConfig().getImage(), is(equalTo(testImage))); + + dockerClient.startContainerCmd(container1.getId()).exec(); + + LOG.info("container id: " + container1.getId()); + + List containers2 = dockerClient.listContainersCmd().withShowAll(true).exec(); + + for (Container container : containers2) { + LOG.info("listContainer: id=" + container.getId() + " image=" + container.getImage()); + } + + assertThat(size + 1, is(equalTo(containers2.size()))); + Matcher matcher = hasItem(hasField("id", startsWith(container1.getId()))); + assertThat(containers2, matcher); + + List filteredContainers = filter(hasField("id", startsWith(container1.getId())), containers2); + assertThat(filteredContainers.size(), is(equalTo(1))); + + for (Container container : filteredContainers) { + LOG.info("filteredContainer: " + container); + } + + Container container2 = filteredContainers.get(0); + assertThat(container2.getCommand(), not(isEmptyString())); + assertThat(container2.getImage(), startsWith(testImage)); + + Map labels = ImmutableMap.of("test", "docker-java"); + + // list with filter by label + dockerClient.createContainerCmd(testImage).withCmd("echo").withLabels(labels).exec(); + filteredContainers = dockerClient.listContainersCmd().withShowAll(true) + .withFilters(new Filters().withLabels(labels)).exec(); + assertThat(filteredContainers.size(), is(equalTo(1))); + Container container3 = filteredContainers.get(0); + assertThat(container3.getCommand(), not(isEmptyString())); + assertThat(container3.getImage(), startsWith(testImage)); + + filteredContainers = dockerClient.listContainersCmd().withShowAll(true) + .withFilters(new Filters().withLabels("test")).exec(); + assertThat(filteredContainers.size(), is(equalTo(1))); + container3 = filteredContainers.get(0); + assertThat(container3.getCommand(), not(isEmptyString())); + assertThat(container3.getImage(), startsWith(testImage)); + assertEquals(container3.getLabels(), labels); + } + +} diff --git a/src/test/java/com/github/dockerjava/netty/exec/LogContainerCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/LogContainerCmdExecTest.java index a9a48dc58..98c1043e1 100644 --- a/src/test/java/com/github/dockerjava/netty/exec/LogContainerCmdExecTest.java +++ b/src/test/java/com/github/dockerjava/netty/exec/LogContainerCmdExecTest.java @@ -17,8 +17,8 @@ import com.github.dockerjava.api.NotFoundException; import com.github.dockerjava.api.command.CreateContainerResponse; -import com.github.dockerjava.client.AbstractDockerClientTest; import com.github.dockerjava.core.command.WaitContainerResultCallback; +import com.github.dockerjava.netty.AbstractDockerClientTest; @Test(groups = "integration") public class LogContainerCmdExecTest extends AbstractDockerClientTest { diff --git a/src/test/java/com/github/dockerjava/netty/exec/WaitContainerCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/WaitContainerCmdExecTest.java index ba1dd518e..d28f45224 100644 --- a/src/test/java/com/github/dockerjava/netty/exec/WaitContainerCmdExecTest.java +++ b/src/test/java/com/github/dockerjava/netty/exec/WaitContainerCmdExecTest.java @@ -96,6 +96,8 @@ public void testWaitContainerAbort() throws Exception { Thread.sleep(5000); + System.err.println("closing callback"); + callback.close(); dockerClient.killContainerCmd(container.getId()).exec(); From f0dc0c7b3241b726e2cf736f0c28f60abe4931c4 Mon Sep 17 00:00:00 2001 From: Marcus Linke Date: Sun, 29 Nov 2015 13:53:04 +0100 Subject: [PATCH 08/13] Added missing exec implementations as well as appropriate tests --- .../dockerjava/api/command/ExecCreateCmd.java | 2 + .../dockerjava/api/command/ExecStartCmd.java | 6 +- .../core/async/ResultCallbackTemplate.java | 1 + .../core/command/ExecCreateCmdImpl.java | 3 + .../core/command/ExecStartCmdImpl.java | 19 + .../core/dockerfile/Dockerfile.java | 11 +- .../netty/DockerCmdExecFactoryImpl.java | 88 ++- .../dockerjava/netty/InvocationBuilder.java | 124 +++- .../github/dockerjava/netty/WebTarget.java | 2 - .../netty/exec/AbstrAsyncDockerCmdExec.java | 10 +- .../netty/exec/AttachContainerCmdExec.java | 39 ++ .../netty/exec/BuildImageCmdExec.java | 93 +++ .../dockerjava/netty/exec/CommitCmdExec.java | 36 ++ .../netty/exec/ContainerDiffCmdExec.java | 34 ++ .../netty/exec/CreateContainerCmdExec.java | 8 +- .../netty/exec/CreateImageCmdExec.java | 31 + .../dockerjava/netty/exec/EventsCmdExec.java | 41 ++ .../netty/exec/ExecStartCmdExec.java | 10 +- .../netty/exec/ListImagesCmdExec.java | 45 ++ .../netty/exec/PauseContainerCmdExec.java | 31 + .../dockerjava/netty/exec/PingCmdExec.java | 28 + .../netty/exec/PullImageCmdExec.java | 46 ++ .../netty/exec/PushImageCmdExec.java | 48 ++ .../netty/exec/RemoveImageCmdExec.java | 31 + .../netty/exec/RestartContainerCmdExec.java | 36 ++ .../netty/exec/SaveImageCmdExec.java | 28 + .../netty/exec/SearchImagesCmdExec.java | 33 ++ .../netty/exec/StartContainerCmdExec.java | 8 +- .../dockerjava/netty/exec/StatsCmdExec.java | 33 ++ .../netty/exec/TagImageCmdExec.java | 30 + .../netty/exec/TopContainerCmdExec.java | 36 ++ .../netty/exec/UnpauseContainerCmdExec.java | 31 + .../dockerjava/netty/exec/VersionCmdExec.java | 30 + .../netty/exec/WaitContainerCmdExec.java | 2 +- .../handler/FramedResponseStreamHandler.java | 163 +++--- .../handler/HttpConnectionHijackHandler.java | 67 +-- .../netty/handler/HttpResponseHandler.java | 7 - ...er.java => HttpResponseStreamHandler.java} | 18 +- .../netty/handler/JsonRequestHandler.java | 7 +- .../handler/JsonResponseCallbackHandler.java | 1 - .../exec/AttachContainerCmdExecTest.java | 122 ++++ .../netty/exec/BuildImageCmdExecTest.java | 296 ++++++++++ .../netty/exec/CommitCmdExecTest.java | 80 +++ .../netty/exec/ContainerDiffCmdExecTest.java | 80 +++ .../CopyFileFromContainerCmdExecTest.java | 73 +++ .../netty/exec/EventsCmdExecTest.java | 162 +++++ .../netty/exec/ExecStartCmdExecTest.java | 49 ++ .../exec/InspectContainerCmdExecTest.java | 6 - .../netty/exec/InspectExecCmdExecTest.java | 18 +- .../netty/exec/ListContainersCmdExecTest.java | 4 +- .../netty/exec/ListImagesCmdExecTest.java | 102 ++++ .../netty/exec/PingCmdExecTest.java | 43 ++ .../netty/exec/PullImageCmdExecTest.java | 119 ++++ .../netty/exec/PushImageCmdExecTest.java | 79 +++ .../exec/RemoveContainerCmdExecTest.java | 81 +++ .../netty/exec/RemoveImageCmdExecTest.java | 88 +++ .../exec/RestartContainerCmdExecTest.java | 84 +++ .../netty/exec/SaveImageCmdExecTest.java | 55 ++ .../netty/exec/SearchImagesCmdExecTest.java | 59 ++ .../netty/exec/StartContainerCmdExecTest.java | 553 ++++++++++++++++++ .../netty/exec/StatsCmdExecTest.java | 106 ++++ .../netty/exec/TagImageCmdExecTest.java | 63 ++ .../netty/exec/VersionCmdExecTest.java | 52 ++ 63 files changed, 3443 insertions(+), 248 deletions(-) create mode 100644 src/main/java/com/github/dockerjava/netty/exec/AttachContainerCmdExec.java create mode 100644 src/main/java/com/github/dockerjava/netty/exec/BuildImageCmdExec.java create mode 100644 src/main/java/com/github/dockerjava/netty/exec/CommitCmdExec.java create mode 100644 src/main/java/com/github/dockerjava/netty/exec/ContainerDiffCmdExec.java create mode 100644 src/main/java/com/github/dockerjava/netty/exec/CreateImageCmdExec.java create mode 100644 src/main/java/com/github/dockerjava/netty/exec/EventsCmdExec.java create mode 100644 src/main/java/com/github/dockerjava/netty/exec/ListImagesCmdExec.java create mode 100644 src/main/java/com/github/dockerjava/netty/exec/PauseContainerCmdExec.java create mode 100644 src/main/java/com/github/dockerjava/netty/exec/PingCmdExec.java create mode 100644 src/main/java/com/github/dockerjava/netty/exec/PullImageCmdExec.java create mode 100644 src/main/java/com/github/dockerjava/netty/exec/PushImageCmdExec.java create mode 100644 src/main/java/com/github/dockerjava/netty/exec/RemoveImageCmdExec.java create mode 100644 src/main/java/com/github/dockerjava/netty/exec/RestartContainerCmdExec.java create mode 100644 src/main/java/com/github/dockerjava/netty/exec/SaveImageCmdExec.java create mode 100644 src/main/java/com/github/dockerjava/netty/exec/SearchImagesCmdExec.java create mode 100644 src/main/java/com/github/dockerjava/netty/exec/StatsCmdExec.java create mode 100644 src/main/java/com/github/dockerjava/netty/exec/TagImageCmdExec.java create mode 100644 src/main/java/com/github/dockerjava/netty/exec/TopContainerCmdExec.java create mode 100644 src/main/java/com/github/dockerjava/netty/exec/UnpauseContainerCmdExec.java create mode 100644 src/main/java/com/github/dockerjava/netty/exec/VersionCmdExec.java rename src/main/java/com/github/dockerjava/netty/handler/{HttpResponseStreamInboundHandler.java => HttpResponseStreamHandler.java} (87%) create mode 100644 src/test/java/com/github/dockerjava/netty/exec/AttachContainerCmdExecTest.java create mode 100644 src/test/java/com/github/dockerjava/netty/exec/BuildImageCmdExecTest.java create mode 100644 src/test/java/com/github/dockerjava/netty/exec/CommitCmdExecTest.java create mode 100644 src/test/java/com/github/dockerjava/netty/exec/ContainerDiffCmdExecTest.java create mode 100644 src/test/java/com/github/dockerjava/netty/exec/CopyFileFromContainerCmdExecTest.java create mode 100644 src/test/java/com/github/dockerjava/netty/exec/EventsCmdExecTest.java create mode 100644 src/test/java/com/github/dockerjava/netty/exec/ListImagesCmdExecTest.java create mode 100644 src/test/java/com/github/dockerjava/netty/exec/PingCmdExecTest.java create mode 100644 src/test/java/com/github/dockerjava/netty/exec/PullImageCmdExecTest.java create mode 100644 src/test/java/com/github/dockerjava/netty/exec/PushImageCmdExecTest.java create mode 100644 src/test/java/com/github/dockerjava/netty/exec/RemoveContainerCmdExecTest.java create mode 100644 src/test/java/com/github/dockerjava/netty/exec/RemoveImageCmdExecTest.java create mode 100644 src/test/java/com/github/dockerjava/netty/exec/RestartContainerCmdExecTest.java create mode 100644 src/test/java/com/github/dockerjava/netty/exec/SaveImageCmdExecTest.java create mode 100644 src/test/java/com/github/dockerjava/netty/exec/SearchImagesCmdExecTest.java create mode 100644 src/test/java/com/github/dockerjava/netty/exec/StartContainerCmdExecTest.java create mode 100644 src/test/java/com/github/dockerjava/netty/exec/StatsCmdExecTest.java create mode 100644 src/test/java/com/github/dockerjava/netty/exec/TagImageCmdExecTest.java create mode 100644 src/test/java/com/github/dockerjava/netty/exec/VersionCmdExecTest.java diff --git a/src/main/java/com/github/dockerjava/api/command/ExecCreateCmd.java b/src/main/java/com/github/dockerjava/api/command/ExecCreateCmd.java index 67ffe5818..3fb227ff8 100644 --- a/src/main/java/com/github/dockerjava/api/command/ExecCreateCmd.java +++ b/src/main/java/com/github/dockerjava/api/command/ExecCreateCmd.java @@ -1,5 +1,7 @@ package com.github.dockerjava.api.command; +import java.io.InputStream; + import javax.annotation.CheckForNull; import javax.annotation.Nonnull; diff --git a/src/main/java/com/github/dockerjava/api/command/ExecStartCmd.java b/src/main/java/com/github/dockerjava/api/command/ExecStartCmd.java index 2129bf40c..b883dee0e 100644 --- a/src/main/java/com/github/dockerjava/api/command/ExecStartCmd.java +++ b/src/main/java/com/github/dockerjava/api/command/ExecStartCmd.java @@ -5,7 +5,6 @@ import javax.annotation.CheckForNull; import javax.annotation.Nonnull; -import com.github.dockerjava.api.NotFoundException; import com.github.dockerjava.api.async.ResultCallback; import com.github.dockerjava.api.model.Frame; @@ -20,12 +19,17 @@ public interface ExecStartCmd extends AsyncDockerCmd { @CheckForNull public Boolean hasTtyEnabled(); + @CheckForNull + public InputStream getStdin(); + public ExecStartCmd withDetach(Boolean detach); public ExecStartCmd withExecId(@Nonnull String execId); public ExecStartCmd withTty(Boolean tty); + public ExecStartCmd withStdIn(InputStream stdin); + /** * * @throws com.github.dockerjava.api.NotFoundException diff --git a/src/main/java/com/github/dockerjava/core/async/ResultCallbackTemplate.java b/src/main/java/com/github/dockerjava/core/async/ResultCallbackTemplate.java index 44af625af..4db4edcea 100644 --- a/src/main/java/com/github/dockerjava/core/async/ResultCallbackTemplate.java +++ b/src/main/java/com/github/dockerjava/core/async/ResultCallbackTemplate.java @@ -63,6 +63,7 @@ public void onError(Throwable throwable) { @Override public void onComplete() { + System.err.println("callback onComplete"); try { close(); } catch (IOException e) { diff --git a/src/main/java/com/github/dockerjava/core/command/ExecCreateCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/ExecCreateCmdImpl.java index 5df38860c..3e68db1e4 100644 --- a/src/main/java/com/github/dockerjava/core/command/ExecCreateCmdImpl.java +++ b/src/main/java/com/github/dockerjava/core/command/ExecCreateCmdImpl.java @@ -2,6 +2,9 @@ import static com.google.common.base.Preconditions.checkNotNull; +import java.io.InputStream; + +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonInclude.Include; diff --git a/src/main/java/com/github/dockerjava/core/command/ExecStartCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/ExecStartCmdImpl.java index 0871fddfb..a70bac7d7 100644 --- a/src/main/java/com/github/dockerjava/core/command/ExecStartCmdImpl.java +++ b/src/main/java/com/github/dockerjava/core/command/ExecStartCmdImpl.java @@ -2,7 +2,11 @@ import static com.google.common.base.Preconditions.checkNotNull; +import java.io.InputStream; + +import com.fasterxml.jackson.annotation.JsonIgnore; import com.github.dockerjava.api.async.ResultCallback; +import com.github.dockerjava.api.command.ExecCreateCmd; import com.github.dockerjava.api.command.ExecStartCmd; import com.github.dockerjava.api.model.Frame; @@ -12,6 +16,8 @@ public class ExecStartCmdImpl extends AbstrAsyncDockerCmd i private Boolean detach, tty; + private InputStream stdin; + public ExecStartCmdImpl(ExecStartCmd.Exec exec, String execId) { super(exec); withExecId(execId); @@ -39,6 +45,13 @@ public Boolean hasTtyEnabled() { return tty; } + @Override + @JsonIgnore + public InputStream getStdin() { + return stdin; + } + + @Override public ExecStartCmd withDetach(Boolean detach) { this.detach = detach; @@ -51,6 +64,12 @@ public ExecStartCmd withTty(Boolean tty) { return this; } + @Override + public ExecStartCmd withStdIn(InputStream stdin) { + this.stdin = stdin; + return this; + } + /** * @throws com.github.dockerjava.api.NotFoundException * No such exec instance diff --git a/src/main/java/com/github/dockerjava/core/dockerfile/Dockerfile.java b/src/main/java/com/github/dockerjava/core/dockerfile/Dockerfile.java index c9bd37d1e..032b81cb7 100644 --- a/src/main/java/com/github/dockerjava/core/dockerfile/Dockerfile.java +++ b/src/main/java/com/github/dockerjava/core/dockerfile/Dockerfile.java @@ -17,6 +17,7 @@ import org.apache.commons.io.filefilter.TrueFileFilter; import java.io.File; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; @@ -133,10 +134,18 @@ public InputStream buildDockerFolderTar(File directory) { dockerFolderTar = CompressArchiveUtil.archiveTARFiles(directory, filesToAdd, archiveNameWithOutExtension); - final InputStream tarInputStream = FileUtils.openInputStream(dockerFolderTar); + long length = dockerFolderTar.length(); + + final FileInputStream tarInputStream = FileUtils.openInputStream(dockerFolderTar); final File tarFile = dockerFolderTar; return new InputStream() { + + @Override + public int available() throws IOException { + return tarInputStream.available(); + } + @Override public int read() throws IOException { return tarInputStream.read(); diff --git a/src/main/java/com/github/dockerjava/netty/DockerCmdExecFactoryImpl.java b/src/main/java/com/github/dockerjava/netty/DockerCmdExecFactoryImpl.java index 37d2534c5..303f8ddfc 100644 --- a/src/main/java/com/github/dockerjava/netty/DockerCmdExecFactoryImpl.java +++ b/src/main/java/com/github/dockerjava/netty/DockerCmdExecFactoryImpl.java @@ -74,9 +74,15 @@ import com.github.dockerjava.core.command.ExecStartCmdImpl; import com.github.dockerjava.core.command.ExecStartResultCallback; import com.github.dockerjava.core.command.InfoCmdImpl; +import com.github.dockerjava.netty.exec.AttachContainerCmdExec; import com.github.dockerjava.netty.exec.AuthCmdExec; +import com.github.dockerjava.netty.exec.BuildImageCmdExec; +import com.github.dockerjava.netty.exec.CommitCmdExec; +import com.github.dockerjava.netty.exec.ContainerDiffCmdExec; import com.github.dockerjava.netty.exec.CopyFileFromContainerCmdExec; import com.github.dockerjava.netty.exec.CreateContainerCmdExec; +import com.github.dockerjava.netty.exec.CreateImageCmdExec; +import com.github.dockerjava.netty.exec.EventsCmdExec; import com.github.dockerjava.netty.exec.ExecCreateCmdExec; import com.github.dockerjava.netty.exec.ExecStartCmdExec; import com.github.dockerjava.netty.exec.InfoCmdExec; @@ -85,16 +91,30 @@ import com.github.dockerjava.netty.exec.InspectImageCmdExec; import com.github.dockerjava.netty.exec.KillContainerCmdExec; import com.github.dockerjava.netty.exec.ListContainersCmdExec; +import com.github.dockerjava.netty.exec.ListImagesCmdExec; import com.github.dockerjava.netty.exec.LogContainerCmdExec; +import com.github.dockerjava.netty.exec.PauseContainerCmdExec; +import com.github.dockerjava.netty.exec.PingCmdExec; +import com.github.dockerjava.netty.exec.PullImageCmdExec; +import com.github.dockerjava.netty.exec.PushImageCmdExec; import com.github.dockerjava.netty.exec.RemoveContainerCmdExec; +import com.github.dockerjava.netty.exec.RemoveImageCmdExec; +import com.github.dockerjava.netty.exec.RestartContainerCmdExec; +import com.github.dockerjava.netty.exec.SaveImageCmdExec; +import com.github.dockerjava.netty.exec.SearchImagesCmdExec; import com.github.dockerjava.netty.exec.StartContainerCmdExec; +import com.github.dockerjava.netty.exec.StatsCmdExec; import com.github.dockerjava.netty.exec.StopContainerCmdExec; +import com.github.dockerjava.netty.exec.TagImageCmdExec; +import com.github.dockerjava.netty.exec.TopContainerCmdExec; +import com.github.dockerjava.netty.exec.UnpauseContainerCmdExec; +import com.github.dockerjava.netty.exec.VersionCmdExec; import com.github.dockerjava.netty.exec.WaitContainerCmdExec; /** - * http://stackoverflow.com/questions/33296749/netty-connect-to-unix-domain- socket-failed + * http://stackoverflow.com/questions/33296749/netty-connect-to-unix-domain-socket-failed * http://netty.io/wiki/native-transports.html - * https://github.com/netty/netty/blob/master/example/src/main/java/io/netty/ example/http/snoop/HttpSnoopClient.java + * https://github.com/netty/netty/blob/master/example/src/main/java/io/netty/example/http/snoop/HttpSnoopClient.java * * @author marcus * @@ -271,56 +291,47 @@ public InfoCmd.Exec createInfoCmdExec() { @Override public PingCmd.Exec createPingCmdExec() { - return null; // new PingCmdExec(getBaseResource(), - // getDockerClientConfig()); + return new PingCmdExec(getBaseResource(), getDockerClientConfig()); } @Override public VersionCmd.Exec createVersionCmdExec() { - return null; // new VersionCmdExec(getBaseResource(), - // getDockerClientConfig()); + return new VersionCmdExec(getBaseResource(), getDockerClientConfig()); } @Override public PullImageCmd.Exec createPullImageCmdExec() { - return null; // new PullImageCmdExec(getBaseResource(), - // getDockerClientConfig()); + return new PullImageCmdExec(getBaseResource(), getDockerClientConfig()); } @Override public PushImageCmd.Exec createPushImageCmdExec() { - return null; // new PushImageCmdExec(getBaseResource(), - // getDockerClientConfig()); + return new PushImageCmdExec(getBaseResource(), getDockerClientConfig()); } @Override public SaveImageCmd.Exec createSaveImageCmdExec() { - return null; // new SaveImageCmdExec(getBaseResource(), - // getDockerClientConfig()); + return new SaveImageCmdExec(getBaseResource(), getDockerClientConfig()); } @Override public CreateImageCmd.Exec createCreateImageCmdExec() { - return null; // new CreateImageCmdExec(getBaseResource(), - // getDockerClientConfig()); + return new CreateImageCmdExec(getBaseResource(), getDockerClientConfig()); } @Override public SearchImagesCmd.Exec createSearchImagesCmdExec() { - return null; // new SearchImagesCmdExec(getBaseResource(), - // getDockerClientConfig()); + return new SearchImagesCmdExec(getBaseResource(), getDockerClientConfig()); } @Override public RemoveImageCmd.Exec createRemoveImageCmdExec() { - return null; // new RemoveImageCmdExec(getBaseResource(), - // getDockerClientConfig()); + return new RemoveImageCmdExec(getBaseResource(), getDockerClientConfig()); } @Override public ListImagesCmd.Exec createListImagesCmdExec() { - return null; // new ListImagesCmdExec(getBaseResource(), - // getDockerClientConfig()); + return new ListImagesCmdExec(getBaseResource(), getDockerClientConfig()); } @Override @@ -365,8 +376,7 @@ public WaitContainerCmd.Exec createWaitContainerCmdExec() { @Override public AttachContainerCmd.Exec createAttachContainerCmdExec() { - return null; // new AttachContainerCmdExec(getBaseResource(), - // getDockerClientConfig()); + return new AttachContainerCmdExec(getBaseResource(), getDockerClientConfig()); } @Override @@ -396,8 +406,7 @@ public StopContainerCmd.Exec createStopContainerCmdExec() { @Override public ContainerDiffCmd.Exec createContainerDiffCmdExec() { - return null; // new ContainerDiffCmdExec(getBaseResource(), - // getDockerClientConfig()); + return new ContainerDiffCmdExec(getBaseResource(), getDockerClientConfig()); } @Override @@ -407,63 +416,52 @@ public KillContainerCmd.Exec createKillContainerCmdExec() { @Override public RestartContainerCmd.Exec createRestartContainerCmdExec() { - return null; // new RestartContainerCmdExec(getBaseResource(), - // getDockerClientConfig()); + return new RestartContainerCmdExec(getBaseResource(), getDockerClientConfig()); } @Override public CommitCmd.Exec createCommitCmdExec() { - return null; // new CommitCmdExec(getBaseResource(), - // getDockerClientConfig()); + return new CommitCmdExec(getBaseResource(), getDockerClientConfig()); } @Override public BuildImageCmd.Exec createBuildImageCmdExec() { - return null; // new BuildImageCmdExec(getBaseResource(), - // getDockerClientConfig()); + return new BuildImageCmdExec(getBaseResource(), getDockerClientConfig()); } @Override public TopContainerCmd.Exec createTopContainerCmdExec() { - return null; // new TopContainerCmdExec(getBaseResource(), - // getDockerClientConfig()); + return new TopContainerCmdExec(getBaseResource(), getDockerClientConfig()); } @Override public TagImageCmd.Exec createTagImageCmdExec() { - return null; // new TagImageCmdExec(getBaseResource(), - // getDockerClientConfig()); + return new TagImageCmdExec(getBaseResource(), getDockerClientConfig()); } @Override public PauseContainerCmd.Exec createPauseContainerCmdExec() { - return null; // new PauseContainerCmdExec(getBaseResource(), - // getDockerClientConfig()); + return new PauseContainerCmdExec(getBaseResource(), getDockerClientConfig()); } @Override public UnpauseContainerCmd.Exec createUnpauseContainerCmdExec() { - return null; // new UnpauseContainerCmdExec(getBaseResource(), - // getDockerClientConfig()); + return new UnpauseContainerCmdExec(getBaseResource(), getDockerClientConfig()); } @Override public EventsCmd.Exec createEventsCmdExec() { - return null; // new EventsCmdExec(getBaseResource(), - // getDockerClientConfig()); + return new EventsCmdExec(getBaseResource(), getDockerClientConfig()); } @Override public StatsCmd.Exec createStatsCmdExec() { - return null; // new StatsCmdExec(getBaseResource(), - // getDockerClientConfig()); + return new StatsCmdExec(getBaseResource(), getDockerClientConfig()); } @Override public void close() throws IOException { - // checkNotNull(client, "Factory not initialized. You probably forgot to - // call init()!"); - // client.close(); + checkNotNull(eventLoopGroup, "Factory not initialized. You probably forgot to call init()!"); eventLoopGroup.shutdownGracefully(); } diff --git a/src/main/java/com/github/dockerjava/netty/InvocationBuilder.java b/src/main/java/com/github/dockerjava/netty/InvocationBuilder.java index 943875270..e8c358267 100644 --- a/src/main/java/com/github/dockerjava/netty/InvocationBuilder.java +++ b/src/main/java/com/github/dockerjava/netty/InvocationBuilder.java @@ -5,6 +5,7 @@ import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.handler.codec.http.DefaultFullHttpRequest; +import io.netty.handler.codec.http.DefaultHttpRequest; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.HttpClientCodec; import io.netty.handler.codec.http.HttpClientUpgradeHandler; @@ -13,8 +14,12 @@ import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpVersion; +import io.netty.handler.codec.http.LastHttpContent; import io.netty.handler.codec.json.JsonObjectDecoder; +import io.netty.handler.stream.ChunkedStream; +import io.netty.handler.stream.ChunkedWriteHandler; +import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.Closeable; import java.io.IOException; @@ -29,6 +34,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.dockerjava.api.DockerClientException; import com.github.dockerjava.api.async.ResultCallback; import com.github.dockerjava.api.model.Frame; import com.github.dockerjava.core.async.ResultCallbackTemplate; @@ -36,7 +42,7 @@ import com.github.dockerjava.netty.handler.HttpConnectionHijackHandler; import com.github.dockerjava.netty.handler.HttpRequestProvider; import com.github.dockerjava.netty.handler.HttpResponseHandler; -import com.github.dockerjava.netty.handler.HttpResponseStreamInboundHandler; +import com.github.dockerjava.netty.handler.HttpResponseStreamHandler; import com.github.dockerjava.netty.handler.JsonResponseCallbackHandler; public class InvocationBuilder { @@ -72,7 +78,11 @@ public InvocationBuilder(ChannelProvider channelProvider, String resource) { } public InvocationBuilder accept(MediaType mediaType) { - headers.put(HttpHeaderNames.ACCEPT.toString(), mediaType.getMediaType()); + return header(HttpHeaderNames.ACCEPT.toString(), mediaType.getMediaType()); + } + + public InvocationBuilder header(String name, String value) { + headers.put(name, value); return this; } @@ -88,7 +98,7 @@ public ResponseCallback delete() { channel.pipeline().addLast(responseHandler); - sendRequestAndHandleResponse(requestProvider, channel); + sendRequest(requestProvider, channel); return callback; } @@ -106,7 +116,7 @@ public void get(ResultCallback resultCallback) { channel.pipeline().addLast(responseHandler); channel.pipeline().addLast(streamHandler); - sendRequestAndHandleResponse(requestProvider, channel); + sendRequest(requestProvider, channel); } public ResponseCallback get(TypeReference typeReference) { @@ -135,15 +145,13 @@ public void get(TypeReference typeReference, ResultCallback resultCall channel.pipeline().addLast(new JsonObjectDecoder()); channel.pipeline().addLast(jsonResponseHandler); - sendRequestAndHandleResponse(requestProvider, channel); + sendRequest(requestProvider, channel); return; } private Channel getChannel() { - Channel channel = channelProvider.getChannel(); - - return channel; + return channelProvider.getChannel(); } private HttpRequestProvider httpDeleteRequestProvider() { @@ -179,17 +187,17 @@ public InputStream post(final Object entity) { Channel channel = getChannel(); - ResponseCallback callback = new ResponseCallback(); + ResponseCallback callback = new ResponseCallback(); HttpResponseHandler responseHandler = new HttpResponseHandler(requestProvider, callback); - HttpResponseStreamInboundHandler streamHandler = new HttpResponseStreamInboundHandler(); + HttpResponseStreamHandler streamHandler = new HttpResponseStreamHandler(callback); channel.pipeline().addLast(responseHandler); channel.pipeline().addLast(streamHandler); - sendRequestAndHandleResponse(requestProvider, channel); + sendRequest(requestProvider, channel); - return streamHandler.getInputStream(); + return callback.awaitResult(); } public void post(final Object entity, final InputStream stdin, ResultCallback resultCallback) { @@ -208,7 +216,7 @@ public void post(final Object entity, final InputStream stdin, ResultCallback void post(final Object entity, TypeReference typeReference, final channel.pipeline().addLast(new JsonObjectDecoder()); channel.pipeline().addLast(jsonResponseHandler); - sendRequestAndHandleResponse(requestProvider, channel); + sendRequest(requestProvider, channel); return; } @@ -311,11 +319,12 @@ private FullHttpRequest prepareGetRequest(String uri) { private HttpRequest preparePostRequest(String uri, Object entity) { - FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, uri); - - setDefaultHeaders(request); + HttpRequest request = null; if (entity != null) { + + FullHttpRequest fullRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, uri); + byte[] bytes; try { bytes = new ObjectMapper().writeValueAsBytes(entity); @@ -323,17 +332,22 @@ private HttpRequest preparePostRequest(String uri, Object entity) { throw new RuntimeException(e); } - request.headers().set(HttpHeaderNames.CONTENT_TYPE, "application/json"); - request.content().clear().writeBytes(Unpooled.copiedBuffer(bytes)); - request.headers().set(HttpHeaderNames.CONTENT_LENGTH, bytes.length); + fullRequest.headers().set(HttpHeaderNames.CONTENT_TYPE, "application/json"); + fullRequest.content().clear().writeBytes(Unpooled.copiedBuffer(bytes)); + fullRequest.headers().set(HttpHeaderNames.CONTENT_LENGTH, bytes.length); + + request = fullRequest; } else { + request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, uri); request.headers().set(HttpHeaderNames.CONTENT_LENGTH, 0); } + setDefaultHeaders(request); + return request; } - private void sendRequestAndHandleResponse(HttpRequestProvider requestProvider, Channel channel) { + private void sendRequest(HttpRequestProvider requestProvider, Channel channel) { ChannelFuture channelFuture = channel.writeAndFlush(requestProvider.getHttpRequest(resource)); @@ -356,5 +370,73 @@ private void setDefaultHeaders(HttpRequest request) { for (Map.Entry entry : headers.entrySet()) { request.headers().set((CharSequence) entry.getKey(), entry.getValue()); } + } + + public T post(TypeReference typeReference, InputStream body) { + + ResponseCallback callback = new ResponseCallback(); + + post(typeReference, callback, body); + + return callback.awaitResult(); + } + + + public void post(TypeReference typeReference, ResultCallback resultCallback, InputStream body) { + HttpRequestProvider requestProvider = httpPostRequestProvider(null); + + Channel channel = getChannel(); + + resultCallbackOnStart(channel, resultCallback); + + JsonResponseCallbackHandler jsonResponseHandler = new JsonResponseCallbackHandler(typeReference, + resultCallback); + + HttpResponseHandler responseHandler = new HttpResponseHandler(requestProvider, resultCallback); + + channel.pipeline().addLast(new ChunkedWriteHandler()); + channel.pipeline().addLast(responseHandler); + channel.pipeline().addLast(new JsonObjectDecoder()); + channel.pipeline().addLast(jsonResponseHandler); + + HttpRequest request = requestProvider.getHttpRequest(resource); + + // don't accept FullHttpRequest here + if (request instanceof FullHttpRequest) { + throw new DockerClientException("fatal: request is instance of FullHttpRequest"); + } + + request.headers().set(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED); + request.headers().remove(HttpHeaderNames.CONTENT_LENGTH); + + channel.write(request); + + channel.write(new ChunkedStream(new BufferedInputStream(body, 1024 * 1024), 1024 * 1024)); + channel.write(LastHttpContent.EMPTY_LAST_CONTENT); + channel.flush(); + + return; + + } + + public InputStream get() { + HttpRequestProvider requestProvider = httpGetRequestProvider(); + + Channel channel = getChannel(); + + ResponseCallback resultCallback = new ResponseCallback(); + + resultCallbackOnStart(channel, resultCallback); + + HttpResponseHandler responseHandler = new HttpResponseHandler(requestProvider, resultCallback); + + HttpResponseStreamHandler streamHandler = new HttpResponseStreamHandler(resultCallback); + + channel.pipeline().addLast(responseHandler); + channel.pipeline().addLast(streamHandler); + + sendRequest(requestProvider, channel); + + return resultCallback.awaitResult(); }; } diff --git a/src/main/java/com/github/dockerjava/netty/WebTarget.java b/src/main/java/com/github/dockerjava/netty/WebTarget.java index d4af90786..4362ccab9 100644 --- a/src/main/java/com/github/dockerjava/netty/WebTarget.java +++ b/src/main/java/com/github/dockerjava/netty/WebTarget.java @@ -8,8 +8,6 @@ import org.apache.commons.lang.StringUtils; -import io.netty.channel.Channel; - public class WebTarget { private ChannelProvider channelProvider; diff --git a/src/main/java/com/github/dockerjava/netty/exec/AbstrAsyncDockerCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/AbstrAsyncDockerCmdExec.java index cb2b523c4..3d1d4fac3 100644 --- a/src/main/java/com/github/dockerjava/netty/exec/AbstrAsyncDockerCmdExec.java +++ b/src/main/java/com/github/dockerjava/netty/exec/AbstrAsyncDockerCmdExec.java @@ -1,15 +1,14 @@ package com.github.dockerjava.netty.exec; +import java.io.Closeable; +import java.io.IOException; + import com.github.dockerjava.api.async.ResultCallback; import com.github.dockerjava.api.command.AsyncDockerCmd; import com.github.dockerjava.api.command.DockerCmdAsyncExec; import com.github.dockerjava.core.DockerClientConfig; -import com.github.dockerjava.jaxrs.async.AbstractCallbackNotifier; import com.github.dockerjava.netty.WebTarget; -import java.io.Closeable; -import java.io.IOException; - public abstract class AbstrAsyncDockerCmdExec, A_RES_T> extends AbstrDockerCmdExec implements DockerCmdAsyncExec { @@ -61,7 +60,4 @@ public void onComplete() { protected abstract Void execute0(final CMD_T command, final ResultCallback resultCallback); - - - } diff --git a/src/main/java/com/github/dockerjava/netty/exec/AttachContainerCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/AttachContainerCmdExec.java new file mode 100644 index 000000000..76cec4272 --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/exec/AttachContainerCmdExec.java @@ -0,0 +1,39 @@ +package com.github.dockerjava.netty.exec; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.async.ResultCallback; +import com.github.dockerjava.api.command.AttachContainerCmd; +import com.github.dockerjava.api.model.Frame; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.netty.WebTarget; + +public class AttachContainerCmdExec extends AbstrAsyncDockerCmdExec implements + AttachContainerCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(AttachContainerCmdExec.class); + + public AttachContainerCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + protected Void execute0(AttachContainerCmd command, ResultCallback resultCallback) { + + WebTarget webTarget = getBaseResource().path("/containers/{id}/attach").resolveTemplate("id", + command.getContainerId()); + + webTarget = booleanQueryParam(webTarget, "logs", command.hasLogsEnabled()); + webTarget = booleanQueryParam(webTarget, "stdout", command.hasStdoutEnabled()); + // webTarget = booleanQueryParam(webTarget, "stdin", command.hasStdinEnabled()); + webTarget = booleanQueryParam(webTarget, "stderr", command.hasStderrEnabled()); + webTarget = booleanQueryParam(webTarget, "stream", command.hasFollowStreamEnabled()); + + LOGGER.trace("POST: {}", webTarget); + + webTarget.request().post(null, System.in, resultCallback); + + return null; + } +} diff --git a/src/main/java/com/github/dockerjava/netty/exec/BuildImageCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/BuildImageCmdExec.java new file mode 100644 index 000000000..9c0e6c09f --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/exec/BuildImageCmdExec.java @@ -0,0 +1,93 @@ +package com.github.dockerjava.netty.exec; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.github.dockerjava.api.async.ResultCallback; +import com.github.dockerjava.api.command.BuildImageCmd; +import com.github.dockerjava.api.model.AuthConfigurations; +import com.github.dockerjava.api.model.BuildResponseItem; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.netty.InvocationBuilder; +import com.github.dockerjava.netty.MediaType; +import com.github.dockerjava.netty.WebTarget; + +public class BuildImageCmdExec extends AbstrAsyncDockerCmdExec implements + BuildImageCmd.Exec { + private static final Logger LOGGER = LoggerFactory.getLogger(BuildImageCmdExec.class); + + public BuildImageCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + private InvocationBuilder resourceWithOptionalAuthConfig(BuildImageCmd command, InvocationBuilder request) { + final AuthConfigurations authConfigs = firstNonNull(command.getBuildAuthConfigs(), getBuildAuthConfigs()); + if (authConfigs != null) { + request = request.header("X-Registry-Config", registryConfigs(authConfigs)); + } + return request; + } + + private static AuthConfigurations firstNonNull(final AuthConfigurations fromCommand, + final AuthConfigurations fromConfig) { + if (fromCommand != null) { + return fromCommand; + } + if (fromConfig != null) { + return fromConfig; + } + return null; + } + + @Override + protected Void execute0(BuildImageCmd command, ResultCallback resultCallback) { + + WebTarget webTarget = getBaseResource().path("/build"); + String dockerFilePath = command.getPathToDockerfile(); + + if (dockerFilePath != null && command.getRemote() == null && !"Dockerfile".equals(dockerFilePath)) { + webTarget = webTarget.queryParam("dockerfile", dockerFilePath); + } + if (command.getTag() != null) { + webTarget = webTarget.queryParam("t", command.getTag()); + } + if (command.getRemote() != null) { + webTarget = webTarget.queryParam("remote", command.getRemote().toString()); + } + + webTarget = booleanQueryParam(webTarget, "q", command.isQuiet()); + webTarget = booleanQueryParam(webTarget, "nocache", command.hasNoCacheEnabled()); + webTarget = booleanQueryParam(webTarget, "pull", command.hasPullEnabled()); + webTarget = booleanQueryParam(webTarget, "rm", command.hasRemoveEnabled()); + webTarget = booleanQueryParam(webTarget, "forcerm", command.isForcerm()); + + // this has to be handled differently as it should switch to 'false' + if (command.hasRemoveEnabled() == null || !command.hasRemoveEnabled()) { + webTarget = webTarget.queryParam("rm", "false"); + } + + if (command.getMemory() != null) { + webTarget = webTarget.queryParam("memory", command.getMemory()); + } + if (command.getMemswap() != null) { + webTarget = webTarget.queryParam("memswap", command.getMemswap()); + } + if (command.getCpushares() != null) { + webTarget = webTarget.queryParam("cpushares", command.getCpushares()); + } + if (command.getCpusetcpus() != null) { + webTarget = webTarget.queryParam("cpusetcpus", command.getCpusetcpus()); + } + + LOGGER.trace("POST: {}", webTarget); + + InvocationBuilder builder = resourceWithOptionalAuthConfig(command, webTarget.request()).accept( + MediaType.APPLICATION_JSON).header("Content-Type", "application/tar").header("encoding", "gzip"); + + builder.post(new TypeReference() { + }, resultCallback, command.getTarInputStream()); + + return null; + } +} diff --git a/src/main/java/com/github/dockerjava/netty/exec/CommitCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/CommitCmdExec.java new file mode 100644 index 000000000..98c2dbd97 --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/exec/CommitCmdExec.java @@ -0,0 +1,36 @@ +package com.github.dockerjava.netty.exec; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.github.dockerjava.api.command.CommitCmd; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.netty.MediaType; +import com.github.dockerjava.netty.WebTarget; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CommitCmdExec extends AbstrSyncDockerCmdExec implements CommitCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(CommitCmdExec.class); + + public CommitCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + protected String execute(CommitCmd command) { + WebTarget webTarget = getBaseResource().path("/commit").queryParam("container", command.getContainerId()) + .queryParam("repo", command.getRepository()).queryParam("tag", command.getTag()) + .queryParam("m", command.getMessage()).queryParam("author", command.getAuthor()); + + webTarget = booleanQueryParam(webTarget, "pause", command.hasPauseEnabled()); + + LOGGER.trace("POST: {}", webTarget); + ObjectNode objectNode = webTarget.request().accept(MediaType.APPLICATION_JSON). + post(command, new TypeReference() {}); + + return objectNode.get("Id").asText(); + } + +} diff --git a/src/main/java/com/github/dockerjava/netty/exec/ContainerDiffCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/ContainerDiffCmdExec.java new file mode 100644 index 000000000..a1d2d9601 --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/exec/ContainerDiffCmdExec.java @@ -0,0 +1,34 @@ +package com.github.dockerjava.netty.exec; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.github.dockerjava.api.command.ContainerDiffCmd; +import com.github.dockerjava.api.model.ChangeLog; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.netty.MediaType; +import com.github.dockerjava.netty.WebTarget; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +public class ContainerDiffCmdExec extends AbstrSyncDockerCmdExec> implements + ContainerDiffCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(ContainerDiffCmdExec.class); + + public ContainerDiffCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + protected List execute(ContainerDiffCmd command) { + WebTarget webResource = getBaseResource().path("/containers/{id}/changes").resolveTemplate("id", + command.getContainerId()); + + LOGGER.trace("GET: {}", webResource); + return webResource.request().accept(MediaType.APPLICATION_JSON).get(new TypeReference>() { + }).awaitResult(); + } + +} diff --git a/src/main/java/com/github/dockerjava/netty/exec/CreateContainerCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/CreateContainerCmdExec.java index 3a5e75d60..f7e4e39d9 100644 --- a/src/main/java/com/github/dockerjava/netty/exec/CreateContainerCmdExec.java +++ b/src/main/java/com/github/dockerjava/netty/exec/CreateContainerCmdExec.java @@ -1,16 +1,15 @@ package com.github.dockerjava.netty.exec; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.fasterxml.jackson.core.type.TypeReference; import com.github.dockerjava.api.command.CreateContainerCmd; import com.github.dockerjava.api.command.CreateContainerResponse; -import com.github.dockerjava.api.command.CreateContainerCmd.Exec; import com.github.dockerjava.core.DockerClientConfig; import com.github.dockerjava.netty.MediaType; import com.github.dockerjava.netty.WebTarget; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - public class CreateContainerCmdExec extends AbstrSyncDockerCmdExec implements CreateContainerCmd.Exec { @@ -33,5 +32,4 @@ protected CreateContainerResponse execute(CreateContainerCmd command) { .post(command, new TypeReference() { }); } - } diff --git a/src/main/java/com/github/dockerjava/netty/exec/CreateImageCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/CreateImageCmdExec.java new file mode 100644 index 000000000..2526bc2e1 --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/exec/CreateImageCmdExec.java @@ -0,0 +1,31 @@ +package com.github.dockerjava.netty.exec; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.github.dockerjava.api.command.CreateImageCmd; +import com.github.dockerjava.api.command.CreateImageResponse; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.netty.MediaType; +import com.github.dockerjava.netty.WebTarget; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CreateImageCmdExec extends AbstrSyncDockerCmdExec implements + CreateImageCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(CreateImageCmdExec.class); + + public CreateImageCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + protected CreateImageResponse execute(CreateImageCmd command) { + WebTarget webResource = getBaseResource().path("/images/create").queryParam("repo", command.getRepository()) + .queryParam("tag", command.getTag()).queryParam("fromSrc", "-"); + + LOGGER.trace("POST: {}", webResource); + return webResource.request().accept(MediaType.APPLICATION_OCTET_STREAM) + .post(new TypeReference() {}, command.getImageStream()); + } +} diff --git a/src/main/java/com/github/dockerjava/netty/exec/EventsCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/EventsCmdExec.java new file mode 100644 index 000000000..ef95d2e0d --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/exec/EventsCmdExec.java @@ -0,0 +1,41 @@ +package com.github.dockerjava.netty.exec; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.github.dockerjava.api.async.ResultCallback; +import com.github.dockerjava.api.command.EventsCmd; +import com.github.dockerjava.api.model.Event; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.netty.WebTarget; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static com.google.common.net.UrlEscapers.urlPathSegmentEscaper; + +public class EventsCmdExec extends AbstrAsyncDockerCmdExec implements EventsCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(EventsCmdExec.class); + + public EventsCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + protected Void execute0(EventsCmd command, ResultCallback resultCallback) { + + WebTarget webTarget = getBaseResource().path("/events").queryParam("since", command.getSince()) + .queryParam("until", command.getUntil()); + + if (command.getFilters() != null) { + webTarget = webTarget + .queryParam("filters", urlPathSegmentEscaper().escape(command.getFilters().toString())); + } + + LOGGER.trace("GET: {}", webTarget); + + webTarget.request().get(new TypeReference() { + }, resultCallback); + + return null; + } +} diff --git a/src/main/java/com/github/dockerjava/netty/exec/ExecStartCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/ExecStartCmdExec.java index 9d684ca7b..374b2062d 100644 --- a/src/main/java/com/github/dockerjava/netty/exec/ExecStartCmdExec.java +++ b/src/main/java/com/github/dockerjava/netty/exec/ExecStartCmdExec.java @@ -12,17 +12,17 @@ public class ExecStartCmdExec extends AbstrAsyncDockerCmdExec implements ExecStartCmd.Exec { - private static final Logger LOGGER = LoggerFactory.getLogger(ExecStartCmdExec.class); + private static final Logger LOGGER = LoggerFactory.getLogger(ExecStartCmdExec.class); - public ExecStartCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { - super(baseResource, dockerClientConfig); - } + public ExecStartCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } @Override protected Void execute0(ExecStartCmd command, ResultCallback resultCallback) { WebTarget webTarget = getBaseResource().path("/exec/{id}/start").resolveTemplate("id", command.getExecId()); - webTarget.request().accept(MediaType.APPLICATION_JSON).post(command, System.in, resultCallback); + webTarget.request().accept(MediaType.APPLICATION_JSON).post(command, command.getStdin(), resultCallback); return null; } diff --git a/src/main/java/com/github/dockerjava/netty/exec/ListImagesCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/ListImagesCmdExec.java new file mode 100644 index 000000000..1521f3e5c --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/exec/ListImagesCmdExec.java @@ -0,0 +1,45 @@ +package com.github.dockerjava.netty.exec; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.github.dockerjava.api.command.ListImagesCmd; +import com.github.dockerjava.api.model.Image; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.netty.MediaType; +import com.github.dockerjava.netty.WebTarget; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +import static com.google.common.net.UrlEscapers.urlPathSegmentEscaper; + +public class ListImagesCmdExec extends AbstrSyncDockerCmdExec> implements ListImagesCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(ListImagesCmdExec.class); + + public ListImagesCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + protected List execute(ListImagesCmd command) { + WebTarget webTarget = getBaseResource().path("/images/json"); + + webTarget = booleanQueryParam(webTarget, "all", command.hasShowAllEnabled()); + + if (command.getFilters() != null) + webTarget = webTarget.queryParam("filters", urlPathSegmentEscaper().escape(command.getFilters())); + + LOGGER.trace("GET: {}", webTarget); + + List images = webTarget.request().accept(MediaType.APPLICATION_JSON) + .get(new TypeReference>() { + }).awaitResult(); + + LOGGER.trace("Response: {}", images); + + return images; + } + +} diff --git a/src/main/java/com/github/dockerjava/netty/exec/PauseContainerCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/PauseContainerCmdExec.java new file mode 100644 index 000000000..178eee99c --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/exec/PauseContainerCmdExec.java @@ -0,0 +1,31 @@ +package com.github.dockerjava.netty.exec; + +import com.github.dockerjava.api.command.PauseContainerCmd; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.netty.MediaType; +import com.github.dockerjava.netty.WebTarget; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class PauseContainerCmdExec extends AbstrSyncDockerCmdExec implements + PauseContainerCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(PauseContainerCmdExec.class); + + public PauseContainerCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + protected Void execute(PauseContainerCmd command) { + WebTarget webResource = getBaseResource().path("/containers/{id}/pause").resolveTemplate("id", + command.getContainerId()); + + LOGGER.trace("POST: {}", webResource); + webResource.request().accept(MediaType.APPLICATION_JSON).post(null); + + return null; + } + +} diff --git a/src/main/java/com/github/dockerjava/netty/exec/PingCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/PingCmdExec.java new file mode 100644 index 000000000..6e305caed --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/exec/PingCmdExec.java @@ -0,0 +1,28 @@ +package com.github.dockerjava.netty.exec; + +import com.github.dockerjava.api.command.PingCmd; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.netty.WebTarget; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class PingCmdExec extends AbstrSyncDockerCmdExec implements PingCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(PingCmdExec.class); + + public PingCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + protected Void execute(PingCmd command) { + WebTarget webResource = getBaseResource().path("/_ping"); + + LOGGER.trace("GET: {}", webResource); + webResource.request().get(); + + return null; + } + +} diff --git a/src/main/java/com/github/dockerjava/netty/exec/PullImageCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/PullImageCmdExec.java new file mode 100644 index 000000000..7f15b4e62 --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/exec/PullImageCmdExec.java @@ -0,0 +1,46 @@ +package com.github.dockerjava.netty.exec; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.github.dockerjava.api.async.ResultCallback; +import com.github.dockerjava.api.command.PullImageCmd; +import com.github.dockerjava.api.model.AuthConfig; +import com.github.dockerjava.api.model.PullResponseItem; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.netty.InvocationBuilder; +import com.github.dockerjava.netty.MediaType; +import com.github.dockerjava.netty.WebTarget; + +public class PullImageCmdExec extends AbstrAsyncDockerCmdExec implements + PullImageCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(PullImageCmdExec.class); + + public PullImageCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + private InvocationBuilder resourceWithOptionalAuthConfig(PullImageCmd command, InvocationBuilder request) { + AuthConfig authConfig = command.getAuthConfig(); + if (authConfig != null) { + request = request.header("X-Registry-Auth", registryAuth(authConfig)); + } + return request; + } + + @Override + protected Void execute0(PullImageCmd command, ResultCallback resultCallback) { + + WebTarget webResource = getBaseResource().path("/images/create").queryParam("tag", command.getTag()) + .queryParam("fromImage", command.getRepository()).queryParam("registry", command.getRegistry()); + + LOGGER.trace("POST: {}", webResource); + resourceWithOptionalAuthConfig(command, webResource.request()).accept(MediaType.APPLICATION_OCTET_STREAM).post( + null, new TypeReference() { + }, resultCallback); + + return null; + } +} diff --git a/src/main/java/com/github/dockerjava/netty/exec/PushImageCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/PushImageCmdExec.java new file mode 100644 index 000000000..d6dcdbd00 --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/exec/PushImageCmdExec.java @@ -0,0 +1,48 @@ +package com.github.dockerjava.netty.exec; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.github.dockerjava.api.async.ResultCallback; +import com.github.dockerjava.api.command.PushImageCmd; +import com.github.dockerjava.api.model.AuthConfig; +import com.github.dockerjava.api.model.PushResponseItem; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.netty.InvocationBuilder; +import com.github.dockerjava.netty.MediaType; +import com.github.dockerjava.netty.WebTarget; + +public class PushImageCmdExec extends AbstrAsyncDockerCmdExec implements + PushImageCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(PushImageCmdExec.class); + + public PushImageCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + private String name(PushImageCmd command) { + String name = command.getName(); + AuthConfig authConfig = command.getAuthConfig(); + return (name.contains("/") || authConfig == null) ? name : authConfig.getUsername(); + } + + @Override + protected Void execute0(PushImageCmd command, ResultCallback resultCallback) { + + WebTarget webResource = getBaseResource().path("/images/" + name(command) + "/push").queryParam("tag", + command.getTag()); + + final String registryAuth = registryAuth(command.getAuthConfig()); + LOGGER.trace("POST: {}", webResource); + + InvocationBuilder builder = webResource.request().header("X-Registry-Auth", registryAuth) + .accept(MediaType.APPLICATION_JSON); + + builder.post(null, new TypeReference() { + }, resultCallback); + + return null; + } +} diff --git a/src/main/java/com/github/dockerjava/netty/exec/RemoveImageCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/RemoveImageCmdExec.java new file mode 100644 index 000000000..fa3a7ed57 --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/exec/RemoveImageCmdExec.java @@ -0,0 +1,31 @@ +package com.github.dockerjava.netty.exec; + +import com.github.dockerjava.api.command.RemoveImageCmd; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.netty.WebTarget; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RemoveImageCmdExec extends AbstrSyncDockerCmdExec implements RemoveImageCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(RemoveImageCmdExec.class); + + public RemoveImageCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + protected Void execute(RemoveImageCmd command) { + WebTarget webTarget = getBaseResource().path("/images/" + command.getImageId()); + + webTarget = booleanQueryParam(webTarget, "force", command.hasForceEnabled()); + webTarget = booleanQueryParam(webTarget, "noprune", command.hasNoPruneEnabled()); + + LOGGER.trace("DELETE: {}", webTarget); + webTarget.request().delete().awaitResult(); + + return null; + } + +} diff --git a/src/main/java/com/github/dockerjava/netty/exec/RestartContainerCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/RestartContainerCmdExec.java new file mode 100644 index 000000000..dc52f0eef --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/exec/RestartContainerCmdExec.java @@ -0,0 +1,36 @@ +package com.github.dockerjava.netty.exec; + +import com.github.dockerjava.api.command.RestartContainerCmd; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.netty.MediaType; +import com.github.dockerjava.netty.WebTarget; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +public class RestartContainerCmdExec extends AbstrSyncDockerCmdExec implements + RestartContainerCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(RestartContainerCmdExec.class); + + public RestartContainerCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + protected Void execute(RestartContainerCmd command) { + WebTarget webResource = getBaseResource().path("/containers/{id}/restart").resolveTemplate("id", + command.getContainerId()); + + if (command.getTimeout() != null) { + webResource = webResource.queryParam("t", String.valueOf(command.getTimeout())); + } + + LOGGER.trace("POST: {}", webResource); + webResource.request().accept(MediaType.APPLICATION_JSON).post(null); + + return null; + } + +} diff --git a/src/main/java/com/github/dockerjava/netty/exec/SaveImageCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/SaveImageCmdExec.java new file mode 100644 index 000000000..629f49871 --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/exec/SaveImageCmdExec.java @@ -0,0 +1,28 @@ +package com.github.dockerjava.netty.exec; + +import java.io.InputStream; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.command.SaveImageCmd; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.netty.MediaType; +import com.github.dockerjava.netty.WebTarget; + +public class SaveImageCmdExec extends AbstrSyncDockerCmdExec implements SaveImageCmd.Exec { + private static final Logger LOGGER = LoggerFactory.getLogger(SaveImageCmdExec.class); + + public SaveImageCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + protected InputStream execute(SaveImageCmd command) { + WebTarget webResource = getBaseResource().path("/images/" + command.getName() + "/get").queryParam("tag", + command.getTag()); + + LOGGER.trace("GET: {}", webResource); + return webResource.request().accept(MediaType.APPLICATION_JSON).get(); + } +} diff --git a/src/main/java/com/github/dockerjava/netty/exec/SearchImagesCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/SearchImagesCmdExec.java new file mode 100644 index 000000000..31db18fc3 --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/exec/SearchImagesCmdExec.java @@ -0,0 +1,33 @@ +package com.github.dockerjava.netty.exec; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.github.dockerjava.api.command.SearchImagesCmd; +import com.github.dockerjava.api.model.SearchItem; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.netty.MediaType; +import com.github.dockerjava.netty.WebTarget; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +public class SearchImagesCmdExec extends AbstrSyncDockerCmdExec> implements + SearchImagesCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(SearchImagesCmdExec.class); + + public SearchImagesCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + protected List execute(SearchImagesCmd command) { + WebTarget webResource = getBaseResource().path("/images/search").queryParam("term", command.getTerm()); + + LOGGER.trace("GET: {}", webResource); + return webResource.request().accept(MediaType.APPLICATION_JSON).get(new TypeReference>() { + }).awaitResult(); + } + +} diff --git a/src/main/java/com/github/dockerjava/netty/exec/StartContainerCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/StartContainerCmdExec.java index cfa7d28ce..12f2d8cbc 100644 --- a/src/main/java/com/github/dockerjava/netty/exec/StartContainerCmdExec.java +++ b/src/main/java/com/github/dockerjava/netty/exec/StartContainerCmdExec.java @@ -1,15 +1,13 @@ package com.github.dockerjava.netty.exec; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.github.dockerjava.api.command.StartContainerCmd; -import com.github.dockerjava.api.command.StartContainerCmd.Exec; import com.github.dockerjava.core.DockerClientConfig; import com.github.dockerjava.netty.MediaType; import com.github.dockerjava.netty.WebTarget; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - - public class StartContainerCmdExec extends AbstrSyncDockerCmdExec implements StartContainerCmd.Exec { diff --git a/src/main/java/com/github/dockerjava/netty/exec/StatsCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/StatsCmdExec.java new file mode 100644 index 000000000..d167d5686 --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/exec/StatsCmdExec.java @@ -0,0 +1,33 @@ +package com.github.dockerjava.netty.exec; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.github.dockerjava.api.async.ResultCallback; +import com.github.dockerjava.api.command.StatsCmd; +import com.github.dockerjava.api.model.Statistics; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.netty.WebTarget; + +public class StatsCmdExec extends AbstrAsyncDockerCmdExec implements StatsCmd.Exec { + private static final Logger LOGGER = LoggerFactory.getLogger(StatsCmdExec.class); + + public StatsCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + protected Void execute0(StatsCmd command, ResultCallback resultCallback) { + + WebTarget webTarget = getBaseResource().path("/containers/{id}/stats").resolveTemplate("id", + command.getContainerId()); + + LOGGER.trace("GET: {}", webTarget); + + webTarget.request().get(new TypeReference() { + }, resultCallback); + + return null; + } +} diff --git a/src/main/java/com/github/dockerjava/netty/exec/TagImageCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/TagImageCmdExec.java new file mode 100644 index 000000000..ad7ed264e --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/exec/TagImageCmdExec.java @@ -0,0 +1,30 @@ +package com.github.dockerjava.netty.exec; + +import com.github.dockerjava.api.command.TagImageCmd; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.netty.WebTarget; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TagImageCmdExec extends AbstrSyncDockerCmdExec implements TagImageCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(TagImageCmdExec.class); + + public TagImageCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + protected Void execute(TagImageCmd command) { + WebTarget webTarget = getBaseResource().path("/images/" + command.getImageId() + "/tag") + .queryParam("repo", command.getRepository()).queryParam("tag", command.getTag()); + + webTarget = booleanQueryParam(webTarget, "force", command.hasForceEnabled()); + + LOGGER.trace("POST: {}", webTarget); + webTarget.request().post(null); + return null; + } + +} diff --git a/src/main/java/com/github/dockerjava/netty/exec/TopContainerCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/TopContainerCmdExec.java new file mode 100644 index 000000000..630179b48 --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/exec/TopContainerCmdExec.java @@ -0,0 +1,36 @@ +package com.github.dockerjava.netty.exec; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.github.dockerjava.api.command.TopContainerCmd; +import com.github.dockerjava.api.command.TopContainerResponse; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.netty.MediaType; +import com.github.dockerjava.netty.WebTarget; + +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TopContainerCmdExec extends AbstrSyncDockerCmdExec implements + TopContainerCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(TopContainerCmdExec.class); + + public TopContainerCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + protected TopContainerResponse execute(TopContainerCmd command) { + WebTarget webResource = getBaseResource().path("/containers/{id}/top").resolveTemplate("id", + command.getContainerId()); + + if (!StringUtils.isEmpty(command.getPsArgs())) + webResource = webResource.queryParam("ps_args", command.getPsArgs()); + + LOGGER.trace("GET: {}", webResource); + return webResource.request().accept(MediaType.APPLICATION_JSON).get(new TypeReference() { + }).awaitResult(); + } + +} diff --git a/src/main/java/com/github/dockerjava/netty/exec/UnpauseContainerCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/UnpauseContainerCmdExec.java new file mode 100644 index 000000000..61fb9a880 --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/exec/UnpauseContainerCmdExec.java @@ -0,0 +1,31 @@ +package com.github.dockerjava.netty.exec; + +import com.github.dockerjava.api.command.UnpauseContainerCmd; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.netty.MediaType; +import com.github.dockerjava.netty.WebTarget; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class UnpauseContainerCmdExec extends AbstrSyncDockerCmdExec implements + UnpauseContainerCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(UnpauseContainerCmdExec.class); + + public UnpauseContainerCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + protected Void execute(UnpauseContainerCmd command) { + WebTarget webResource = getBaseResource().path("/containers/{id}/unpause").resolveTemplate("id", + command.getContainerId()); + + LOGGER.trace("POST: {}", webResource); + webResource.request().accept(MediaType.APPLICATION_JSON).post(null); + + return null; + } + +} diff --git a/src/main/java/com/github/dockerjava/netty/exec/VersionCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/VersionCmdExec.java new file mode 100644 index 000000000..9f62ea921 --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/exec/VersionCmdExec.java @@ -0,0 +1,30 @@ +package com.github.dockerjava.netty.exec; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.github.dockerjava.api.command.VersionCmd; +import com.github.dockerjava.api.model.Version; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.netty.MediaType; +import com.github.dockerjava.netty.WebTarget; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class VersionCmdExec extends AbstrSyncDockerCmdExec implements VersionCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(VersionCmdExec.class); + + public VersionCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + protected Version execute(VersionCmd command) { + WebTarget webResource = getBaseResource().path("/version"); + + LOGGER.trace("GET: {}", webResource); + return webResource.request().accept(MediaType.APPLICATION_JSON).get(new TypeReference() { + }).awaitResult(); + } + +} diff --git a/src/main/java/com/github/dockerjava/netty/exec/WaitContainerCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/WaitContainerCmdExec.java index d6e5eb0f6..5883b7188 100644 --- a/src/main/java/com/github/dockerjava/netty/exec/WaitContainerCmdExec.java +++ b/src/main/java/com/github/dockerjava/netty/exec/WaitContainerCmdExec.java @@ -27,7 +27,7 @@ protected Void execute0(WaitContainerCmd command, ResultCallback r LOGGER.trace("POST: {}", webTarget); - webTarget.request().accept(MediaType.APPLICATION_JSON).post(null, new TypeReference() { + webTarget.request().accept(MediaType.APPLICATION_JSON).post((Object) null, new TypeReference() { }, resultCallback); return null; diff --git a/src/main/java/com/github/dockerjava/netty/handler/FramedResponseStreamHandler.java b/src/main/java/com/github/dockerjava/netty/handler/FramedResponseStreamHandler.java index 8a3a7f1d3..5974f407f 100644 --- a/src/main/java/com/github/dockerjava/netty/handler/FramedResponseStreamHandler.java +++ b/src/main/java/com/github/dockerjava/netty/handler/FramedResponseStreamHandler.java @@ -2,10 +2,8 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.util.concurrent.GenericFutureListener; import com.github.dockerjava.api.async.ResultCallback; import com.github.dockerjava.api.model.Frame; @@ -13,115 +11,124 @@ public class FramedResponseStreamHandler extends SimpleChannelInboundHandler { - private static final int HEADER_SIZE = 8; + private static final int HEADER_SIZE = 8; - private final ByteBuf rawBuffer = Unpooled.buffer(1000); + private final ByteBuf rawBuffer = Unpooled.buffer(1000); - private byte[] header = new byte[HEADER_SIZE]; + private byte[] header = new byte[HEADER_SIZE]; - private int headerCnt = 0; + private int headerCnt = 0; - private byte[] payload = new byte[0]; + private byte[] payload = new byte[0]; - private int payloadCnt = 0; + private int payloadCnt = 0; - private ResultCallback resultCallback; + private ResultCallback resultCallback; - public FramedResponseStreamHandler(ResultCallback resultCallback) { - this.resultCallback = resultCallback; - } + private StreamType streamType = null; - @Override - protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { + public FramedResponseStreamHandler(ResultCallback resultCallback) { + this.resultCallback = resultCallback; + } - rawBuffer.writeBytes(msg.copy(), 0, msg.readableBytes()); + @Override + protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { - Frame frame = null; + rawBuffer.writeBytes(msg.copy(), 0, msg.readableBytes()); - do { - frame = decode(); + Frame frame = null; - if (frame != null) { - resultCallback.onNext(frame); - headerCnt = 0; - payloadCnt = 0; - } + do { + frame = decode(); - } while (frame != null); + if (frame != null) { + resultCallback.onNext(frame); + } + } while (frame != null); + } -// ctx.channel().closeFuture().addListener(new GenericFutureListener() { -// -// @Override -// public void operationComplete(ChannelFuture future) throws Exception { -// System.err.println("channel closed"); -// resultCallback.onComplete(); -// } -// }); + private int read(byte[] buf, int offset, int length) { + length = Math.min(rawBuffer.readableBytes(), length); + rawBuffer.readBytes(buf, offset, length); + return length; + } - } + private Frame decode() { + if (headerCnt < HEADER_SIZE) { + int headerCount = read(header, headerCnt, HEADER_SIZE - headerCnt); + if (headerCount == 0) { + return null; + } + headerCnt += headerCount; - private int read(byte[] buf, int offset, int length) { - length = Math.min(rawBuffer.readableBytes(), length); - rawBuffer.readBytes(buf, offset, length); - return length; - } + if (headerCnt < HEADER_SIZE) + return null; - private Frame decode() { - if (headerCnt < HEADER_SIZE) { + streamType = streamType(header[0]); - int headerCount = read(header, headerCnt, HEADER_SIZE - headerCnt); + if (streamType.equals(StreamType.RAW)) { + return new Frame(streamType, header); + } + } - if (headerCount == 0) { - return null; - } - headerCnt += headerCount; + if (streamType.equals(StreamType.RAW)) { - if (headerCnt < HEADER_SIZE) - return null; - } + if (payloadCnt == 0) + payload = new byte[rawBuffer.readableBytes()]; - StreamType streamType = streamType(header[0]); + int count = read(payload, payloadCnt, rawBuffer.readableBytes()); - int payloadSize = ((header[4] & 0xff) << 24) + ((header[5] & 0xff) << 16) + ((header[6] & 0xff) << 8) - + (header[7] & 0xff); + if (count == 0) + return null; - if(payloadCnt == 0) - payload = new byte[payloadSize]; + payloadCnt = 0; - int count = read(payload, payloadCnt, payloadSize - payloadCnt); + return new Frame(StreamType.RAW, payload); + } else { - if (count == 0) - return null; + int payloadSize = ((header[4] & 0xff) << 24) + ((header[5] & 0xff) << 16) + ((header[6] & 0xff) << 8) + + (header[7] & 0xff); - payloadCnt += count; + if (payloadCnt == 0) + payload = new byte[payloadSize]; - if (payloadCnt < payloadSize) - return null; + int count = read(payload, payloadCnt, payloadSize - payloadCnt); - return new Frame(streamType, payload); + if (count == 0) + return null; - } + payloadCnt += count; - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - resultCallback.onError(cause); - ctx.close(); - } + if (payloadCnt < payloadSize) + return null; - private static StreamType streamType(byte streamType) { - switch (streamType) { - case 0: - return StreamType.STDIN; - case 1: - return StreamType.STDOUT; - case 2: - return StreamType.STDERR; - default: - return StreamType.RAW; - } - } + headerCnt = 0; + payloadCnt = 0; + + return new Frame(streamType, payload); + } + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + resultCallback.onError(cause); + ctx.close(); + } + + private static StreamType streamType(byte streamType) { + switch (streamType) { + case 0: + return StreamType.STDIN; + case 1: + return StreamType.STDOUT; + case 2: + return StreamType.STDERR; + default: + return StreamType.RAW; + } + } } diff --git a/src/main/java/com/github/dockerjava/netty/handler/HttpConnectionHijackHandler.java b/src/main/java/com/github/dockerjava/netty/handler/HttpConnectionHijackHandler.java index 1a59118fb..0b0135d7e 100644 --- a/src/main/java/com/github/dockerjava/netty/handler/HttpConnectionHijackHandler.java +++ b/src/main/java/com/github/dockerjava/netty/handler/HttpConnectionHijackHandler.java @@ -1,57 +1,46 @@ package com.github.dockerjava.netty.handler; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.Map.Entry; -import java.util.concurrent.CountDownLatch; - -import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpClientCodec; import io.netty.handler.codec.http.HttpClientUpgradeHandler; import io.netty.handler.codec.http.HttpRequest; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CountDownLatch; + public class HttpConnectionHijackHandler implements HttpClientUpgradeHandler.UpgradeCodec { - private CountDownLatch latch = new CountDownLatch(1); + private CountDownLatch latch = new CountDownLatch(1); - private HttpResponseHandler httpResponseHandler; + private HttpResponseHandler httpResponseHandler; - public HttpConnectionHijackHandler(HttpResponseHandler httpResponseHandler) { + public HttpConnectionHijackHandler(HttpResponseHandler httpResponseHandler) { this.httpResponseHandler = httpResponseHandler; } - @Override - public void upgradeTo(ChannelHandlerContext ctx, FullHttpResponse upgradeResponse) - throws Exception { - System.out.println("UPGRADED"); - httpResponseHandler.channelRead(ctx, upgradeResponse); - ctx.pipeline().addLast(httpResponseHandler); - latch.countDown(); - } - - @Override - public Collection setUpgradeHeaders(ChannelHandlerContext ctx, - HttpRequest upgradeRequest) { - return Collections.emptyList(); - } - - @Override - public CharSequence protocol() { - return "tcp"; - } - - public void await() { - try { - latch.await(); - System.out.println("upgrade awaited"); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } + @Override + public void upgradeTo(ChannelHandlerContext ctx, FullHttpResponse upgradeResponse) throws Exception { + httpResponseHandler.channelRead(ctx, upgradeResponse); + ctx.pipeline().addLast(httpResponseHandler); + latch.countDown(); + } + @Override + public Collection setUpgradeHeaders(ChannelHandlerContext ctx, HttpRequest upgradeRequest) { + return Collections.emptyList(); + } + @Override + public CharSequence protocol() { + return "tcp"; + } + public void await() { + try { + latch.await(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } } diff --git a/src/main/java/com/github/dockerjava/netty/handler/HttpResponseHandler.java b/src/main/java/com/github/dockerjava/netty/handler/HttpResponseHandler.java index 548c13697..851295269 100644 --- a/src/main/java/com/github/dockerjava/netty/handler/HttpResponseHandler.java +++ b/src/main/java/com/github/dockerjava/netty/handler/HttpResponseHandler.java @@ -12,7 +12,6 @@ import io.netty.handler.codec.http.LastHttpContent; import java.nio.charset.Charset; -import java.util.concurrent.CountDownLatch; import com.github.dockerjava.api.BadRequestException; import com.github.dockerjava.api.ConflictException; @@ -74,7 +73,6 @@ protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Ex case 302: if (response.headers().contains(HttpHeaderNames.LOCATION)) { String location = response.headers().get(HttpHeaderNames.LOCATION); - System.out.println("redirected to :" + location); HttpRequest redirected = requestProvider.getHttpRequest(location); ctx.channel().writeAndFlush(redirected); @@ -100,18 +98,13 @@ protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Ex } catch (Throwable e) { resultCallback.onError(e); } finally { - System.err.println("LastHttpContent"); resultCallback.onComplete(); } } - } else { - System.err.println("UNKNOWN"); } - } private String getBodyAsMessage(ByteBuf body) { return body.readBytes(body.readableBytes()).toString(Charset.forName("UTF-8")); } - } diff --git a/src/main/java/com/github/dockerjava/netty/handler/HttpResponseStreamInboundHandler.java b/src/main/java/com/github/dockerjava/netty/handler/HttpResponseStreamHandler.java similarity index 87% rename from src/main/java/com/github/dockerjava/netty/handler/HttpResponseStreamInboundHandler.java rename to src/main/java/com/github/dockerjava/netty/handler/HttpResponseStreamHandler.java index f2a66c202..0cda2de81 100644 --- a/src/main/java/com/github/dockerjava/netty/handler/HttpResponseStreamInboundHandler.java +++ b/src/main/java/com/github/dockerjava/netty/handler/HttpResponseStreamHandler.java @@ -10,10 +10,16 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -public class HttpResponseStreamInboundHandler extends SimpleChannelInboundHandler { +import com.github.dockerjava.api.async.ResultCallback; + +public class HttpResponseStreamHandler extends SimpleChannelInboundHandler { private HttpResponseInputStream stream = new HttpResponseInputStream(); + public HttpResponseStreamHandler(ResultCallback resultCallback) { + resultCallback.onNext(stream); + } + @Override protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { stream.write(msg.copy()); @@ -25,16 +31,6 @@ public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { super.channelReadComplete(ctx); } - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - cause.printStackTrace(); - ctx.close(); - } - - public InputStream getInputStream() { - return stream; - } - public static class HttpResponseInputStream extends InputStream { private AtomicBoolean closed = new AtomicBoolean(false); diff --git a/src/main/java/com/github/dockerjava/netty/handler/JsonRequestHandler.java b/src/main/java/com/github/dockerjava/netty/handler/JsonRequestHandler.java index d797ba188..4340e1a38 100644 --- a/src/main/java/com/github/dockerjava/netty/handler/JsonRequestHandler.java +++ b/src/main/java/com/github/dockerjava/netty/handler/JsonRequestHandler.java @@ -1,19 +1,18 @@ package com.github.dockerjava.netty.handler; -import com.fasterxml.jackson.databind.ObjectMapper; - import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; +import com.fasterxml.jackson.databind.ObjectMapper; + public class JsonRequestHandler extends MessageToByteEncoder{ - + private ObjectMapper mapper = new ObjectMapper(); @Override protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception { byte[] serialized = mapper.writeValueAsBytes(msg); - System.out.println("serialized: " + new String(serialized)); out.writeBytes(serialized); } } diff --git a/src/main/java/com/github/dockerjava/netty/handler/JsonResponseCallbackHandler.java b/src/main/java/com/github/dockerjava/netty/handler/JsonResponseCallbackHandler.java index e6b1ef3a8..614508a81 100644 --- a/src/main/java/com/github/dockerjava/netty/handler/JsonResponseCallbackHandler.java +++ b/src/main/java/com/github/dockerjava/netty/handler/JsonResponseCallbackHandler.java @@ -36,7 +36,6 @@ protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Excep } callback.onNext(object); - callback.close(); } @Override diff --git a/src/test/java/com/github/dockerjava/netty/exec/AttachContainerCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/AttachContainerCmdExecTest.java new file mode 100644 index 000000000..2dedf0255 --- /dev/null +++ b/src/test/java/com/github/dockerjava/netty/exec/AttachContainerCmdExecTest.java @@ -0,0 +1,122 @@ +package com.github.dockerjava.netty.exec; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.Matchers.not; + +import java.io.File; +import java.lang.reflect.Method; +import java.util.concurrent.TimeUnit; + +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.model.Frame; +import com.github.dockerjava.api.model.StreamType; +import com.github.dockerjava.core.command.AttachContainerResultCallback; +import com.github.dockerjava.netty.AbstractDockerClientTest; + +@Test(groups = "integration") +public class AttachContainerCmdExecTest extends AbstractDockerClientTest { + + @BeforeTest + public void beforeTest() throws Exception { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test + public void attachContainerWithoutTTY() throws Exception { + + String snippet = "hello world"; + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("echo", snippet) + .withTty(false).exec(); + + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container.getId()).exec(); + + AttachContainerTestCallback callback = new AttachContainerTestCallback() { + @Override + public void onNext(Frame frame) { + assertEquals(frame.getStreamType(), StreamType.STDOUT); + super.onNext(frame); + }; + }; + + dockerClient.attachContainerCmd(container.getId()).withStdErr(true).withStdOut(true).withFollowStream(true) + .withLogs(true).exec(callback).awaitCompletion(10, TimeUnit.SECONDS).close(); + + assertThat(callback.toString(), containsString(snippet)); + } + + @Test + public void attachContainerWithTTY() throws Exception { + + File baseDir = new File(Thread.currentThread().getContextClassLoader() + .getResource("attachContainerTestDockerfile").getFile()); + + String imageId = buildImage(baseDir); + + CreateContainerResponse container = dockerClient.createContainerCmd(imageId).withTty(true).exec(); + + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container.getId()).exec(); + + AttachContainerTestCallback callback = new AttachContainerTestCallback() { + @Override + public void onNext(Frame frame) { + assertEquals(frame.getStreamType(), StreamType.RAW); + super.onNext(frame); + }; + }; + + dockerClient.attachContainerCmd(container.getId()).withStdErr(true).withStdOut(true).withFollowStream(true) + .exec(callback).awaitCompletion(10, TimeUnit.SECONDS).close(); + + System.out.println("log: " + callback.toString()); + + // HexDump.dump(collectFramesCallback.toString().getBytes(), 0, System.out, 0); + + assertThat(callback.toString(), containsString("stdout\r\nstderr")); + } + + public static class AttachContainerTestCallback extends AttachContainerResultCallback { + private StringBuffer log = new StringBuffer(); + + @Override + public void onNext(Frame item) { + log.append(new String(item.getPayload())); + super.onNext(item); + } + + @Override + public String toString() { + return log.toString(); + } + } +} diff --git a/src/test/java/com/github/dockerjava/netty/exec/BuildImageCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/BuildImageCmdExecTest.java new file mode 100644 index 000000000..b60968d06 --- /dev/null +++ b/src/test/java/com/github/dockerjava/netty/exec/BuildImageCmdExecTest.java @@ -0,0 +1,296 @@ +package com.github.dockerjava.netty.exec; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.UUID; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.filefilter.TrueFileFilter; +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.DockerClientException; +import com.github.dockerjava.api.command.BuildImageCmd; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.api.command.InspectImageResponse; +import com.github.dockerjava.api.model.AuthConfig; +import com.github.dockerjava.api.model.AuthConfigurations; +import com.github.dockerjava.api.model.ExposedPort; +import com.github.dockerjava.api.model.PortBinding; +import com.github.dockerjava.api.model.Ports; +import com.github.dockerjava.core.CompressArchiveUtil; +import com.github.dockerjava.core.command.BuildImageResultCallback; +import com.github.dockerjava.core.command.PushImageResultCallback; +import com.github.dockerjava.core.command.WaitContainerResultCallback; +import com.github.dockerjava.netty.AbstractDockerClientTest; + +@Test(groups = "integration") +public class BuildImageCmdExecTest extends AbstractDockerClientTest { + + @BeforeTest + public void beforeTest() throws Exception { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test + public void testNginxDockerfileBuilder() throws Exception { + File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("nginx").getFile()); + + String imageId = buildImage(baseDir); + + InspectImageResponse inspectImageResponse = dockerClient.inspectImageCmd(imageId).exec(); + assertThat(inspectImageResponse, not(nullValue())); + LOG.info("Image Inspect: {}", inspectImageResponse.toString()); + + assertThat(inspectImageResponse.getAuthor(), equalTo("Guillaume J. Charmes \"guillaume@dotcloud.com\"")); + } + + @Test(groups = "ignoreInCircleCi") + public void testNonstandard1() throws Exception { + File baseDir = new File(Thread.currentThread().getContextClassLoader() + .getResource("nonstandard/subdirectory/Dockerfile-nonstandard").getFile()); + + buildImage(baseDir); + } + + @Test(groups = "ignoreInCircleCi") + public void testNonstandard2() throws Exception { + File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("nonstandard").getFile()); + File dockerFile = new File(Thread.currentThread().getContextClassLoader() + .getResource("nonstandard/subdirectory/Dockerfile-nonstandard").getFile()); + + dockerClient.buildImageCmd().withBaseDirectory(baseDir).withDockerfile(dockerFile).withNoCache(true) + .exec(new BuildImageResultCallback()).awaitImageId(); + } + + @Test + public void testDockerBuilderFromTar() throws Exception { + File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("testAddFile").getFile()); + Collection files = FileUtils.listFiles(baseDir, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE); + File tarFile = CompressArchiveUtil.archiveTARFiles(baseDir, files, UUID.randomUUID().toString()); + String response = dockerfileBuild(new FileInputStream(tarFile)); + assertThat(response, containsString("Successfully executed testrun.sh")); + } + + @Test + public void testDockerBuilderAddUrl() throws Exception { + File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("testAddUrl").getFile()); + String response = dockerfileBuild(baseDir); + assertThat(response, containsString("Docker")); + } + + @Test + public void testDockerBuilderAddFileInSubfolder() throws Exception { + File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("testAddFileInSubfolder") + .getFile()); + String response = dockerfileBuild(baseDir); + assertThat(response, containsString("Successfully executed testrun.sh")); + } + + @Test + public void testDockerBuilderAddFilesViaWildcard() throws Exception { + File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("testAddFilesViaWildcard") + .getFile()); + String response = dockerfileBuild(baseDir); + assertThat(response, containsString("Successfully executed testinclude1.sh")); + assertThat(response, not(containsString("Successfully executed testinclude2.sh"))); + } + + @Test + public void testDockerBuilderAddFolder() throws Exception { + File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("testAddFolder").getFile()); + String response = dockerfileBuild(baseDir); + assertThat(response, containsString("Successfully executed testAddFolder.sh")); + } + + @Test + public void testDockerBuilderEnv() throws Exception { + File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("testEnv").getFile()); + String response = dockerfileBuild(baseDir); + assertThat(response, containsString("Successfully executed testrun.sh")); + } + + private String dockerfileBuild(InputStream tarInputStream) throws Exception { + + return execBuild(dockerClient.buildImageCmd().withTarInputStream(tarInputStream)); + } + + private String dockerfileBuild(File baseDir) throws Exception { + + return execBuild(dockerClient.buildImageCmd(baseDir)); + } + + private String execBuild(BuildImageCmd buildImageCmd) throws Exception { + String imageId = buildImageCmd.withNoCache(true).exec(new BuildImageResultCallback()).awaitImageId(); + + // Create container based on image + CreateContainerResponse container = dockerClient.createContainerCmd(imageId).exec(); + + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container.getId()).exec(); + dockerClient.waitContainerCmd(container.getId()).exec(new WaitContainerResultCallback()).awaitStatusCode(); + + return containerLog(container.getId()); + } + + @Test(expectedExceptions = { DockerClientException.class }) + public void testDockerfileIgnored() throws Exception { + File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("testDockerfileIgnored") + .getFile()); + + dockerClient.buildImageCmd(baseDir).withNoCache(true).exec(new BuildImageResultCallback()).awaitImageId(); + } + + @Test + public void testDockerfileNotIgnored() throws Exception { + File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("testDockerfileNotIgnored") + .getFile()); + + dockerClient.buildImageCmd(baseDir).withNoCache(true).exec(new BuildImageResultCallback()).awaitImageId(); + } + + @Test(expectedExceptions = { DockerClientException.class }) + public void testInvalidDockerIgnorePattern() throws Exception { + File baseDir = new File(Thread.currentThread().getContextClassLoader() + .getResource("testInvalidDockerignorePattern").getFile()); + + dockerClient.buildImageCmd(baseDir).withNoCache(true).exec(new BuildImageResultCallback()).awaitImageId(); + } + + @Test(groups = "ignoreInCircleCi") + public void testDockerIgnore() throws Exception { + File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("testDockerignore") + .getFile()); + String response = dockerfileBuild(baseDir); + assertThat(response, containsString("/tmp/a/a /tmp/a/c /tmp/a/d")); + } + + @Test + public void testNetCatDockerfileBuilder() throws Exception { + File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("netcat").getFile()); + + String imageId = dockerClient.buildImageCmd(baseDir).withNoCache(true).exec(new BuildImageResultCallback()) + .awaitImageId(); + + assertNotNull(imageId, "Not successful in build"); + + InspectImageResponse inspectImageResponse = dockerClient.inspectImageCmd(imageId).exec(); + assertThat(inspectImageResponse, not(nullValue())); + assertThat(inspectImageResponse.getId(), not(nullValue())); + LOG.info("Image Inspect: {}", inspectImageResponse.toString()); + + CreateContainerResponse container = dockerClient.createContainerCmd(inspectImageResponse.getId()).exec(); + assertThat(container.getId(), not(isEmptyString())); + dockerClient.startContainerCmd(container.getId()).exec(); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + + assertThat(inspectContainerResponse.getId(), notNullValue()); + assertThat(inspectContainerResponse.getNetworkSettings().getPorts(), notNullValue()); + + // No use as such if not running on the server + // for (Ports.Port p : inspectContainerResponse.getNetworkSettings().getPorts().getAllPorts()) { + // int port = Integer.valueOf(p.getHostPort()); + // LOG.info("Checking port {} is open", port); + // assertThat(available(port), is(false)); + // } + dockerClient.stopContainerCmd(container.getId()).withTimeout(0).exec(); + + } + + @Test + public void testAddAndCopySubstitution() throws Exception { + File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("testENVSubstitution") + .getFile()); + String response = dockerfileBuild(baseDir); + assertThat(response, containsString("testENVSubstitution successfully completed")); + } + + @Test + public void testBuildFromPrivateRegistry() throws Exception { + File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("privateRegistry").getFile()); + + String imageId = buildImage(baseDir); + + InspectImageResponse inspectImageResponse = dockerClient.inspectImageCmd(imageId).exec(); + assertThat(inspectImageResponse, not(nullValue())); + LOG.info("Image Inspect: {}", inspectImageResponse.toString()); + + dockerClient.tagImageCmd(imageId, "testregistry", "2").withForce().exec(); + + // see https://github.com/docker/distribution/blob/master/docs/deploying.md#native-basic-auth + CreateContainerResponse testregistry = dockerClient + .createContainerCmd("testregistry:2") + .withName("registry") + .withPortBindings(new PortBinding(new Ports.Binding(5000), ExposedPort.tcp(5000))) + .withEnv("REGISTRY_AUTH=htpasswd", "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm", + "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd", "REGISTRY_LOG_LEVEL=debug", + "REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt", "REGISTRY_HTTP_TLS_KEY=/certs/domain.key") + .exec(); + + dockerClient.startContainerCmd(testregistry.getId()).exec(); + + AuthConfig authConfig = new AuthConfig(); + + // credentials as configured in /auth/htpasswd + authConfig.setUsername("testuser"); + authConfig.setPassword("testpassword"); + authConfig.setEmail("foo@bar.de"); + authConfig.setServerAddress("localhost:5000"); + + dockerClient.authCmd().withAuthConfig(authConfig).exec(); + dockerClient.tagImageCmd("busybox:latest", "localhost:5000/testuser/busybox", "latest").withForce().exec(); + + dockerClient.pushImageCmd("localhost:5000/testuser/busybox").withTag("latest").withAuthConfig(authConfig) + .exec(new PushImageResultCallback()).awaitSuccess(); + + dockerClient.removeImageCmd("localhost:5000/testuser/busybox").withForce(true).exec(); + + baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("testBuildFromPrivateRegistry") + .getFile()); + + AuthConfigurations authConfigurations = new AuthConfigurations(); + authConfigurations.addConfig(authConfig); + + imageId = dockerClient.buildImageCmd(baseDir).withNoCache(true).withBuildAuthConfigs(authConfigurations) + .exec(new BuildImageResultCallback()).awaitImageId(); + + inspectImageResponse = dockerClient.inspectImageCmd(imageId).exec(); + assertThat(inspectImageResponse, not(nullValue())); + LOG.info("Image Inspect: {}", inspectImageResponse.toString()); + + } +} diff --git a/src/test/java/com/github/dockerjava/netty/exec/CommitCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/CommitCmdExecTest.java new file mode 100644 index 000000000..fbb5cd547 --- /dev/null +++ b/src/test/java/com/github/dockerjava/netty/exec/CommitCmdExecTest.java @@ -0,0 +1,80 @@ +package com.github.dockerjava.netty.exec; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.startsWith; +import static org.testinfected.hamcrest.jpa.HasFieldWithValue.hasField; + +import java.lang.reflect.Method; + +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.command.InspectImageResponse; +import com.github.dockerjava.netty.AbstractDockerClientTest; + +@Test(groups = "integration") +public class CommitCmdExecTest extends AbstractDockerClientTest { + + @BeforeTest + public void beforeTest() throws Exception { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test + public void commit() throws DockerException { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("touch", "/test").exec(); + + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + dockerClient.startContainerCmd(container.getId()).exec(); + + LOG.info("Commiting container: {}", container.toString()); + String imageId = dockerClient.commitCmd(container.getId()).exec(); + + InspectImageResponse inspectImageResponse = dockerClient.inspectImageCmd(imageId).exec(); + LOG.info("Image Inspect: {}", inspectImageResponse.toString()); + + assertThat(inspectImageResponse, hasField("container", startsWith(container.getId()))); + assertThat(inspectImageResponse.getContainerConfig().getImage(), equalTo("busybox")); + + InspectImageResponse busyboxImg = dockerClient.inspectImageCmd("busybox").exec(); + + assertThat(inspectImageResponse.getParent(), equalTo(busyboxImg.getId())); + } + + @Test + public void commitNonExistingContainer() throws DockerException { + try { + dockerClient.commitCmd("non-existent").exec(); + fail("expected NotFoundException"); + } catch (NotFoundException e) { + } + } + +} diff --git a/src/test/java/com/github/dockerjava/netty/exec/ContainerDiffCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/ContainerDiffCmdExecTest.java new file mode 100644 index 000000000..6d03064da --- /dev/null +++ b/src/test/java/com/github/dockerjava/netty/exec/ContainerDiffCmdExecTest.java @@ -0,0 +1,80 @@ +package com.github.dockerjava.netty.exec; + +import static ch.lambdaj.Lambda.selectUnique; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.Matchers.not; +import static org.testinfected.hamcrest.jpa.HasFieldWithValue.hasField; + +import java.lang.reflect.Method; +import java.util.List; + +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.model.ChangeLog; +import com.github.dockerjava.core.command.WaitContainerResultCallback; +import com.github.dockerjava.netty.AbstractDockerClientTest; + +@Test(groups = "integration") +public class ContainerDiffCmdExecTest extends AbstractDockerClientTest { + + @BeforeTest + public void beforeTest() throws Exception { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test(groups = "ignoreInCircleCi") + public void testContainerDiff() throws DockerException { + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("touch", "/test").exec(); + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + dockerClient.startContainerCmd(container.getId()).exec(); + + int exitCode = dockerClient.waitContainerCmd(container.getId()).exec(new WaitContainerResultCallback()) + .awaitStatusCode(); + assertThat(exitCode, equalTo(0)); + + List filesystemDiff = dockerClient.containerDiffCmd(container.getId()).exec(); + LOG.info("Container DIFF: {}", filesystemDiff.toString()); + + assertThat(filesystemDiff.size(), equalTo(1)); + ChangeLog testChangeLog = selectUnique(filesystemDiff, hasField("path", equalTo("/test"))); + + assertThat(testChangeLog, hasField("path", equalTo("/test"))); + assertThat(testChangeLog, hasField("kind", equalTo(1))); + } + + @Test + public void testContainerDiffWithNonExistingContainer() throws DockerException { + try { + dockerClient.containerDiffCmd("non-existing").exec(); + fail("expected NotFoundException"); + } catch (NotFoundException e) { + } + } + +} diff --git a/src/test/java/com/github/dockerjava/netty/exec/CopyFileFromContainerCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/CopyFileFromContainerCmdExecTest.java new file mode 100644 index 000000000..d0a705a3a --- /dev/null +++ b/src/test/java/com/github/dockerjava/netty/exec/CopyFileFromContainerCmdExecTest.java @@ -0,0 +1,73 @@ +package com.github.dockerjava.netty.exec; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.isEmptyOrNullString; +import static org.hamcrest.Matchers.not; + +import java.io.InputStream; +import java.lang.reflect.Method; + +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.netty.AbstractDockerClientTest; + +@Test(groups = "integration") +public class CopyFileFromContainerCmdExecTest extends AbstractDockerClientTest { + + @BeforeTest + public void beforeTest() throws Exception { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test + public void copyFromContainer() throws Exception { + // TODO extract this into a shared method + CreateContainerResponse container = dockerClient.createContainerCmd("busybox") + .withName("docker-java-itest-copyFromContainer").withCmd("touch", "/copyFromContainer").exec(); + + LOG.info("Created container: {}", container); + assertThat(container.getId(), not(isEmptyOrNullString())); + + dockerClient.startContainerCmd(container.getId()).exec(); + + InputStream response = dockerClient.copyFileFromContainerCmd(container.getId(), "/copyFromContainer").exec(); + Boolean bytesAvailable = response.available() > 0; + assertTrue(bytesAvailable, "The file was not copied from the container."); + + // read the stream fully. Otherwise, the underlying stream will not be closed. + String responseAsString = asString(response); + assertNotNull(responseAsString); + assertTrue(responseAsString.length() > 0); + } + + @Test + public void copyFromNonExistingContainer() throws Exception { + try { + dockerClient.copyFileFromContainerCmd("non-existing", "/test").exec(); + fail("expected NotFoundException"); + } catch (NotFoundException ignored) { + } + } +} diff --git a/src/test/java/com/github/dockerjava/netty/exec/EventsCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/EventsCmdExecTest.java new file mode 100644 index 000000000..0122858a7 --- /dev/null +++ b/src/test/java/com/github/dockerjava/netty/exec/EventsCmdExecTest.java @@ -0,0 +1,162 @@ +package com.github.dockerjava.netty.exec; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.model.Event; +import com.github.dockerjava.api.model.EventFilters; +import com.github.dockerjava.core.command.EventsResultCallback; +import com.github.dockerjava.core.command.PullImageResultCallback; +import com.github.dockerjava.netty.AbstractDockerClientTest; + +@Test(groups = "integration") +public class EventsCmdExecTest extends AbstractDockerClientTest { + + private static int KNOWN_NUM_EVENTS = 4; + + private static String getEpochTime() { + return String.valueOf(System.currentTimeMillis() / 1000); + } + + @BeforeTest + public void beforeTest() throws Exception { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + /* + * This specific test may fail with boot2docker as time may not in sync with host system + */ + @Test + public void testEventStreamTimeBound() throws Exception { + // Don't include other tests events + TimeUnit.SECONDS.sleep(1); + + String startTime = getEpochTime(); + int expectedEvents = generateEvents(); + String endTime = getEpochTime(); + + CountDownLatch countDownLatch = new CountDownLatch(expectedEvents); + EventsTestCallback eventCallback = new EventsTestCallback(countDownLatch); + + dockerClient.eventsCmd().withSince(startTime).withUntil(endTime).exec(eventCallback); + + Boolean zeroCount = countDownLatch.await(10, TimeUnit.SECONDS); + + eventCallback.close(); + + assertTrue(zeroCount, "Received only: " + eventCallback.getEvents()); + } + + @Test + public void testEventStreaming1() throws Exception { + // Don't include other tests events + TimeUnit.SECONDS.sleep(1); + + CountDownLatch countDownLatch = new CountDownLatch(KNOWN_NUM_EVENTS); + EventsTestCallback eventCallback = new EventsTestCallback(countDownLatch); + + dockerClient.eventsCmd().withSince(getEpochTime()).exec(eventCallback); + + generateEvents(); + + Boolean zeroCount = countDownLatch.await(10, TimeUnit.SECONDS); + + eventCallback.close(); + assertTrue(zeroCount, "Received only: " + eventCallback.getEvents()); + } + + @Test + public void testEventStreaming2() throws Exception { + // Don't include other tests events + TimeUnit.SECONDS.sleep(1); + + CountDownLatch countDownLatch = new CountDownLatch(KNOWN_NUM_EVENTS); + EventsTestCallback eventCallback = new EventsTestCallback(countDownLatch); + + dockerClient.eventsCmd().withSince(getEpochTime()).exec(eventCallback); + + generateEvents(); + + Boolean zeroCount = countDownLatch.await(10, TimeUnit.SECONDS); + + eventCallback.close(); + assertTrue(zeroCount, "Received only: " + eventCallback.getEvents()); + } + + public void testEventStreamingWithFilter() throws Exception { + // Don't include other tests events + TimeUnit.SECONDS.sleep(1); + + CountDownLatch countDownLatch = new CountDownLatch(1); + EventsTestCallback eventCallback = dockerClient.eventsCmd().withFilters(new EventFilters().withEvent("start")) + .exec(new EventsTestCallback(countDownLatch)); + + generateEvents(); + + Boolean zeroCount = countDownLatch.await(10, TimeUnit.SECONDS); + + eventCallback.close(); + assertTrue(zeroCount, "Received only: " + eventCallback.getEvents()); + } + + /** + * This method generates {#link KNOWN_NUM_EVENTS} events + */ + private int generateEvents() throws Exception { + String testImage = "busybox"; + + dockerClient.pullImageCmd(testImage).exec(new PullImageResultCallback()).awaitSuccess(); + + CreateContainerResponse container = dockerClient.createContainerCmd(testImage).withCmd("sleep", "9999").exec(); + dockerClient.startContainerCmd(container.getId()).exec(); + dockerClient.stopContainerCmd(container.getId()).exec(); + return KNOWN_NUM_EVENTS; + } + + private class EventsTestCallback extends EventsResultCallback { + + private final CountDownLatch countDownLatch; + + private final List events = new ArrayList(); + + public EventsTestCallback(CountDownLatch countDownLatch) { + this.countDownLatch = countDownLatch; + } + + public void onNext(Event event) { + LOG.info("Received event #{}: {}", countDownLatch.getCount(), event); + countDownLatch.countDown(); + events.add(event); + } + + public List getEvents() { + return new ArrayList(events); + } + } +} diff --git a/src/test/java/com/github/dockerjava/netty/exec/ExecStartCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/ExecStartCmdExecTest.java index 3702a7102..a05e91b99 100644 --- a/src/test/java/com/github/dockerjava/netty/exec/ExecStartCmdExecTest.java +++ b/src/test/java/com/github/dockerjava/netty/exec/ExecStartCmdExecTest.java @@ -4,9 +4,12 @@ import static org.hamcrest.Matchers.isEmptyString; import static org.hamcrest.Matchers.not; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.lang.reflect.Method; import java.security.SecureRandom; +import java.util.concurrent.TimeUnit; import org.testng.ITestResult; import org.testng.annotations.AfterMethod; @@ -94,4 +97,50 @@ public void execStartAttached() throws Exception { assertNotNull(responseAsString); assertTrue(responseAsString.length() > 0); } + + @Test(groups = "ignoreInCircleCi") + public void execStartAttachStdin() throws Exception { + String containerName = "generated_" + new SecureRandom().nextInt(); + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999") + .withName(containerName).exec(); + LOG.info("Created container {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container.getId()).exec(); + + InputStream stdin = new ByteArrayInputStream("echo STDIN\n".getBytes()); + + ByteArrayOutputStream stdout = new ByteArrayOutputStream(); + + ExecCreateCmdResponse execCreateCmdResponse = dockerClient.execCreateCmd(container.getId()) + .withAttachStdout(true).withAttachStdin(true).withCmd("/bin/sh").exec(); + dockerClient.execStartCmd(execCreateCmdResponse.getId()).withDetach(false).withTty(true).withStdIn(stdin) + .exec(new ExecStartResultCallback(stdout, System.err)).awaitCompletion(5, TimeUnit.SECONDS); + + assertEquals(stdout.toString(), "STDIN\n"); + } + + @Test(groups = "ignoreInCircleCi") + public void execStartNotAttachedStdin() throws Exception { + String containerName = "generated_" + new SecureRandom().nextInt(); + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999") + .withName(containerName).exec(); + LOG.info("Created container {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container.getId()).exec(); + + InputStream stdin = new ByteArrayInputStream("echo STDIN\n".getBytes()); + + ByteArrayOutputStream stdout = new ByteArrayOutputStream(); + + ExecCreateCmdResponse execCreateCmdResponse = dockerClient.execCreateCmd(container.getId()) + .withAttachStdout(true).withAttachStdin(false).withCmd("/bin/sh").exec(); + dockerClient.execStartCmd(execCreateCmdResponse.getId()).withDetach(false).withTty(true).withStdIn(stdin) + .exec(new ExecStartResultCallback(stdout, System.err)).awaitCompletion(5, TimeUnit.SECONDS); + + assertEquals(stdout.toString(), ""); + } } diff --git a/src/test/java/com/github/dockerjava/netty/exec/InspectContainerCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/InspectContainerCmdExecTest.java index dde8b3ab9..e9403caa1 100644 --- a/src/test/java/com/github/dockerjava/netty/exec/InspectContainerCmdExecTest.java +++ b/src/test/java/com/github/dockerjava/netty/exec/InspectContainerCmdExecTest.java @@ -1,8 +1,6 @@ package com.github.dockerjava.netty.exec; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.isEmptyString; import static org.hamcrest.Matchers.not; @@ -21,12 +19,8 @@ import com.github.dockerjava.api.DockerException; import com.github.dockerjava.api.NotFoundException; import com.github.dockerjava.api.command.CreateContainerResponse; -import com.github.dockerjava.api.command.ExecCreateCmdResponse; import com.github.dockerjava.api.command.InspectContainerResponse; -import com.github.dockerjava.api.command.InspectExecResponse; -import com.github.dockerjava.core.command.ExecStartResultCallback; import com.github.dockerjava.netty.AbstractDockerClientTest; -import com.github.dockerjava.test.serdes.JSONTestHelper; @Test(groups = "integration") public class InspectContainerCmdExecTest extends AbstractDockerClientTest { diff --git a/src/test/java/com/github/dockerjava/netty/exec/InspectExecCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/InspectExecCmdExecTest.java index c2fbc84a0..5efadaa2a 100644 --- a/src/test/java/com/github/dockerjava/netty/exec/InspectExecCmdExecTest.java +++ b/src/test/java/com/github/dockerjava/netty/exec/InspectExecCmdExecTest.java @@ -59,7 +59,7 @@ public void inspectExecTest() throws IOException { dockerClient.startContainerCmd(container.getId()).exec(); ExecCreateCmdResponse touchFileCmdCreateResponse = dockerClient.execCreateCmd(container.getId()) - .withAttachStdout(true).withAttachStderr(true).withCmd("touch", "/marker").exec(); + .withAttachStdout(true).withAttachStderr(true).withCmd("touch", "/marker").withTty(true).exec(); LOG.info("Created exec {}", touchFileCmdCreateResponse.toString()); assertThat(touchFileCmdCreateResponse.getId(), not(isEmptyString())); ExecCreateCmdResponse checkFileCmdCreateResponse = dockerClient.execCreateCmd(container.getId()) @@ -68,22 +68,26 @@ public void inspectExecTest() throws IOException { assertThat(checkFileCmdCreateResponse.getId(), not(isEmptyString())); // Check that file does not exist - dockerClient.execStartCmd(container.getId()) - .withExecId(checkFileCmdCreateResponse.getId()).exec(new ExecStartResultCallback(System.out, System.err)); + dockerClient.execStartCmd(container.getId()).withDetach(false).withTty(true) + .withExecId(checkFileCmdCreateResponse.getId()) + .exec(new ExecStartResultCallback(System.out, System.err)); InspectExecResponse first = dockerClient.inspectExecCmd(checkFileCmdCreateResponse.getId()).exec(); + assertEquals(first.isRunning(), new Boolean(false)); assertThat(first.getExitCode(), is(1)); // Create the file - dockerClient.execStartCmd(container.getId()) - .withExecId(touchFileCmdCreateResponse.getId()).exec(new ExecStartResultCallback(System.out, System.err)); + dockerClient.execStartCmd(container.getId()).withDetach(false).withTty(true) + .withExecId(touchFileCmdCreateResponse.getId()) + .exec(new ExecStartResultCallback(System.out, System.err)); InspectExecResponse second = dockerClient.inspectExecCmd(touchFileCmdCreateResponse.getId()).exec(); + assertEquals(first.isRunning(), new Boolean(false)); assertThat(second.getExitCode(), is(0)); // Check that file does exist now - dockerClient.execStartCmd(container.getId()) - .withExecId(checkFileCmdCreateResponse.getId()).exec(new ExecStartResultCallback(System.out, System.err)); + dockerClient.execStartCmd(container.getId()).withExecId(checkFileCmdCreateResponse.getId()) + .exec(new ExecStartResultCallback(System.out, System.err)); InspectExecResponse third = dockerClient.inspectExecCmd(checkFileCmdCreateResponse.getId()).exec(); assertThat(third.getExitCode(), is(0)); diff --git a/src/test/java/com/github/dockerjava/netty/exec/ListContainersCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/ListContainersCmdExecTest.java index 3f97aa69d..bf4cb7504 100644 --- a/src/test/java/com/github/dockerjava/netty/exec/ListContainersCmdExecTest.java +++ b/src/test/java/com/github/dockerjava/netty/exec/ListContainersCmdExecTest.java @@ -58,8 +58,8 @@ public void testListContainers() throws Exception { String testImage = "busybox"; -// // need to block until image is pulled completely -// dockerClient.pullImageCmd(testImage).exec(new PullImageResultCallback()).awaitSuccess(); + // // need to block until image is pulled completely + // dockerClient.pullImageCmd(testImage).exec(new PullImageResultCallback()).awaitSuccess(); List containers = dockerClient.listContainersCmd().withShowAll(true).exec(); assertThat(containers, notNullValue()); diff --git a/src/test/java/com/github/dockerjava/netty/exec/ListImagesCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/ListImagesCmdExecTest.java new file mode 100644 index 000000000..24def3d4a --- /dev/null +++ b/src/test/java/com/github/dockerjava/netty/exec/ListImagesCmdExecTest.java @@ -0,0 +1,102 @@ +package com.github.dockerjava.netty.exec; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.emptyArray; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.notNullValue; + +import java.lang.reflect.Method; +import java.util.List; + +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.model.Image; +import com.github.dockerjava.api.model.Info; +import com.github.dockerjava.netty.AbstractDockerClientTest; + +@Test(groups = "integration") +public class ListImagesCmdExecTest extends AbstractDockerClientTest { + + @BeforeTest + public void beforeTest() throws Exception { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test + public void listImages() throws DockerException { + List images = dockerClient.listImagesCmd().withShowAll(true).exec(); + assertThat(images, notNullValue()); + LOG.info("Images List: {}", images); + Info info = dockerClient.infoCmd().exec(); + + assertThat(images.size(), equalTo(info.getImages())); + + Image img = images.get(0); + assertThat(img.getCreated(), is(greaterThan(0L))); + assertThat(img.getVirtualSize(), is(greaterThan(0L))); + assertThat(img.getId(), not(isEmptyString())); + assertThat(img.getRepoTags(), not(emptyArray())); + } + + @Test(groups = "ignoreInCircleCi") + public void listDanglingImages() throws DockerException { + String imageId = createDanglingImage(); + List images = dockerClient.listImagesCmd().withFilters("{\"dangling\":[\"true\"]}").withShowAll(true) + .exec(); + assertThat(images, notNullValue()); + LOG.info("Images List: {}", images); + assertThat(images.size(), is(greaterThan(0))); + Boolean imageInFilteredList = isImageInFilteredList(images, imageId); + assertTrue(imageInFilteredList); + } + + private boolean isImageInFilteredList(List images, String expectedImageId) { + for (Image image : images) { + if (expectedImageId.equals(image.getId())) { + return true; + } + } + return false; + } + + private String createDanglingImage() { + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("sleep", "5").exec(); + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + dockerClient.startContainerCmd(container.getId()).exec(); + + LOG.info("Committing container {}", container.toString()); + String imageId = dockerClient.commitCmd(container.getId()).exec(); + + dockerClient.stopContainerCmd(container.getId()).exec(); + dockerClient.killContainerCmd(container.getId()).exec(); + dockerClient.removeContainerCmd(container.getId()).exec(); + return imageId; + } +} diff --git a/src/test/java/com/github/dockerjava/netty/exec/PingCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/PingCmdExecTest.java new file mode 100644 index 000000000..b98ff9bfd --- /dev/null +++ b/src/test/java/com/github/dockerjava/netty/exec/PingCmdExecTest.java @@ -0,0 +1,43 @@ +package com.github.dockerjava.netty.exec; + +import java.lang.reflect.Method; + +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.netty.AbstractDockerClientTest; + +@Test(groups = "integration") +public class PingCmdExecTest extends AbstractDockerClientTest { + + @BeforeTest + public void beforeTest() throws Exception { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test + public void ping() throws DockerException { + dockerClient.pingCmd().exec(); + } + +} diff --git a/src/test/java/com/github/dockerjava/netty/exec/PullImageCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/PullImageCmdExecTest.java new file mode 100644 index 000000000..620519326 --- /dev/null +++ b/src/test/java/com/github/dockerjava/netty/exec/PullImageCmdExecTest.java @@ -0,0 +1,119 @@ +package com.github.dockerjava.netty.exec; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.lessThanOrEqualTo; +import static org.hamcrest.Matchers.notNullValue; + +import java.lang.reflect.Method; + +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.async.ResultCallback; +import com.github.dockerjava.api.command.InspectImageResponse; +import com.github.dockerjava.api.command.PullImageCmd; +import com.github.dockerjava.api.model.Info; +import com.github.dockerjava.api.model.PullResponseItem; +import com.github.dockerjava.core.command.PullImageCmdImpl; +import com.github.dockerjava.core.command.PullImageResultCallback; +import com.github.dockerjava.netty.AbstractDockerClientTest; + +@Test(groups = "integration") +public class PullImageCmdExecTest extends AbstractDockerClientTest { + + private static final PullImageCmd.Exec NOP_EXEC = new PullImageCmd.Exec() { + public Void exec(PullImageCmd command, ResultCallback resultCallback) { + return null; + }; + }; + + @BeforeTest + public void beforeTest() throws Exception { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test + public void nullAuthConfig() throws Exception { + PullImageCmdImpl pullImageCmd = new PullImageCmdImpl(NOP_EXEC, null, ""); + try { + pullImageCmd.withAuthConfig(null); + fail(); + } catch (Exception e) { + assertEquals(e.getMessage(), "authConfig was not specified"); + } finally { + pullImageCmd.close(); + } + } + + @Test + public void testPullImage() throws Exception { + Info info = dockerClient.infoCmd().exec(); + LOG.info("Client info: {}", info.toString()); + + int imgCount = info.getImages(); + LOG.info("imgCount1: {}", imgCount); + + // This should be an image that is not used by other repositories + // already + // pulled down, preferably small in size. If tag is not used pull will + // download all images in that repository but tmpImgs will only + // deleted 'latest' image but not images with other tags + String testImage = "hackmann/empty"; + + LOG.info("Removing image: {}", testImage); + + try { + dockerClient.removeImageCmd(testImage).withForce(true).exec(); + } catch (NotFoundException e) { + // just ignore if not exist + } + + info = dockerClient.infoCmd().exec(); + LOG.info("Client info: {}", info.toString()); + + imgCount = info.getImages(); + LOG.info("imgCount2: {}", imgCount); + + LOG.info("Pulling image: {}", testImage); + + dockerClient.pullImageCmd(testImage).exec(new PullImageResultCallback()).awaitSuccess(); + + info = dockerClient.infoCmd().exec(); + LOG.info("Client info after pull, {}", info.toString()); + + assertThat(imgCount, lessThanOrEqualTo(info.getImages())); + + InspectImageResponse inspectImageResponse = dockerClient.inspectImageCmd(testImage).exec(); + LOG.info("Image Inspect: {}", inspectImageResponse.toString()); + assertThat(inspectImageResponse, notNullValue()); + } + + @Test + public void testPullNonExistingImage() throws Exception { + + // does not throw an exception + // stream needs to be fully read in order to close the underlying connection + dockerClient.pullImageCmd("xvxcv/foo").exec(new PullImageResultCallback()).awaitCompletion(); + } + +} diff --git a/src/test/java/com/github/dockerjava/netty/exec/PushImageCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/PushImageCmdExecTest.java new file mode 100644 index 000000000..3c8bd4eb8 --- /dev/null +++ b/src/test/java/com/github/dockerjava/netty/exec/PushImageCmdExecTest.java @@ -0,0 +1,79 @@ +package com.github.dockerjava.netty.exec; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.Matchers.not; + +import java.lang.reflect.Method; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.DockerClientException; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.core.command.PullImageResultCallback; +import com.github.dockerjava.core.command.PushImageResultCallback; +import com.github.dockerjava.netty.AbstractDockerClientTest; + +@Test(groups = "integration") +public class PushImageCmdExecTest extends AbstractDockerClientTest { + + public static final Logger LOG = LoggerFactory.getLogger(PushImageCmdExecTest.class); + + String username; + + @BeforeTest + public void beforeTest() throws Exception { + super.beforeTest(); + username = dockerClient.authConfig().getUsername(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test + public void pushLatest() throws Exception { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("true").exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + LOG.info("Committing container: {}", container.toString()); + String imageId = dockerClient.commitCmd(container.getId()).withRepository(username + "/busybox").exec(); + + // we have to block until image is pushed + dockerClient.pushImageCmd(username + "/busybox").exec(new PushImageResultCallback()).awaitSuccess(); + + LOG.info("Removing image: {}", imageId); + dockerClient.removeImageCmd(imageId).exec(); + + dockerClient.pullImageCmd(username + "/busybox").exec(new PullImageResultCallback()).awaitSuccess(); + } + + @Test(expectedExceptions = DockerClientException.class) + public void pushNonExistentImage() throws Exception { + + dockerClient.pushImageCmd(username + "/xxx").exec(new PushImageResultCallback()).awaitSuccess(); + } + +} diff --git a/src/test/java/com/github/dockerjava/netty/exec/RemoveContainerCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/RemoveContainerCmdExecTest.java new file mode 100644 index 000000000..58be803d6 --- /dev/null +++ b/src/test/java/com/github/dockerjava/netty/exec/RemoveContainerCmdExecTest.java @@ -0,0 +1,81 @@ +package com.github.dockerjava.netty.exec; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.startsWith; +import static org.testinfected.hamcrest.jpa.HasFieldWithValue.hasField; + +import java.lang.reflect.Method; +import java.util.List; + +import org.hamcrest.Matcher; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.model.Container; +import com.github.dockerjava.core.command.WaitContainerResultCallback; +import com.github.dockerjava.netty.AbstractDockerClientTest; + +@Test(groups = "integration") +public class RemoveContainerCmdExecTest extends AbstractDockerClientTest { + + public static final Logger LOG = LoggerFactory.getLogger(RemoveContainerCmdExecTest.class); + + @BeforeTest + public void beforeTest() throws Exception { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test(groups = "ignoreInCircleCi") + public void removeContainer() throws DockerException { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("true").exec(); + + dockerClient.startContainerCmd(container.getId()).exec(); + dockerClient.waitContainerCmd(container.getId()).exec(new WaitContainerResultCallback()).awaitStatusCode(); + + LOG.info("Removing container: {}", container.getId()); + dockerClient.removeContainerCmd(container.getId()).exec(); + + List containers2 = dockerClient.listContainersCmd().withShowAll(true).exec(); + + Matcher matcher = not(hasItem(hasField("id", startsWith(container.getId())))); + assertThat(containers2, matcher); + + } + + @Test + public void removeNonExistingContainer() throws DockerException { + try { + dockerClient.removeContainerCmd("non-existing").exec(); + fail("expected NotFoundException"); + } catch (NotFoundException e) { + } + } + +} diff --git a/src/test/java/com/github/dockerjava/netty/exec/RemoveImageCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/RemoveImageCmdExecTest.java new file mode 100644 index 000000000..e1e11c308 --- /dev/null +++ b/src/test/java/com/github/dockerjava/netty/exec/RemoveImageCmdExecTest.java @@ -0,0 +1,88 @@ +package com.github.dockerjava.netty.exec; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.startsWith; +import static org.testinfected.hamcrest.jpa.HasFieldWithValue.hasField; + +import java.lang.reflect.Method; +import java.util.List; + +import org.hamcrest.Matcher; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.model.Container; +import com.github.dockerjava.netty.AbstractDockerClientTest; + +@Test(groups = "integration") +public class RemoveImageCmdExecTest extends AbstractDockerClientTest { + + public static final Logger LOG = LoggerFactory.getLogger(RemoveImageCmdExecTest.class); + + @BeforeTest + public void beforeTest() throws Exception { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test(groups = "ignoreInCircleCi") + public void removeImage() throws DockerException, InterruptedException { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999").exec(); + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + dockerClient.startContainerCmd(container.getId()).exec(); + + LOG.info("Committing container {}", container.toString()); + String imageId = dockerClient.commitCmd(container.getId()).exec(); + + dockerClient.stopContainerCmd(container.getId()).exec(); + dockerClient.killContainerCmd(container.getId()).exec(); + dockerClient.removeContainerCmd(container.getId()).exec(); + + LOG.info("Removing image: {}", imageId); + dockerClient.removeImageCmd(imageId).exec(); + + List containers = dockerClient.listContainersCmd().withShowAll(true).exec(); + + Matcher matcher = not(hasItem(hasField("id", startsWith(imageId)))); + assertThat(containers, matcher); + } + + @Test + public void removeNonExistingImage() throws DockerException, InterruptedException { + try { + dockerClient.removeImageCmd("non-existing").exec(); + fail("expected NotFoundException"); + } catch (NotFoundException e) { + } + + } + +} diff --git a/src/test/java/com/github/dockerjava/netty/exec/RestartContainerCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/RestartContainerCmdExecTest.java new file mode 100644 index 000000000..6fd50e122 --- /dev/null +++ b/src/test/java/com/github/dockerjava/netty/exec/RestartContainerCmdExecTest.java @@ -0,0 +1,84 @@ +package com.github.dockerjava.netty.exec; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.Matchers.not; + +import java.lang.reflect.Method; + +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.netty.AbstractDockerClientTest; + +@Test(groups = "integration") +public class RestartContainerCmdExecTest extends AbstractDockerClientTest { + + @BeforeTest + public void beforeTest() throws Exception { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test + public void restartContainer() throws DockerException { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999").exec(); + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + dockerClient.startContainerCmd(container.getId()).exec(); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + LOG.info("Container Inspect: {}", inspectContainerResponse.toString()); + + String startTime = inspectContainerResponse.getState().getStartedAt(); + + dockerClient.restartContainerCmd(container.getId()).withtTimeout(2).exec(); + + InspectContainerResponse inspectContainerResponse2 = dockerClient.inspectContainerCmd(container.getId()).exec(); + LOG.info("Container Inspect After Restart: {}", inspectContainerResponse2.toString()); + + String startTime2 = inspectContainerResponse2.getState().getStartedAt(); + + assertThat(startTime, not(equalTo(startTime2))); + + assertThat(inspectContainerResponse.getState().isRunning(), is(equalTo(true))); + + dockerClient.killContainerCmd(container.getId()).exec(); + } + + @Test + public void restartNonExistingContainer() throws DockerException, InterruptedException { + try { + dockerClient.restartContainerCmd("non-existing").exec(); + fail("expected NotFoundException"); + } catch (NotFoundException e) { + } + + } + +} diff --git a/src/test/java/com/github/dockerjava/netty/exec/SaveImageCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/SaveImageCmdExecTest.java new file mode 100644 index 000000000..399eb5341 --- /dev/null +++ b/src/test/java/com/github/dockerjava/netty/exec/SaveImageCmdExecTest.java @@ -0,0 +1,55 @@ +package com.github.dockerjava.netty.exec; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.greaterThan; + +import java.io.InputStream; +import java.lang.reflect.Method; + +import org.apache.commons.io.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.netty.AbstractDockerClientTest; + +@Test(groups = "integration") +public class SaveImageCmdExecTest extends AbstractDockerClientTest { + public static final Logger LOG = LoggerFactory.getLogger(SaveImageCmdExecTest.class); + + String username; + + @BeforeTest + public void beforeTest() throws Exception { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test + public void saveImage() throws Exception { + + InputStream image = IOUtils.toBufferedInputStream(dockerClient.saveImageCmd("busybox").exec()); + assertThat(image.available(), greaterThan(0)); + + } + +} diff --git a/src/test/java/com/github/dockerjava/netty/exec/SearchImagesCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/SearchImagesCmdExecTest.java new file mode 100644 index 000000000..1055d6782 --- /dev/null +++ b/src/test/java/com/github/dockerjava/netty/exec/SearchImagesCmdExecTest.java @@ -0,0 +1,59 @@ +package com.github.dockerjava.netty.exec; + +import static ch.lambdaj.Lambda.filter; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.is; +import static org.testinfected.hamcrest.jpa.HasFieldWithValue.hasField; + +import java.lang.reflect.Method; +import java.util.List; + +import org.hamcrest.Matcher; +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.model.SearchItem; +import com.github.dockerjava.netty.AbstractDockerClientTest; + +@Test(groups = "integration") +public class SearchImagesCmdExecTest extends AbstractDockerClientTest { + + @BeforeTest + public void beforeTest() throws Exception { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test + public void searchImages() throws DockerException { + List dockerSearch = dockerClient.searchImagesCmd("busybox").exec(); + LOG.info("Search returned {}", dockerSearch.toString()); + + Matcher matcher = hasItem(hasField("name", equalTo("busybox"))); + assertThat(dockerSearch, matcher); + + assertThat(filter(hasField("name", is("busybox")), dockerSearch).size(), equalTo(1)); + } + +} diff --git a/src/test/java/com/github/dockerjava/netty/exec/StartContainerCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/StartContainerCmdExecTest.java new file mode 100644 index 000000000..5beded265 --- /dev/null +++ b/src/test/java/com/github/dockerjava/netty/exec/StartContainerCmdExecTest.java @@ -0,0 +1,553 @@ +package com.github.dockerjava.netty.exec; + +import static com.github.dockerjava.api.model.AccessMode.ro; +import static com.github.dockerjava.api.model.Capability.MKNOD; +import static com.github.dockerjava.api.model.Capability.NET_ADMIN; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.startsWith; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.UUID; + +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.InternalServerErrorException; +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.api.command.StartContainerCmd; +import com.github.dockerjava.api.model.AccessMode; +import com.github.dockerjava.api.model.Bind; +import com.github.dockerjava.api.model.Device; +import com.github.dockerjava.api.model.ExposedPort; +import com.github.dockerjava.api.model.Link; +import com.github.dockerjava.api.model.Ports; +import com.github.dockerjava.api.model.RestartPolicy; +import com.github.dockerjava.api.model.Volume; +import com.github.dockerjava.api.model.VolumeRW; +import com.github.dockerjava.api.model.VolumesFrom; +import com.github.dockerjava.core.command.WaitContainerResultCallback; +import com.github.dockerjava.netty.AbstractDockerClientTest; + +@Test(groups = "integration") +public class StartContainerCmdExecTest extends AbstractDockerClientTest { + + @BeforeTest + public void beforeTest() throws Exception { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test + public void startContainerWithVolumes() throws DockerException { + + // see http://docs.docker.io/use/working_with_volumes/ + Volume volume1 = new Volume("/opt/webapp1"); + + Volume volume2 = new Volume("/opt/webapp2"); + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withVolumes(volume1, volume2) + .withCmd("true").withBinds(new Bind("/src/webapp1", volume1, ro), new Bind("/src/webapp2", volume2)) + .exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + + assertThat(inspectContainerResponse.getConfig().getVolumes().keySet(), contains("/opt/webapp1", "/opt/webapp2")); + + dockerClient.startContainerCmd(container.getId()).exec(); + + dockerClient.waitContainerCmd(container.getId()).exec(new WaitContainerResultCallback()).awaitStatusCode(); + + inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + + assertContainerHasVolumes(inspectContainerResponse, volume1, volume2); + + assertThat(Arrays.asList(inspectContainerResponse.getVolumesRW()), + contains(new VolumeRW(volume1, AccessMode.ro), new VolumeRW(volume2))); + + } + + @Test + public void startContainerWithVolumesFrom() throws DockerException { + + Volume volume1 = new Volume("/opt/webapp1"); + Volume volume2 = new Volume("/opt/webapp2"); + + String container1Name = UUID.randomUUID().toString(); + + CreateContainerResponse container1 = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999") + .withName(container1Name) + .withBinds(new Bind("/src/webapp1", volume1), new Bind("/src/webapp2", volume2)).exec(); + LOG.info("Created container1 {}", container1.toString()); + + dockerClient.startContainerCmd(container1.getId()).exec(); + LOG.info("Started container1 {}", container1.toString()); + + InspectContainerResponse inspectContainerResponse1 = dockerClient.inspectContainerCmd(container1.getId()) + .exec(); + + assertContainerHasVolumes(inspectContainerResponse1, volume1, volume2); + + CreateContainerResponse container2 = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999") + .withVolumesFrom(new VolumesFrom(container1Name)).exec(); + LOG.info("Created container2 {}", container2.toString()); + + dockerClient.startContainerCmd(container2.getId()).exec(); + LOG.info("Started container2 {}", container2.toString()); + + InspectContainerResponse inspectContainerResponse2 = dockerClient.inspectContainerCmd(container2.getId()) + .exec(); + + assertContainerHasVolumes(inspectContainerResponse2, volume1, volume2); + } + + @Test + public void startContainerWithDns() throws DockerException { + + String aDnsServer = "8.8.8.8"; + String anotherDnsServer = "8.8.4.4"; + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("true") + .withDns(aDnsServer, anotherDnsServer).exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container.getId()).exec(); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + + assertThat(Arrays.asList(inspectContainerResponse.getHostConfig().getDns()), + contains(aDnsServer, anotherDnsServer)); + } + + @Test + public void startContainerWithDnsSearch() throws DockerException { + + String dnsSearch = "example.com"; + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("true") + .withDnsSearch(dnsSearch).exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + + dockerClient.startContainerCmd(container.getId()).exec(); + + inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + + assertThat(Arrays.asList(inspectContainerResponse.getHostConfig().getDnsSearch()), contains(dnsSearch)); + } + + @Test + public void startContainerWithPortBindings() throws DockerException { + + ExposedPort tcp22 = ExposedPort.tcp(22); + ExposedPort tcp23 = ExposedPort.tcp(23); + + Ports portBindings = new Ports(); + portBindings.bind(tcp22, Ports.Binding(11022)); + portBindings.bind(tcp23, Ports.Binding(11023)); + portBindings.bind(tcp23, Ports.Binding(11024)); + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("true") + .withExposedPorts(tcp22, tcp23).withPortBindings(portBindings).exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + + dockerClient.startContainerCmd(container.getId()).exec(); + + inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + + assertThat(Arrays.asList(inspectContainerResponse.getConfig().getExposedPorts()), contains(tcp22, tcp23)); + + assertThat(inspectContainerResponse.getHostConfig().getPortBindings().getBindings().get(tcp22)[0], + is(equalTo(Ports.Binding(11022)))); + + assertThat(inspectContainerResponse.getHostConfig().getPortBindings().getBindings().get(tcp23)[0], + is(equalTo(Ports.Binding(11023)))); + + assertThat(inspectContainerResponse.getHostConfig().getPortBindings().getBindings().get(tcp23)[1], + is(equalTo(Ports.Binding(11024)))); + + } + + @Test + public void startContainerWithRandomPortBindings() throws DockerException { + + ExposedPort tcp22 = ExposedPort.tcp(22); + ExposedPort tcp23 = ExposedPort.tcp(23); + + Ports portBindings = new Ports(); + portBindings.bind(tcp22, Ports.Binding(null)); + portBindings.bind(tcp23, Ports.Binding(null)); + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999") + .withExposedPorts(tcp22, tcp23).withPortBindings(portBindings).withPublishAllPorts(true).exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container.getId()).exec(); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + + assertThat(Arrays.asList(inspectContainerResponse.getConfig().getExposedPorts()), contains(tcp22, tcp23)); + + assertThat(inspectContainerResponse.getNetworkSettings().getPorts().getBindings().get(tcp22)[0].getHostPort(), + is(not(equalTo(tcp22.getPort())))); + + assertThat(inspectContainerResponse.getNetworkSettings().getPorts().getBindings().get(tcp23)[0].getHostPort(), + is(not(equalTo(tcp23.getPort())))); + + } + + @Test + public void startContainerWithConflictingPortBindings() throws DockerException { + + ExposedPort tcp22 = ExposedPort.tcp(22); + ExposedPort tcp23 = ExposedPort.tcp(23); + + Ports portBindings = new Ports(); + portBindings.bind(tcp22, Ports.Binding(11022)); + portBindings.bind(tcp23, Ports.Binding(11022)); + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("true") + .withExposedPorts(tcp22, tcp23).withPortBindings(portBindings).exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + try { + dockerClient.startContainerCmd(container.getId()).exec(); + fail("expected InternalServerErrorException"); + } catch (InternalServerErrorException e) { + + } + + } + + @Test + public void startContainerWithLinkingDeprecated() throws DockerException { + + CreateContainerResponse container1 = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999") + .withName("container1").exec(); + + LOG.info("Created container1 {}", container1.toString()); + assertThat(container1.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container1.getId()).exec(); + + InspectContainerResponse inspectContainerResponse1 = dockerClient.inspectContainerCmd(container1.getId()) + .exec(); + LOG.info("Container1 Inspect: {}", inspectContainerResponse1.toString()); + + assertThat(inspectContainerResponse1.getConfig(), is(notNullValue())); + assertThat(inspectContainerResponse1.getId(), not(isEmptyString())); + assertThat(inspectContainerResponse1.getId(), startsWith(container1.getId())); + assertThat(inspectContainerResponse1.getName(), equalTo("/container1")); + assertThat(inspectContainerResponse1.getImageId(), not(isEmptyString())); + assertThat(inspectContainerResponse1.getState(), is(notNullValue())); + assertThat(inspectContainerResponse1.getState().isRunning(), is(true)); + + if (!inspectContainerResponse1.getState().isRunning()) { + assertThat(inspectContainerResponse1.getState().getExitCode(), is(equalTo(0))); + } + + CreateContainerResponse container2 = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999") + .withName("container2").withLinks(new Link("container1", "container1Link")).exec(); + + LOG.info("Created container2 {}", container2.toString()); + assertThat(container2.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container2.getId()).exec(); + + InspectContainerResponse inspectContainerResponse2 = dockerClient.inspectContainerCmd(container2.getId()) + .exec(); + LOG.info("Container2 Inspect: {}", inspectContainerResponse2.toString()); + + assertThat(inspectContainerResponse2.getConfig(), is(notNullValue())); + assertThat(inspectContainerResponse2.getId(), not(isEmptyString())); + assertThat(inspectContainerResponse2.getHostConfig(), is(notNullValue())); + assertThat(inspectContainerResponse2.getHostConfig().getLinks(), is(notNullValue())); + assertThat(inspectContainerResponse2.getHostConfig().getLinks(), equalTo(new Link[] { new Link("container1", + "container1Link") })); + assertThat(inspectContainerResponse2.getId(), startsWith(container2.getId())); + assertThat(inspectContainerResponse2.getName(), equalTo("/container2")); + assertThat(inspectContainerResponse2.getImageId(), not(isEmptyString())); + assertThat(inspectContainerResponse2.getState(), is(notNullValue())); + assertThat(inspectContainerResponse2.getState().isRunning(), is(true)); + + } + + @Test + public void startContainerWithLinking() throws DockerException { + + CreateContainerResponse container1 = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999") + .withName("container1").exec(); + + LOG.info("Created container1 {}", container1.toString()); + assertThat(container1.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container1.getId()).exec(); + + InspectContainerResponse inspectContainerResponse1 = dockerClient.inspectContainerCmd(container1.getId()) + .exec(); + LOG.info("Container1 Inspect: {}", inspectContainerResponse1.toString()); + + assertThat(inspectContainerResponse1.getConfig(), is(notNullValue())); + assertThat(inspectContainerResponse1.getId(), not(isEmptyString())); + assertThat(inspectContainerResponse1.getId(), startsWith(container1.getId())); + assertThat(inspectContainerResponse1.getName(), equalTo("/container1")); + assertThat(inspectContainerResponse1.getImageId(), not(isEmptyString())); + assertThat(inspectContainerResponse1.getState(), is(notNullValue())); + assertThat(inspectContainerResponse1.getState().isRunning(), is(true)); + + if (!inspectContainerResponse1.getState().isRunning()) { + assertThat(inspectContainerResponse1.getState().getExitCode(), is(equalTo(0))); + } + + CreateContainerResponse container2 = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999") + .withName("container2").withLinks(new Link("container1", "container1Link")).exec(); + + LOG.info("Created container2 {}", container2.toString()); + assertThat(container2.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container2.getId()).exec(); + + InspectContainerResponse inspectContainerResponse2 = dockerClient.inspectContainerCmd(container2.getId()) + .exec(); + LOG.info("Container2 Inspect: {}", inspectContainerResponse2.toString()); + + assertThat(inspectContainerResponse2.getConfig(), is(notNullValue())); + assertThat(inspectContainerResponse2.getId(), not(isEmptyString())); + assertThat(inspectContainerResponse2.getHostConfig(), is(notNullValue())); + assertThat(inspectContainerResponse2.getHostConfig().getLinks(), is(notNullValue())); + assertThat(inspectContainerResponse2.getHostConfig().getLinks(), equalTo(new Link[] { new Link("container1", + "container1Link") })); + assertThat(inspectContainerResponse2.getId(), startsWith(container2.getId())); + assertThat(inspectContainerResponse2.getName(), equalTo("/container2")); + assertThat(inspectContainerResponse2.getImageId(), not(isEmptyString())); + assertThat(inspectContainerResponse2.getState(), is(notNullValue())); + assertThat(inspectContainerResponse2.getState().isRunning(), is(true)); + + } + + @Test + public void startContainer() throws DockerException { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd(new String[] { "top" }) + .exec(); + + LOG.info("Created container {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container.getId()).exec(); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + LOG.info("Container Inspect: {}", inspectContainerResponse.toString()); + + assertThat(inspectContainerResponse.getConfig(), is(notNullValue())); + assertThat(inspectContainerResponse.getId(), not(isEmptyString())); + + assertThat(inspectContainerResponse.getId(), startsWith(container.getId())); + + assertThat(inspectContainerResponse.getImageId(), not(isEmptyString())); + assertThat(inspectContainerResponse.getState(), is(notNullValue())); + + assertThat(inspectContainerResponse.getState().isRunning(), is(true)); + + if (!inspectContainerResponse.getState().isRunning()) { + assertThat(inspectContainerResponse.getState().getExitCode(), is(equalTo(0))); + } + } + + @Test + public void testStartNonExistingContainer() throws DockerException { + try { + dockerClient.startContainerCmd("non-existing").exec(); + fail("expected NotFoundException"); + } catch (NotFoundException e) { + } + } + + /** + * This tests support for --net option for the docker run command: --net="bridge" Set the Network mode for the + * container 'bridge': creates a new network stack for the container on the docker bridge 'none': no networking for + * this container 'container:': reuses another container network stack 'host': use the host network stack inside the + * container. Note: the host mode gives the container full access to local system services such as D-bus and is + * therefore considered insecure. + */ + @Test + public void startContainerWithNetworkMode() throws DockerException { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("true") + .withNetworkMode("host").exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + + dockerClient.startContainerCmd(container.getId()).exec(); + + inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + + assertThat(inspectContainerResponse.getHostConfig().getNetworkMode(), is(equalTo("host"))); + } + + @Test + public void startContainerWithCapAddAndCapDrop() throws DockerException { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999") + .withCapAdd(NET_ADMIN).withCapDrop(MKNOD).exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container.getId()).exec(); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + + assertThat(inspectContainerResponse.getState().isRunning(), is(true)); + + assertThat(Arrays.asList(inspectContainerResponse.getHostConfig().getCapAdd()), contains(NET_ADMIN)); + + assertThat(Arrays.asList(inspectContainerResponse.getHostConfig().getCapDrop()), contains(MKNOD)); + } + + @Test + public void startContainerWithDevices() throws DockerException { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999") + .withDevices(new Device("rwm", "/dev/nulo", "/dev/zero")).exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container.getId()).exec(); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + + assertThat(inspectContainerResponse.getState().isRunning(), is(true)); + + assertThat(Arrays.asList(inspectContainerResponse.getHostConfig().getDevices()), contains(new Device("rwm", + "/dev/nulo", "/dev/zero"))); + } + + @Test + public void startContainerWithExtraHosts() throws DockerException { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999") + .withExtraHosts("dockerhost:127.0.0.1").exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container.getId()).exec(); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + + assertThat(inspectContainerResponse.getState().isRunning(), is(true)); + + assertThat(Arrays.asList(inspectContainerResponse.getHostConfig().getExtraHosts()), + contains("dockerhost:127.0.0.1")); + } + + @Test + public void startContainerWithRestartPolicy() throws DockerException { + + RestartPolicy restartPolicy = RestartPolicy.onFailureRestart(5); + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999") + .withRestartPolicy(restartPolicy).exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container.getId()).exec(); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + + assertThat(inspectContainerResponse.getState().isRunning(), is(true)); + + assertThat(inspectContainerResponse.getHostConfig().getRestartPolicy(), is(equalTo(restartPolicy))); + } + + @Test + public void existingHostConfigIsPreservedByBlankStartCmd() throws DockerException { + + String dnsServer = "8.8.8.8"; + + // prepare a container with custom DNS + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withDns(dnsServer) + .withCmd("true").exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + // start container _without_any_customization_ (important!) + dockerClient.startContainerCmd(container.getId()).exec(); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + + // The DNS setting survived. + assertThat(inspectContainerResponse.getHostConfig().getDns(), is(notNullValue())); + assertThat(Arrays.asList(inspectContainerResponse.getHostConfig().getDns()), contains(dnsServer)); + } + + @Test + public void anUnconfiguredCommandSerializesToEmptyJson() throws Exception { + ObjectMapper objectMapper = new ObjectMapper(); + StartContainerCmd command = dockerClient.startContainerCmd(""); + assertThat(objectMapper.writeValueAsString(command), is("{}")); + } +} diff --git a/src/test/java/com/github/dockerjava/netty/exec/StatsCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/StatsCmdExecTest.java new file mode 100644 index 000000000..9b3e0a377 --- /dev/null +++ b/src/test/java/com/github/dockerjava/netty/exec/StatsCmdExecTest.java @@ -0,0 +1,106 @@ +package com.github.dockerjava.netty.exec; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.Matchers.not; + +import java.io.IOException; +import java.lang.reflect.Method; +import java.security.SecureRandom; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.model.Statistics; +import com.github.dockerjava.core.async.ResultCallbackTemplate; +import com.github.dockerjava.netty.AbstractDockerClientTest; + +@Test(groups = "integration") +public class StatsCmdExecTest extends AbstractDockerClientTest { + + private static int NUM_STATS = 5; + + @BeforeTest + public void beforeTest() throws Exception { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test(groups = "ignoreInCircleCi") + public void testStatsStreaming() throws InterruptedException, IOException { + TimeUnit.SECONDS.sleep(1); + + CountDownLatch countDownLatch = new CountDownLatch(NUM_STATS); + + String containerName = "generated_" + new SecureRandom().nextInt(); + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("top") + .withName(containerName).exec(); + LOG.info("Created container {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container.getId()).exec(); + + StatsCallbackTest statsCallback = dockerClient.statsCmd().withContainerId(container.getId()) + .exec(new StatsCallbackTest(countDownLatch)); + + countDownLatch.await(3, TimeUnit.SECONDS); + Boolean gotStats = statsCallback.gotStats(); + + LOG.info("Stop stats collection"); + + statsCallback.close(); + + LOG.info("Stopping container"); + dockerClient.stopContainerCmd(container.getId()).exec(); + dockerClient.removeContainerCmd(container.getId()).exec(); + + LOG.info("Completed test"); + assertTrue(gotStats, "Expected true"); + + } + + private class StatsCallbackTest extends ResultCallbackTemplate { + private final CountDownLatch countDownLatch; + + private Boolean gotStats = false; + + public StatsCallbackTest(CountDownLatch countDownLatch) { + this.countDownLatch = countDownLatch; + } + + @Override + public void onNext(Statistics stats) { + LOG.info("Received stats #{}: {}", countDownLatch.getCount(), stats); + if (stats != null) { + gotStats = true; + } + countDownLatch.countDown(); + } + + public Boolean gotStats() { + return gotStats; + } + } +} diff --git a/src/test/java/com/github/dockerjava/netty/exec/TagImageCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/TagImageCmdExecTest.java new file mode 100644 index 000000000..edfc1b657 --- /dev/null +++ b/src/test/java/com/github/dockerjava/netty/exec/TagImageCmdExecTest.java @@ -0,0 +1,63 @@ +package com.github.dockerjava.netty.exec; + +import java.lang.reflect.Method; + +import org.apache.commons.lang.math.RandomUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.netty.AbstractDockerClientTest; + +@Test(groups = "integration") +public class TagImageCmdExecTest extends AbstractDockerClientTest { + + public static final Logger LOG = LoggerFactory.getLogger(TagImageCmdExecTest.class); + + @BeforeTest + public void beforeTest() throws Exception { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test + public void tagImage() throws Exception { + String tag = "" + RandomUtils.nextInt(Integer.MAX_VALUE); + + dockerClient.tagImageCmd("busybox:latest", "docker-java/busybox", tag).exec(); + + dockerClient.removeImageCmd("docker-java/busybox:" + tag).exec(); + } + + @Test + public void tagNonExistingImage() throws Exception { + String tag = "" + RandomUtils.nextInt(Integer.MAX_VALUE); + + try { + dockerClient.tagImageCmd("non-existing", "docker-java/busybox", tag).exec(); + fail("expected NotFoundException"); + } catch (NotFoundException e) { + } + } + +} diff --git a/src/test/java/com/github/dockerjava/netty/exec/VersionCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/VersionCmdExecTest.java new file mode 100644 index 000000000..78ac5ee27 --- /dev/null +++ b/src/test/java/com/github/dockerjava/netty/exec/VersionCmdExecTest.java @@ -0,0 +1,52 @@ +package com.github.dockerjava.netty.exec; + +import java.lang.reflect.Method; + +import org.apache.commons.lang.StringUtils; +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.model.Version; +import com.github.dockerjava.netty.AbstractDockerClientTest; + +@Test(groups = "integration") +public class VersionCmdExecTest extends AbstractDockerClientTest { + + @BeforeTest + public void beforeTest() throws Exception { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test + public void version() throws DockerException { + Version version = dockerClient.versionCmd().exec(); + LOG.info(version.toString()); + + assertTrue(version.getGoVersion().length() > 0); + assertTrue(version.getVersion().length() > 0); + + assertEquals(StringUtils.split(version.getVersion(), ".").length, 3); + + } + +} From a1c7bf594e781219aa3da19055f11c655b701d4a Mon Sep 17 00:00:00 2001 From: Marcus Linke Date: Thu, 10 Dec 2015 22:13:16 +0100 Subject: [PATCH 09/13] Added copy archive from/to container command for (netty) --- .../exec/CopyArchiveFromContainerCmdExec.java | 32 +++++++ .../exec/CopyArchiveToContainerCmdExec.java | 36 +++++++ .../CopyArchiveFromContainerCmdExecTest.java | 73 ++++++++++++++ .../CopyArchiveToContainerCmdExecTest.java | 94 +++++++++++++++++++ 4 files changed, 235 insertions(+) create mode 100644 src/main/java/com/github/dockerjava/netty/exec/CopyArchiveFromContainerCmdExec.java create mode 100644 src/main/java/com/github/dockerjava/netty/exec/CopyArchiveToContainerCmdExec.java create mode 100644 src/test/java/com/github/dockerjava/netty/exec/CopyArchiveFromContainerCmdExecTest.java create mode 100644 src/test/java/com/github/dockerjava/netty/exec/CopyArchiveToContainerCmdExecTest.java diff --git a/src/main/java/com/github/dockerjava/netty/exec/CopyArchiveFromContainerCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/CopyArchiveFromContainerCmdExec.java new file mode 100644 index 000000000..6f4a93ba3 --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/exec/CopyArchiveFromContainerCmdExec.java @@ -0,0 +1,32 @@ +package com.github.dockerjava.netty.exec; + +import java.io.InputStream; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.command.CopyArchiveFromContainerCmd; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.netty.MediaType; +import com.github.dockerjava.netty.WebTarget; + +public class CopyArchiveFromContainerCmdExec extends AbstrSyncDockerCmdExec + implements CopyArchiveFromContainerCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(CopyArchiveFromContainerCmdExec.class); + + public CopyArchiveFromContainerCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + protected InputStream execute(CopyArchiveFromContainerCmd command) { + WebTarget webResource = getBaseResource().path("/containers/{id}/archive").resolveTemplate("id", + command.getContainerId()); + + LOGGER.trace("Get: " + webResource.toString()); + + return webResource.queryParam("path", command.getResource()).request().accept(MediaType.APPLICATION_X_TAR) + .get(); + } +} diff --git a/src/main/java/com/github/dockerjava/netty/exec/CopyArchiveToContainerCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/CopyArchiveToContainerCmdExec.java new file mode 100644 index 000000000..c956a815d --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/exec/CopyArchiveToContainerCmdExec.java @@ -0,0 +1,36 @@ +package com.github.dockerjava.netty.exec; + +import java.io.InputStream; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.command.CopyArchiveToContainerCmd; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.netty.MediaType; +import com.github.dockerjava.netty.WebTarget; + +public class CopyArchiveToContainerCmdExec extends AbstrSyncDockerCmdExec implements + CopyArchiveToContainerCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(CopyArchiveFromContainerCmdExec.class); + + public CopyArchiveToContainerCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + protected Void execute(CopyArchiveToContainerCmd command) { + WebTarget webResource = getBaseResource().path("/containers/{id}/archive").resolveTemplate("id", + command.getContainerId()); + + LOGGER.trace("PUT: " + webResource.toString()); + InputStream streamToUpload = command.getTarInputStream(); + + webResource.queryParam("path", command.getRemotePath()) + .queryParam("noOverwriteDirNonDir", command.isNoOverwriteDirNonDir()).request() + .put(streamToUpload, MediaType.APPLICATION_X_TAR); + + return null; + } +} diff --git a/src/test/java/com/github/dockerjava/netty/exec/CopyArchiveFromContainerCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/CopyArchiveFromContainerCmdExecTest.java new file mode 100644 index 000000000..aa4dcc575 --- /dev/null +++ b/src/test/java/com/github/dockerjava/netty/exec/CopyArchiveFromContainerCmdExecTest.java @@ -0,0 +1,73 @@ +package com.github.dockerjava.netty.exec; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.isEmptyOrNullString; +import static org.hamcrest.Matchers.not; + +import java.io.InputStream; +import java.lang.reflect.Method; + +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.exception.NotFoundException; +import com.github.dockerjava.netty.AbstractNettyDockerClientTest; + +@Test(groups = "integration") +public class CopyArchiveFromContainerCmdExecTest extends AbstractNettyDockerClientTest { + + @BeforeTest + public void beforeTest() throws Exception { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test + public void copyFromContainer() throws Exception { + // TODO extract this into a shared method + CreateContainerResponse container = dockerClient.createContainerCmd("busybox") + .withName("docker-java-itest-copyFromContainer").withCmd("touch", "/copyFromContainer").exec(); + + LOG.info("Created container: {}", container); + assertThat(container.getId(), not(isEmptyOrNullString())); + + dockerClient.startContainerCmd(container.getId()).exec(); + + InputStream response = dockerClient.copyArchiveFromContainerCmd(container.getId(), "/copyFromContainer").exec(); + Boolean bytesAvailable = response.available() > 0; + assertTrue(bytesAvailable, "The file was not copied from the container."); + + // read the stream fully. Otherwise, the underlying stream will not be closed. + String responseAsString = asString(response); + assertNotNull(responseAsString); + assertTrue(responseAsString.length() > 0); + } + + @Test + public void copyFromNonExistingContainer() throws Exception { + try { + dockerClient.copyArchiveFromContainerCmd("non-existing", "/test").exec(); + fail("expected NotFoundException"); + } catch (NotFoundException ignored) { + } + } +} diff --git a/src/test/java/com/github/dockerjava/netty/exec/CopyArchiveToContainerCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/CopyArchiveToContainerCmdExecTest.java new file mode 100644 index 000000000..d755dfcba --- /dev/null +++ b/src/test/java/com/github/dockerjava/netty/exec/CopyArchiveToContainerCmdExecTest.java @@ -0,0 +1,94 @@ +package com.github.dockerjava.netty.exec; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.isEmptyOrNullString; +import static org.hamcrest.Matchers.not; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Method; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.exception.NotFoundException; +import com.github.dockerjava.core.util.CompressArchiveUtil; +import com.github.dockerjava.netty.AbstractNettyDockerClientTest; + +@Test(groups = "integration") +public class CopyArchiveToContainerCmdExecTest extends AbstractNettyDockerClientTest { + @BeforeTest + public void beforeTest() throws Exception { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test + public void copyFileToContainer() throws Exception { + CreateContainerResponse container = prepareContainerForCopy(); + Path temp = Files.createTempFile("", ".tar.gz"); + CompressArchiveUtil.tar(Paths.get("src/test/resources/testReadFile"), temp, true, false); + try (InputStream uploadStream = Files.newInputStream(temp)) { + dockerClient.copyArchiveToContainerCmd(container.getId()).withTarInputStream(uploadStream).exec(); + assertFileCopied(container); + } + } + + @Test + public void copyStreamToContainer() throws Exception { + CreateContainerResponse container = prepareContainerForCopy(); + dockerClient.copyArchiveToContainerCmd(container.getId()).withHostResource("src/test/resources/testReadFile") + .exec(); + assertFileCopied(container); + } + + private CreateContainerResponse prepareContainerForCopy() { + CreateContainerResponse container = dockerClient.createContainerCmd("busybox") + .withName("docker-java-itest-copyToContainer").exec(); + LOG.info("Created container: {}", container); + assertThat(container.getId(), not(isEmptyOrNullString())); + dockerClient.startContainerCmd(container.getId()).exec(); + // Copy a folder to the container + return container; + } + + private void assertFileCopied(CreateContainerResponse container) throws IOException { + try (InputStream response = dockerClient.copyArchiveFromContainerCmd(container.getId(), "testReadFile").exec()) { + boolean bytesAvailable = response.available() > 0; + assertTrue(bytesAvailable, "The file was not copied to the container."); + } + } + + @Test + public void copyToNonExistingContainer() throws Exception { + try { + dockerClient.copyArchiveToContainerCmd("non-existing").withHostResource("src/test/resources/testReadFile") + .exec(); + fail("expected NotFoundException"); + } catch (NotFoundException ignored) { + } + } + +} From ca376d6862d160df16e17b48e37f8a6246e8779b Mon Sep 17 00:00:00 2001 From: Marcus Linke Date: Fri, 11 Dec 2015 22:18:21 +0100 Subject: [PATCH 10/13] Added stdin parameter to attach command api, implemented for netty --- .../api/command/AttachContainerCmd.java | 5 +++ .../core/async/ResultCallbackTemplate.java | 1 - .../core/command/AttachContainerCmdImpl.java | 18 +++++++++ .../jaxrs/AttachContainerCmdExec.java | 5 ++- .../dockerjava/netty/InvocationBuilder.java | 22 +++++------ .../netty/exec/AttachContainerCmdExec.java | 4 +- .../netty/exec/RemoveContainerCmdExec.java | 4 +- .../netty/exec/RemoveImageCmdExec.java | 2 +- .../command/AttachContainerCmdImplTest.java | 29 +++++++++++++++ .../exec/AttachContainerCmdExecTest.java | 37 +++++++++++++++++++ 10 files changed, 109 insertions(+), 18 deletions(-) diff --git a/src/main/java/com/github/dockerjava/api/command/AttachContainerCmd.java b/src/main/java/com/github/dockerjava/api/command/AttachContainerCmd.java index 13979fb8b..6326fa13b 100644 --- a/src/main/java/com/github/dockerjava/api/command/AttachContainerCmd.java +++ b/src/main/java/com/github/dockerjava/api/command/AttachContainerCmd.java @@ -43,6 +43,9 @@ public interface AttachContainerCmd extends AsyncDockerCmd { diff --git a/src/main/java/com/github/dockerjava/core/async/ResultCallbackTemplate.java b/src/main/java/com/github/dockerjava/core/async/ResultCallbackTemplate.java index 4db4edcea..44af625af 100644 --- a/src/main/java/com/github/dockerjava/core/async/ResultCallbackTemplate.java +++ b/src/main/java/com/github/dockerjava/core/async/ResultCallbackTemplate.java @@ -63,7 +63,6 @@ public void onError(Throwable throwable) { @Override public void onComplete() { - System.err.println("callback onComplete"); try { close(); } catch (IOException e) { diff --git a/src/main/java/com/github/dockerjava/core/command/AttachContainerCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/AttachContainerCmdImpl.java index 629990ff1..89e27c8ea 100644 --- a/src/main/java/com/github/dockerjava/core/command/AttachContainerCmdImpl.java +++ b/src/main/java/com/github/dockerjava/core/command/AttachContainerCmdImpl.java @@ -2,6 +2,8 @@ import static com.google.common.base.Preconditions.checkNotNull; +import java.io.InputStream; + import com.github.dockerjava.api.command.AttachContainerCmd; import com.github.dockerjava.api.model.Frame; @@ -17,6 +19,8 @@ * - true or false, includes stdout log. Defaults to false. * @param stderr * - true or false, includes stderr log. Defaults to false. + * @param stdin + * - null or {@link InputStream}, pass stream to stdin of the container. * @param timestamps * - true or false, if true, print timestamps for every log line. Defaults to false. */ @@ -27,6 +31,8 @@ public class AttachContainerCmdImpl extends AbstrAsyncDockerCmd callbackNotifier(AttachContainerCmd command, ResultCallback resultCallback) { + if (command.getStdin() != null) + throw new UnsupportedOperationException( + "Passing stdin to the container is currently not supported. Try experimental netty engine!"); + WebTarget webTarget = getBaseResource().path("/containers/{id}/attach").resolveTemplate("id", command.getContainerId()); webTarget = booleanQueryParam(webTarget, "logs", command.hasLogsEnabled()); webTarget = booleanQueryParam(webTarget, "stdout", command.hasStdoutEnabled()); - // webTarget = booleanQueryParam(webTarget, "stdin", command.hasStdinEnabled()); webTarget = booleanQueryParam(webTarget, "stderr", command.hasStderrEnabled()); webTarget = booleanQueryParam(webTarget, "stream", command.hasFollowStreamEnabled()); diff --git a/src/main/java/com/github/dockerjava/netty/InvocationBuilder.java b/src/main/java/com/github/dockerjava/netty/InvocationBuilder.java index c49328c2a..8b6688ca6 100644 --- a/src/main/java/com/github/dockerjava/netty/InvocationBuilder.java +++ b/src/main/java/com/github/dockerjava/netty/InvocationBuilder.java @@ -86,7 +86,7 @@ public InvocationBuilder header(String name, String value) { return this; } - public ResponseCallback delete() { + public void delete() { HttpRequestProvider requestProvider = httpDeleteRequestProvider(); @@ -100,7 +100,7 @@ public ResponseCallback delete() { sendRequest(requestProvider, channel); - return callback; + callback.awaitResult(); } public void get(ResultCallback resultCallback) { @@ -134,7 +134,7 @@ public void get(TypeReference typeReference, ResultCallback resultCall Channel channel = getChannel(); - resultCallbackOnStart(channel, resultCallback); + initCallback(channel, resultCallback); JsonResponseCallbackHandler jsonResponseHandler = new JsonResponseCallbackHandler(typeReference, resultCallback); @@ -276,7 +276,7 @@ public void post(final Object entity, TypeReference typeReference, final Channel channel = getChannel(); - resultCallbackOnStart(channel, resultCallback); + initCallback(channel, resultCallback); JsonResponseCallbackHandler jsonResponseHandler = new JsonResponseCallbackHandler(typeReference, resultCallback); @@ -292,12 +292,11 @@ public void post(final Object entity, TypeReference typeReference, final return; } - private void resultCallbackOnStart(final Channel channel, final ResultCallback resultCallback) { + private void initCallback(final Channel channel, final ResultCallback resultCallback) { Closeable closeable = new Closeable() { @Override public void close() throws IOException { try { - System.err.println("closing channel"); channel.close().sync(); } catch (InterruptedException e) { resultCallback.onError(e); @@ -327,14 +326,14 @@ private FullHttpRequest prepareGetRequest(String uri) { } private HttpRequest preparePostRequest(String uri, Object entity) { - return prepareRequest(uri, entity, HttpMethod.POST); + return prepareEntityRequest(uri, entity, HttpMethod.POST); } private HttpRequest preparePutRequest(String uri, Object entity) { - return prepareRequest(uri, entity, HttpMethod.PUT); + return prepareEntityRequest(uri, entity, HttpMethod.PUT); } - private HttpRequest prepareRequest(String uri, Object entity, HttpMethod httpMethod) { + private HttpRequest prepareEntityRequest(String uri, Object entity, HttpMethod httpMethod) { HttpRequest request = null; @@ -404,7 +403,7 @@ public void post(TypeReference typeReference, ResultCallback resultCal Channel channel = getChannel(); - resultCallbackOnStart(channel, resultCallback); + initCallback(channel, resultCallback); JsonResponseCallbackHandler jsonResponseHandler = new JsonResponseCallbackHandler(typeReference, resultCallback); @@ -440,7 +439,7 @@ public InputStream get() { ResponseCallback resultCallback = new ResponseCallback(); - resultCallbackOnStart(channel, resultCallback); + initCallback(channel, resultCallback); HttpResponseHandler responseHandler = new HttpResponseHandler(requestProvider, resultCallback); @@ -482,6 +481,5 @@ public void put(InputStream body, MediaType mediaType) { channel.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT); resultCallback.awaitResult(); - }; } diff --git a/src/main/java/com/github/dockerjava/netty/exec/AttachContainerCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/AttachContainerCmdExec.java index 76cec4272..2fdace48a 100644 --- a/src/main/java/com/github/dockerjava/netty/exec/AttachContainerCmdExec.java +++ b/src/main/java/com/github/dockerjava/netty/exec/AttachContainerCmdExec.java @@ -26,13 +26,13 @@ protected Void execute0(AttachContainerCmd command, ResultCallback result webTarget = booleanQueryParam(webTarget, "logs", command.hasLogsEnabled()); webTarget = booleanQueryParam(webTarget, "stdout", command.hasStdoutEnabled()); - // webTarget = booleanQueryParam(webTarget, "stdin", command.hasStdinEnabled()); webTarget = booleanQueryParam(webTarget, "stderr", command.hasStderrEnabled()); + webTarget = booleanQueryParam(webTarget, "stdin", command.getStdin() != null); webTarget = booleanQueryParam(webTarget, "stream", command.hasFollowStreamEnabled()); LOGGER.trace("POST: {}", webTarget); - webTarget.request().post(null, System.in, resultCallback); + webTarget.request().post(null, command.getStdin(), resultCallback); return null; } diff --git a/src/main/java/com/github/dockerjava/netty/exec/RemoveContainerCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/RemoveContainerCmdExec.java index bf3a3a56b..63d16bda6 100644 --- a/src/main/java/com/github/dockerjava/netty/exec/RemoveContainerCmdExec.java +++ b/src/main/java/com/github/dockerjava/netty/exec/RemoveContainerCmdExec.java @@ -25,7 +25,9 @@ protected Void execute(RemoveContainerCmd command) { webTarget = booleanQueryParam(webTarget, "force", command.hasForceEnabled()); LOGGER.trace("DELETE: {}", webTarget); - return webTarget.request().accept(MediaType.APPLICATION_JSON).delete().awaitResult(); + webTarget.request().accept(MediaType.APPLICATION_JSON).delete(); + + return null; } } diff --git a/src/main/java/com/github/dockerjava/netty/exec/RemoveImageCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/RemoveImageCmdExec.java index d4e0e212a..35ae053d3 100644 --- a/src/main/java/com/github/dockerjava/netty/exec/RemoveImageCmdExec.java +++ b/src/main/java/com/github/dockerjava/netty/exec/RemoveImageCmdExec.java @@ -23,7 +23,7 @@ protected Void execute(RemoveImageCmd command) { webTarget = booleanQueryParam(webTarget, "noprune", command.hasNoPruneEnabled()); LOGGER.trace("DELETE: {}", webTarget); - webTarget.request().delete().awaitResult(); + webTarget.request().delete(); return null; } diff --git a/src/test/java/com/github/dockerjava/core/command/AttachContainerCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/AttachContainerCmdImplTest.java index ead8ad39e..be2d5fdd0 100644 --- a/src/test/java/com/github/dockerjava/core/command/AttachContainerCmdImplTest.java +++ b/src/test/java/com/github/dockerjava/core/command/AttachContainerCmdImplTest.java @@ -5,7 +5,9 @@ import static org.hamcrest.Matchers.isEmptyString; import static org.hamcrest.Matchers.not; +import java.io.ByteArrayInputStream; import java.io.File; +import java.io.InputStream; import java.lang.reflect.Method; import java.util.concurrent.TimeUnit; @@ -104,6 +106,33 @@ public void onNext(Frame frame) { assertThat(callback.toString(), containsString("stdout\r\nstderr")); } + @Test(expectedExceptions = UnsupportedOperationException.class) + public void attachContainerStdinUnsupported() throws Exception { + + String snippet = "hello world"; + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("echo", snippet) + .withTty(false).exec(); + + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container.getId()).exec(); + + AttachContainerTestCallback callback = new AttachContainerTestCallback() { + @Override + public void onNext(Frame frame) { + assertEquals(frame.getStreamType(), StreamType.STDOUT); + super.onNext(frame); + }; + }; + + InputStream stdin = new ByteArrayInputStream("".getBytes()); + + dockerClient.attachContainerCmd(container.getId()).withStdErr(true).withStdOut(true).withFollowStream(true) + .withLogs(true).withStdIn(stdin).exec(callback).awaitCompletion(30, TimeUnit.SECONDS).close(); + } + public static class AttachContainerTestCallback extends AttachContainerResultCallback { private StringBuffer log = new StringBuffer(); diff --git a/src/test/java/com/github/dockerjava/netty/exec/AttachContainerCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/AttachContainerCmdExecTest.java index 06f33d00e..08daa7e6f 100644 --- a/src/test/java/com/github/dockerjava/netty/exec/AttachContainerCmdExecTest.java +++ b/src/test/java/com/github/dockerjava/netty/exec/AttachContainerCmdExecTest.java @@ -5,7 +5,9 @@ import static org.hamcrest.Matchers.isEmptyString; import static org.hamcrest.Matchers.not; +import java.io.ByteArrayInputStream; import java.io.File; +import java.io.InputStream; import java.lang.reflect.Method; import java.util.concurrent.TimeUnit; @@ -17,6 +19,7 @@ import org.testng.annotations.Test; import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.command.InspectContainerResponse; import com.github.dockerjava.api.model.Frame; import com.github.dockerjava.api.model.StreamType; import com.github.dockerjava.core.command.AttachContainerResultCallback; @@ -72,6 +75,40 @@ public void onNext(Frame frame) { assertThat(callback.toString(), containsString(snippet)); } + @Test + public void attachContainerWithStdin() throws Exception { + + String snippet = "hello world"; + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("/bin/sh", "-c", "read line && echo $line") + .withTty(false).withStdinOpen(true).exec(); + + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container.getId()).exec(); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + + assertTrue(inspectContainerResponse.getState().getRunning()); + + AttachContainerTestCallback callback = new AttachContainerTestCallback() { + + @Override + public void onNext(Frame frame) { + assertEquals(frame.getStreamType(), StreamType.STDOUT); + super.onNext(frame); + }; + }; + + InputStream stdin = new ByteArrayInputStream((snippet + "\n").getBytes()); + + dockerClient.attachContainerCmd(container.getId()).withStdErr(true).withStdOut(true).withFollowStream(true) + .withStdIn(stdin).exec(callback).awaitCompletion(2, TimeUnit.SECONDS).close(); + + assertThat(callback.toString(), containsString(snippet)); + } + @Test public void attachContainerWithTTY() throws Exception { From feb93a41658067105a60a9b5f05a923e80be14ca Mon Sep 17 00:00:00 2001 From: Marcus Linke Date: Fri, 11 Dec 2015 22:20:58 +0100 Subject: [PATCH 11/13] Removed println() debugging --- .../java/com/github/dockerjava/core/command/FrameReader.java | 4 +--- .../dockerjava/netty/exec/AttachContainerCmdExecTest.java | 2 -- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/java/com/github/dockerjava/core/command/FrameReader.java b/src/main/java/com/github/dockerjava/core/command/FrameReader.java index ff1a9d7ea..0e885082a 100644 --- a/src/main/java/com/github/dockerjava/core/command/FrameReader.java +++ b/src/main/java/com/github/dockerjava/core/command/FrameReader.java @@ -67,7 +67,7 @@ public Frame readFrame() throws IOException { } while (actualHeaderSize < HEADER_SIZE); // HexDump.dump(header, 0, System.err, 0); - + StreamType streamType = streamType(header[0]); if (streamType.equals(StreamType.RAW)) { @@ -77,8 +77,6 @@ public Frame readFrame() throws IOException { int payloadSize = ((header[4] & 0xff) << 24) + ((header[5] & 0xff) << 16) + ((header[6] & 0xff) << 8) + (header[7] & 0xff); - - //System.out.println("payload size: " + payloadSize); byte[] payload = new byte[payloadSize]; int actualPayloadSize = 0; diff --git a/src/test/java/com/github/dockerjava/netty/exec/AttachContainerCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/AttachContainerCmdExecTest.java index 08daa7e6f..fe34aa537 100644 --- a/src/test/java/com/github/dockerjava/netty/exec/AttachContainerCmdExecTest.java +++ b/src/test/java/com/github/dockerjava/netty/exec/AttachContainerCmdExecTest.java @@ -135,8 +135,6 @@ public void onNext(Frame frame) { dockerClient.attachContainerCmd(container.getId()).withStdErr(true).withStdOut(true).withFollowStream(true) .exec(callback).awaitCompletion(10, TimeUnit.SECONDS).close(); - System.out.println("log: " + callback.toString()); - // HexDump.dump(collectFramesCallback.toString().getBytes(), 0, System.out, 0); assertThat(callback.toString(), containsString("stdout\r\nstderr")); From 8039e18dfb12d9c1412d7e90c1c47d96ac4c28dd Mon Sep 17 00:00:00 2001 From: Marcus Linke Date: Sat, 12 Dec 2015 20:13:53 +0100 Subject: [PATCH 12/13] Code cleanup --- .../api/command/AsyncDockerCmd.java | 2 +- .../api/command/TopContainerResponse.java | 2 +- .../dockerjava/api/model/RestartPolicy.java | 2 +- .../dockerjava/core/DockerClientConfig.java | 3 +- .../core/async/FrameStreamProcessor.java | 2 +- .../core/async/JsonStreamProcessor.java | 2 +- .../core/async/ResponseStreamProcessor.java | 2 +- .../core/async/ResultCallbackTemplate.java | 2 +- .../AttachContainerResultCallback.java | 2 +- .../command/BuildImageResultCallback.java | 2 +- .../core/command/EventsResultCallback.java | 2 +- .../core/command/ExecStartResultCallback.java | 6 +- .../command/LogContainerResultCallback.java | 2 +- .../core/command/PullImageResultCallback.java | 2 +- .../core/command/PushImageResultCallback.java | 2 +- .../command/WaitContainerResultCallback.java | 2 +- .../jaxrs/async/GETCallbackNotifier.java | 2 +- .../jaxrs/async/POSTCallbackNotifier.java | 2 +- .../ApacheConnectorClientResponse.java | 2 +- .../filter/ResponseStatusExceptionFilter.java | 2 +- .../util/WrappedResponseInputStream.java | 2 +- .../dockerjava/netty/ChannelProvider.java | 4 +- .../netty/DockerCmdExecFactoryImpl.java | 6 +- .../dockerjava/netty/InvocationBuilder.java | 6 +- .../netty/exec/ContainerDiffCmdExec.java | 2 +- .../dockerjava/netty/exec/InfoCmdExec.java | 7 +- .../netty/exec/InspectContainerCmdExec.java | 2 +- .../netty/exec/InspectExecCmdExec.java | 2 +- .../netty/exec/InspectImageCmdExec.java | 2 +- .../netty/exec/ListContainersCmdExec.java | 2 +- .../netty/exec/ListImagesCmdExec.java | 6 +- .../netty/exec/LogContainerCmdExec.java | 1 - .../netty/exec/SearchImagesCmdExec.java | 2 +- .../netty/exec/TopContainerCmdExec.java | 2 +- .../dockerjava/netty/exec/VersionCmdExec.java | 2 +- .../handler/FramedResponseStreamHandler.java | 16 +- .../handler/HttpConnectionHijackHandler.java | 2 +- .../netty/handler/HttpRequestProvider.java | 3 +- .../handler/HttpResponseStreamHandler.java | 14 +- .../dockerjava/core/NameParserTest.java | 2 +- .../core/TestDockerCmdExecFactory.java | 2 +- .../netty/AbstractNettyDockerClientTest.java | 209 ------------------ 42 files changed, 64 insertions(+), 277 deletions(-) diff --git a/src/main/java/com/github/dockerjava/api/command/AsyncDockerCmd.java b/src/main/java/com/github/dockerjava/api/command/AsyncDockerCmd.java index 78ee978fa..221d3d165 100644 --- a/src/main/java/com/github/dockerjava/api/command/AsyncDockerCmd.java +++ b/src/main/java/com/github/dockerjava/api/command/AsyncDockerCmd.java @@ -8,7 +8,7 @@ /** * * - * @author marcus + * @author Marcus Linke * */ public interface AsyncDockerCmd, A_RES_T> extends DockerCmd { diff --git a/src/main/java/com/github/dockerjava/api/command/TopContainerResponse.java b/src/main/java/com/github/dockerjava/api/command/TopContainerResponse.java index 8864167f3..cd6da6814 100644 --- a/src/main/java/com/github/dockerjava/api/command/TopContainerResponse.java +++ b/src/main/java/com/github/dockerjava/api/command/TopContainerResponse.java @@ -6,7 +6,7 @@ /** * - * @author marcus + * @author Marcus Linke * */ @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/src/main/java/com/github/dockerjava/api/model/RestartPolicy.java b/src/main/java/com/github/dockerjava/api/model/RestartPolicy.java index cd437667f..34f8e7e57 100644 --- a/src/main/java/com/github/dockerjava/api/model/RestartPolicy.java +++ b/src/main/java/com/github/dockerjava/api/model/RestartPolicy.java @@ -24,7 +24,7 @@ *
* * - * @author marcus + * @author Marcus Linke * */ public class RestartPolicy { diff --git a/src/main/java/com/github/dockerjava/core/DockerClientConfig.java b/src/main/java/com/github/dockerjava/core/DockerClientConfig.java index 618309fd6..fa5c77e6a 100644 --- a/src/main/java/com/github/dockerjava/core/DockerClientConfig.java +++ b/src/main/java/com/github/dockerjava/core/DockerClientConfig.java @@ -346,7 +346,7 @@ public String toString() { public static class DockerClientConfigBuilder { private URI uri; - private String version, username, password, email, serverAddress, dockerCfgPath, dockerCertPath; + private String version, username, password, email, serverAddress, dockerCfgPath; private SSLConfig sslConfig; @@ -401,7 +401,6 @@ public DockerClientConfigBuilder withServerAddress(String serverAddress) { public final DockerClientConfigBuilder withDockerCertPath(String dockerCertPath) { if (dockerCertPath != null) { this.sslConfig = new LocalDirectorySSLConfig(dockerCertPath); - this.dockerCertPath = dockerCertPath; } return this; } diff --git a/src/main/java/com/github/dockerjava/core/async/FrameStreamProcessor.java b/src/main/java/com/github/dockerjava/core/async/FrameStreamProcessor.java index 220cd80ef..3070930d6 100644 --- a/src/main/java/com/github/dockerjava/core/async/FrameStreamProcessor.java +++ b/src/main/java/com/github/dockerjava/core/async/FrameStreamProcessor.java @@ -13,7 +13,7 @@ /** * * - * @author marcus + * @author Marcus Linke * */ public class FrameStreamProcessor implements ResponseStreamProcessor { diff --git a/src/main/java/com/github/dockerjava/core/async/JsonStreamProcessor.java b/src/main/java/com/github/dockerjava/core/async/JsonStreamProcessor.java index 522d5b189..7e6c497e6 100644 --- a/src/main/java/com/github/dockerjava/core/async/JsonStreamProcessor.java +++ b/src/main/java/com/github/dockerjava/core/async/JsonStreamProcessor.java @@ -14,7 +14,7 @@ /** * - * @author marcus + * @author Marcus Linke * */ public class JsonStreamProcessor implements ResponseStreamProcessor { diff --git a/src/main/java/com/github/dockerjava/core/async/ResponseStreamProcessor.java b/src/main/java/com/github/dockerjava/core/async/ResponseStreamProcessor.java index 879a8c305..5c93801d3 100644 --- a/src/main/java/com/github/dockerjava/core/async/ResponseStreamProcessor.java +++ b/src/main/java/com/github/dockerjava/core/async/ResponseStreamProcessor.java @@ -9,7 +9,7 @@ /** * - * @author marcus + * @author Marcus Linke * */ public interface ResponseStreamProcessor { diff --git a/src/main/java/com/github/dockerjava/core/async/ResultCallbackTemplate.java b/src/main/java/com/github/dockerjava/core/async/ResultCallbackTemplate.java index 44af625af..3a62ae46a 100644 --- a/src/main/java/com/github/dockerjava/core/async/ResultCallbackTemplate.java +++ b/src/main/java/com/github/dockerjava/core/async/ResultCallbackTemplate.java @@ -19,7 +19,7 @@ /** * Abstract template implementation of {@link ResultCallback} * - * @author marcus + * @author Marcus Linke * */ public abstract class ResultCallbackTemplate, A_RES_T> implements diff --git a/src/main/java/com/github/dockerjava/core/command/AttachContainerResultCallback.java b/src/main/java/com/github/dockerjava/core/command/AttachContainerResultCallback.java index 2822b8e48..f16b20301 100644 --- a/src/main/java/com/github/dockerjava/core/command/AttachContainerResultCallback.java +++ b/src/main/java/com/github/dockerjava/core/command/AttachContainerResultCallback.java @@ -11,7 +11,7 @@ /** * - * @author marcus + * @author Marcus Linke * */ public class AttachContainerResultCallback extends ResultCallbackTemplate { diff --git a/src/main/java/com/github/dockerjava/core/command/BuildImageResultCallback.java b/src/main/java/com/github/dockerjava/core/command/BuildImageResultCallback.java index 2463f8b1d..06c6b1e51 100644 --- a/src/main/java/com/github/dockerjava/core/command/BuildImageResultCallback.java +++ b/src/main/java/com/github/dockerjava/core/command/BuildImageResultCallback.java @@ -16,7 +16,7 @@ /** * - * @author marcus + * @author Marcus Linke * */ public class BuildImageResultCallback extends ResultCallbackTemplate { diff --git a/src/main/java/com/github/dockerjava/core/command/EventsResultCallback.java b/src/main/java/com/github/dockerjava/core/command/EventsResultCallback.java index 29efac6ca..c731b863e 100644 --- a/src/main/java/com/github/dockerjava/core/command/EventsResultCallback.java +++ b/src/main/java/com/github/dockerjava/core/command/EventsResultCallback.java @@ -11,7 +11,7 @@ /** * - * @author marcus + * @author Marcus Linke * */ public class EventsResultCallback extends ResultCallbackTemplate { diff --git a/src/main/java/com/github/dockerjava/core/command/ExecStartResultCallback.java b/src/main/java/com/github/dockerjava/core/command/ExecStartResultCallback.java index dda336807..ebf1d08a1 100644 --- a/src/main/java/com/github/dockerjava/core/command/ExecStartResultCallback.java +++ b/src/main/java/com/github/dockerjava/core/command/ExecStartResultCallback.java @@ -1,6 +1,3 @@ -/* - * Created on 21.07.2015 - */ package com.github.dockerjava.core.command; import java.io.IOException; @@ -14,7 +11,7 @@ /** * - * @author marcus + * @author Marcus Linke * */ public class ExecStartResultCallback extends ResultCallbackTemplate { @@ -32,7 +29,6 @@ public ExecStartResultCallback() { this(null, null); } - @Override public void onNext(Frame frame) { if (frame != null) { diff --git a/src/main/java/com/github/dockerjava/core/command/LogContainerResultCallback.java b/src/main/java/com/github/dockerjava/core/command/LogContainerResultCallback.java index ffe46baf8..b7fc81cb2 100644 --- a/src/main/java/com/github/dockerjava/core/command/LogContainerResultCallback.java +++ b/src/main/java/com/github/dockerjava/core/command/LogContainerResultCallback.java @@ -11,7 +11,7 @@ /** * - * @author marcus + * @author Marcus Linke * */ public class LogContainerResultCallback extends ResultCallbackTemplate { diff --git a/src/main/java/com/github/dockerjava/core/command/PullImageResultCallback.java b/src/main/java/com/github/dockerjava/core/command/PullImageResultCallback.java index 5416d0537..27be12215 100644 --- a/src/main/java/com/github/dockerjava/core/command/PullImageResultCallback.java +++ b/src/main/java/com/github/dockerjava/core/command/PullImageResultCallback.java @@ -14,7 +14,7 @@ /** * - * @author marcus + * @author Marcus Linke * */ public class PullImageResultCallback extends ResultCallbackTemplate { diff --git a/src/main/java/com/github/dockerjava/core/command/PushImageResultCallback.java b/src/main/java/com/github/dockerjava/core/command/PushImageResultCallback.java index 8f08f3e15..65ec93c88 100644 --- a/src/main/java/com/github/dockerjava/core/command/PushImageResultCallback.java +++ b/src/main/java/com/github/dockerjava/core/command/PushImageResultCallback.java @@ -14,7 +14,7 @@ /** * - * @author marcus + * @author Marcus Linke * */ public class PushImageResultCallback extends ResultCallbackTemplate { diff --git a/src/main/java/com/github/dockerjava/core/command/WaitContainerResultCallback.java b/src/main/java/com/github/dockerjava/core/command/WaitContainerResultCallback.java index 5af956cf2..5416d124c 100644 --- a/src/main/java/com/github/dockerjava/core/command/WaitContainerResultCallback.java +++ b/src/main/java/com/github/dockerjava/core/command/WaitContainerResultCallback.java @@ -16,7 +16,7 @@ /** * - * @author marcus + * @author Marcus Linke * */ public class WaitContainerResultCallback extends ResultCallbackTemplate { diff --git a/src/main/java/com/github/dockerjava/jaxrs/async/GETCallbackNotifier.java b/src/main/java/com/github/dockerjava/jaxrs/async/GETCallbackNotifier.java index 996696479..e6f4fd1d2 100644 --- a/src/main/java/com/github/dockerjava/jaxrs/async/GETCallbackNotifier.java +++ b/src/main/java/com/github/dockerjava/jaxrs/async/GETCallbackNotifier.java @@ -11,7 +11,7 @@ /** * - * @author marcus + * @author Marcus Linke * */ public class GETCallbackNotifier extends AbstractCallbackNotifier { diff --git a/src/main/java/com/github/dockerjava/jaxrs/async/POSTCallbackNotifier.java b/src/main/java/com/github/dockerjava/jaxrs/async/POSTCallbackNotifier.java index 9fe848950..bd96e8250 100644 --- a/src/main/java/com/github/dockerjava/jaxrs/async/POSTCallbackNotifier.java +++ b/src/main/java/com/github/dockerjava/jaxrs/async/POSTCallbackNotifier.java @@ -12,7 +12,7 @@ /** * - * @author marcus + * @author Marcus Linke * */ public class POSTCallbackNotifier extends AbstractCallbackNotifier { diff --git a/src/main/java/com/github/dockerjava/jaxrs/connector/ApacheConnectorClientResponse.java b/src/main/java/com/github/dockerjava/jaxrs/connector/ApacheConnectorClientResponse.java index a4d68e202..d358f574a 100644 --- a/src/main/java/com/github/dockerjava/jaxrs/connector/ApacheConnectorClientResponse.java +++ b/src/main/java/com/github/dockerjava/jaxrs/connector/ApacheConnectorClientResponse.java @@ -14,7 +14,7 @@ * * https://java.net/jira/browse/JERSEY-2852 * - * @author marcus + * @author Marcus Linke * */ public class ApacheConnectorClientResponse extends ClientResponse { diff --git a/src/main/java/com/github/dockerjava/jaxrs/filter/ResponseStatusExceptionFilter.java b/src/main/java/com/github/dockerjava/jaxrs/filter/ResponseStatusExceptionFilter.java index e597b594b..e119c7303 100644 --- a/src/main/java/com/github/dockerjava/jaxrs/filter/ResponseStatusExceptionFilter.java +++ b/src/main/java/com/github/dockerjava/jaxrs/filter/ResponseStatusExceptionFilter.java @@ -24,7 +24,7 @@ /** * This {@link ClientResponseFilter} implementation detects http status codes and throws {@link DockerException}s * - * @author marcus + * @author Marcus Linke * */ public class ResponseStatusExceptionFilter implements ClientResponseFilter { diff --git a/src/main/java/com/github/dockerjava/jaxrs/util/WrappedResponseInputStream.java b/src/main/java/com/github/dockerjava/jaxrs/util/WrappedResponseInputStream.java index 05d4f9066..14072ba55 100644 --- a/src/main/java/com/github/dockerjava/jaxrs/util/WrappedResponseInputStream.java +++ b/src/main/java/com/github/dockerjava/jaxrs/util/WrappedResponseInputStream.java @@ -10,7 +10,7 @@ * {@link WrappedResponseInputStream} is closed it closes the underlying {@link Response} object also to prevent * blocking/hanging connections. * - * @author marcus + * @author Marcus Linke */ public class WrappedResponseInputStream extends InputStream { diff --git a/src/main/java/com/github/dockerjava/netty/ChannelProvider.java b/src/main/java/com/github/dockerjava/netty/ChannelProvider.java index a868f1cd1..49cefe9d5 100644 --- a/src/main/java/com/github/dockerjava/netty/ChannelProvider.java +++ b/src/main/java/com/github/dockerjava/netty/ChannelProvider.java @@ -3,5 +3,5 @@ import io.netty.channel.Channel; public interface ChannelProvider { - Channel getChannel(); -} + Channel getChannel(); +} \ No newline at end of file diff --git a/src/main/java/com/github/dockerjava/netty/DockerCmdExecFactoryImpl.java b/src/main/java/com/github/dockerjava/netty/DockerCmdExecFactoryImpl.java index 4ac044ac6..570f8a663 100644 --- a/src/main/java/com/github/dockerjava/netty/DockerCmdExecFactoryImpl.java +++ b/src/main/java/com/github/dockerjava/netty/DockerCmdExecFactoryImpl.java @@ -115,7 +115,7 @@ * https://github.com/netty/netty/blob/master/example/src/main/java/io/netty/example/http/snoop/HttpSnoopClient.java * https://github.com/slandelle/netty-request-chunking/blob/master/src/test/java/slandelle/ChunkingTest.java * - * @author marcus + * @author Marcus Linke * */ public class DockerCmdExecFactoryImpl implements DockerCmdExecFactory { @@ -154,7 +154,7 @@ public void init(DockerClientConfig dockerClientConfig) { nettyInitializer = new InetSocketInitializer(); } - nettyInitializer.init(bootstrap, dockerClientConfig); + eventLoopGroup = nettyInitializer.init(bootstrap, dockerClientConfig); } private Channel connect() { @@ -169,7 +169,7 @@ private Channel connect(final Bootstrap bootstrap) throws InterruptedException { return nettyInitializer.connect(bootstrap); } - private static interface NettyInitializer { + private interface NettyInitializer { EventLoopGroup init(final Bootstrap bootstrap, DockerClientConfig dockerClientConfig); Channel connect(final Bootstrap bootstrap) throws InterruptedException; diff --git a/src/main/java/com/github/dockerjava/netty/InvocationBuilder.java b/src/main/java/com/github/dockerjava/netty/InvocationBuilder.java index 8b6688ca6..68516100a 100644 --- a/src/main/java/com/github/dockerjava/netty/InvocationBuilder.java +++ b/src/main/java/com/github/dockerjava/netty/InvocationBuilder.java @@ -119,13 +119,13 @@ public void get(ResultCallback resultCallback) { sendRequest(requestProvider, channel); } - public ResponseCallback get(TypeReference typeReference) { + public T get(TypeReference typeReference) { ResponseCallback callback = new ResponseCallback(); get(typeReference, callback); - return callback; + return callback.awaitResult(); } public void get(TypeReference typeReference, ResultCallback resultCallback) { @@ -228,7 +228,7 @@ public void post(final Object entity, final InputStream stdin, ResultCallback execute(ContainerDiffCmd command) { LOGGER.trace("GET: {}", webResource); return webResource.request().accept(MediaType.APPLICATION_JSON).get(new TypeReference>() { - }).awaitResult(); + }); } } diff --git a/src/main/java/com/github/dockerjava/netty/exec/InfoCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/InfoCmdExec.java index 1db090ae6..288765650 100644 --- a/src/main/java/com/github/dockerjava/netty/exec/InfoCmdExec.java +++ b/src/main/java/com/github/dockerjava/netty/exec/InfoCmdExec.java @@ -14,7 +14,7 @@ * http://netty.io/wiki/native-transports.html * https://github.com/netty/netty/blob/master/example/src/main/java/io/netty/ example/http/snoop/HttpSnoopClient.java * - * @author marcus + * @author Marcus Linke * */ public class InfoCmdExec implements InfoCmd.Exec { @@ -23,17 +23,14 @@ public class InfoCmdExec implements InfoCmd.Exec { private WebTarget webResource; - private DockerClientConfig dockerClientConfig; - public InfoCmdExec(WebTarget webResource, DockerClientConfig dockerClientConfig) { this.webResource = webResource; - this.dockerClientConfig = dockerClientConfig; } @Override public Info exec(InfoCmd command) { return webResource.path("info").request().get(new TypeReference() { - }).awaitResult(); + }); } } diff --git a/src/main/java/com/github/dockerjava/netty/exec/InspectContainerCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/InspectContainerCmdExec.java index 85b391a20..e8f691ba1 100644 --- a/src/main/java/com/github/dockerjava/netty/exec/InspectContainerCmdExec.java +++ b/src/main/java/com/github/dockerjava/netty/exec/InspectContainerCmdExec.java @@ -27,7 +27,7 @@ protected InspectContainerResponse execute(InspectContainerCmd command) { LOGGER.debug("GET: {}", webResource); return webResource.request().accept(MediaType.APPLICATION_JSON) .get(new TypeReference() { - }).awaitResult(); + }); } } diff --git a/src/main/java/com/github/dockerjava/netty/exec/InspectExecCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/InspectExecCmdExec.java index aac395d32..ee913f369 100644 --- a/src/main/java/com/github/dockerjava/netty/exec/InspectExecCmdExec.java +++ b/src/main/java/com/github/dockerjava/netty/exec/InspectExecCmdExec.java @@ -25,6 +25,6 @@ protected InspectExecResponse execute(InspectExecCmd command) { LOGGER.debug("GET: {}", webResource); return webResource.request().accept(MediaType.APPLICATION_JSON).get(new TypeReference() { - }).awaitResult(); + }); } } diff --git a/src/main/java/com/github/dockerjava/netty/exec/InspectImageCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/InspectImageCmdExec.java index b6f42414d..b86072ecb 100644 --- a/src/main/java/com/github/dockerjava/netty/exec/InspectImageCmdExec.java +++ b/src/main/java/com/github/dockerjava/netty/exec/InspectImageCmdExec.java @@ -26,7 +26,7 @@ protected InspectImageResponse execute(InspectImageCmd command) { LOGGER.trace("GET: {}", webResource); return webResource.request().accept(MediaType.APPLICATION_JSON).get(new TypeReference() { - }).awaitResult(); + }); } } diff --git a/src/main/java/com/github/dockerjava/netty/exec/ListContainersCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/ListContainersCmdExec.java index fe08c507d..b45e76cbd 100644 --- a/src/main/java/com/github/dockerjava/netty/exec/ListContainersCmdExec.java +++ b/src/main/java/com/github/dockerjava/netty/exec/ListContainersCmdExec.java @@ -44,7 +44,7 @@ protected List execute(ListContainersCmd command) { List containers = webTarget.request().accept(MediaType.APPLICATION_JSON) .get(new TypeReference>() { - }).awaitResult(); + }); LOGGER.trace("Response: {}", containers); diff --git a/src/main/java/com/github/dockerjava/netty/exec/ListImagesCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/ListImagesCmdExec.java index b27bfcf8b..4ca65a061 100644 --- a/src/main/java/com/github/dockerjava/netty/exec/ListImagesCmdExec.java +++ b/src/main/java/com/github/dockerjava/netty/exec/ListImagesCmdExec.java @@ -28,18 +28,18 @@ protected List execute(ListImagesCmd command) { webTarget = booleanQueryParam(webTarget, "all", command.hasShowAllEnabled()); - if (command.getFilters() != null) + if (command.getFilters() != null) { webTarget = webTarget.queryParam("filters", urlPathSegmentEscaper().escape(command.getFilters())); + } LOGGER.trace("GET: {}", webTarget); List images = webTarget.request().accept(MediaType.APPLICATION_JSON) .get(new TypeReference>() { - }).awaitResult(); + }); LOGGER.trace("Response: {}", images); return images; } - } diff --git a/src/main/java/com/github/dockerjava/netty/exec/LogContainerCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/LogContainerCmdExec.java index 4135f7308..911780362 100644 --- a/src/main/java/com/github/dockerjava/netty/exec/LogContainerCmdExec.java +++ b/src/main/java/com/github/dockerjava/netty/exec/LogContainerCmdExec.java @@ -44,5 +44,4 @@ protected Void execute0(LogContainerCmd command, ResultCallback resultCal return null; } - } diff --git a/src/main/java/com/github/dockerjava/netty/exec/SearchImagesCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/SearchImagesCmdExec.java index b1ff7b10c..d31d00a9f 100644 --- a/src/main/java/com/github/dockerjava/netty/exec/SearchImagesCmdExec.java +++ b/src/main/java/com/github/dockerjava/netty/exec/SearchImagesCmdExec.java @@ -27,7 +27,7 @@ protected List execute(SearchImagesCmd command) { LOGGER.trace("GET: {}", webResource); return webResource.request().accept(MediaType.APPLICATION_JSON).get(new TypeReference>() { - }).awaitResult(); + }); } } diff --git a/src/main/java/com/github/dockerjava/netty/exec/TopContainerCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/TopContainerCmdExec.java index 9b04e6c7d..07ec12b03 100644 --- a/src/main/java/com/github/dockerjava/netty/exec/TopContainerCmdExec.java +++ b/src/main/java/com/github/dockerjava/netty/exec/TopContainerCmdExec.java @@ -30,7 +30,7 @@ protected TopContainerResponse execute(TopContainerCmd command) { LOGGER.trace("GET: {}", webResource); return webResource.request().accept(MediaType.APPLICATION_JSON).get(new TypeReference() { - }).awaitResult(); + }); } } diff --git a/src/main/java/com/github/dockerjava/netty/exec/VersionCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/VersionCmdExec.java index 4d2bf936e..84857c208 100644 --- a/src/main/java/com/github/dockerjava/netty/exec/VersionCmdExec.java +++ b/src/main/java/com/github/dockerjava/netty/exec/VersionCmdExec.java @@ -24,7 +24,7 @@ protected Version execute(VersionCmd command) { LOGGER.trace("GET: {}", webResource); return webResource.request().accept(MediaType.APPLICATION_JSON).get(new TypeReference() { - }).awaitResult(); + }); } } diff --git a/src/main/java/com/github/dockerjava/netty/handler/FramedResponseStreamHandler.java b/src/main/java/com/github/dockerjava/netty/handler/FramedResponseStreamHandler.java index 5974f407f..c1b6a4812 100644 --- a/src/main/java/com/github/dockerjava/netty/handler/FramedResponseStreamHandler.java +++ b/src/main/java/com/github/dockerjava/netty/handler/FramedResponseStreamHandler.java @@ -62,10 +62,12 @@ private Frame decode() { if (headerCount == 0) { return null; } + headerCnt += headerCount; - if (headerCnt < HEADER_SIZE) + if (headerCnt < HEADER_SIZE) { return null; + } streamType = streamType(header[0]); @@ -81,8 +83,9 @@ private Frame decode() { int count = read(payload, payloadCnt, rawBuffer.readableBytes()); - if (count == 0) + if (count == 0) { return null; + } payloadCnt = 0; @@ -92,18 +95,21 @@ private Frame decode() { int payloadSize = ((header[4] & 0xff) << 24) + ((header[5] & 0xff) << 16) + ((header[6] & 0xff) << 8) + (header[7] & 0xff); - if (payloadCnt == 0) + if (payloadCnt == 0) { payload = new byte[payloadSize]; + } int count = read(payload, payloadCnt, payloadSize - payloadCnt); - if (count == 0) + if (count == 0) { return null; + } payloadCnt += count; - if (payloadCnt < payloadSize) + if (payloadCnt < payloadSize) { return null; + } headerCnt = 0; payloadCnt = 0; diff --git a/src/main/java/com/github/dockerjava/netty/handler/HttpConnectionHijackHandler.java b/src/main/java/com/github/dockerjava/netty/handler/HttpConnectionHijackHandler.java index 0b0135d7e..3010682f6 100644 --- a/src/main/java/com/github/dockerjava/netty/handler/HttpConnectionHijackHandler.java +++ b/src/main/java/com/github/dockerjava/netty/handler/HttpConnectionHijackHandler.java @@ -36,7 +36,7 @@ public CharSequence protocol() { return "tcp"; } - public void await() { + public void awaitUpgrade() { try { latch.await(); } catch (InterruptedException e) { diff --git a/src/main/java/com/github/dockerjava/netty/handler/HttpRequestProvider.java b/src/main/java/com/github/dockerjava/netty/handler/HttpRequestProvider.java index 9777a1098..0d861cc01 100644 --- a/src/main/java/com/github/dockerjava/netty/handler/HttpRequestProvider.java +++ b/src/main/java/com/github/dockerjava/netty/handler/HttpRequestProvider.java @@ -3,7 +3,6 @@ import io.netty.handler.codec.http.HttpRequest; public interface HttpRequestProvider { - - HttpRequest getHttpRequest(String uri); + public HttpRequest getHttpRequest(String uri); } diff --git a/src/main/java/com/github/dockerjava/netty/handler/HttpResponseStreamHandler.java b/src/main/java/com/github/dockerjava/netty/handler/HttpResponseStreamHandler.java index 0cda2de81..59dc8929e 100644 --- a/src/main/java/com/github/dockerjava/netty/handler/HttpResponseStreamHandler.java +++ b/src/main/java/com/github/dockerjava/netty/handler/HttpResponseStreamHandler.java @@ -56,10 +56,12 @@ public int available() throws IOException { } private int readableBytes() { - if (current != null) + if (current != null) { return current.readableBytes(); - else + } else { return 0; + } + } @Override @@ -68,8 +70,9 @@ public int read() throws IOException { poll(); if (readableBytes() == 0) { - if (closed.get()) + if (closed.get()) { return -1; + } } if (current != null && current.readableBytes() > 0) { @@ -77,7 +80,6 @@ public int read() throws IOException { } else { return read(); } - } private void poll() { @@ -89,7 +91,5 @@ private void poll() { } } } - } - -} +} \ No newline at end of file diff --git a/src/test/java/com/github/dockerjava/core/NameParserTest.java b/src/test/java/com/github/dockerjava/core/NameParserTest.java index afae80ebb..865f9a401 100644 --- a/src/test/java/com/github/dockerjava/core/NameParserTest.java +++ b/src/test/java/com/github/dockerjava/core/NameParserTest.java @@ -16,7 +16,7 @@ /** * * - * @author marcus + * @author Marcus Linke * */ public class NameParserTest { diff --git a/src/test/java/com/github/dockerjava/core/TestDockerCmdExecFactory.java b/src/test/java/com/github/dockerjava/core/TestDockerCmdExecFactory.java index d1ff920b0..4a96e664f 100644 --- a/src/test/java/com/github/dockerjava/core/TestDockerCmdExecFactory.java +++ b/src/test/java/com/github/dockerjava/core/TestDockerCmdExecFactory.java @@ -53,7 +53,7 @@ * Special {@link DockerCmdExecFactory} implementation that collects container and image creations while test execution * for the purpose of automatically cleanup. * - * @author marcus + * @author Marcus Linke */ public class TestDockerCmdExecFactory implements DockerCmdExecFactory { diff --git a/src/test/java/com/github/dockerjava/netty/AbstractNettyDockerClientTest.java b/src/test/java/com/github/dockerjava/netty/AbstractNettyDockerClientTest.java index 7f2b83f4d..6dcb435f1 100644 --- a/src/test/java/com/github/dockerjava/netty/AbstractNettyDockerClientTest.java +++ b/src/test/java/com/github/dockerjava/netty/AbstractNettyDockerClientTest.java @@ -1,39 +1,10 @@ package com.github.dockerjava.netty; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.contains; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.StringWriter; -import java.lang.reflect.Method; -import java.net.DatagramSocket; -import java.net.ServerSocket; -import java.util.ArrayList; -import java.util.List; - -import org.apache.commons.io.IOUtils; -import org.apache.commons.io.LineIterator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.testng.Assert; -import org.testng.ITestResult; -import com.github.dockerjava.api.DockerClient; -import com.github.dockerjava.api.exception.DockerException; -import com.github.dockerjava.api.command.InspectContainerResponse; -import com.github.dockerjava.api.model.Frame; -import com.github.dockerjava.api.model.Volume; -import com.github.dockerjava.api.model.VolumeBind; import com.github.dockerjava.client.AbstractDockerClientTest; -import com.github.dockerjava.core.DockerClientBuilder; -import com.github.dockerjava.core.DockerClientConfig; import com.github.dockerjava.core.TestDockerCmdExecFactory; -import com.github.dockerjava.core.command.BuildImageResultCallback; -import com.github.dockerjava.core.command.LogContainerResultCallback; -import com.github.dockerjava.core.command.PullImageResultCallback; -import com.google.common.base.Joiner; public abstract class AbstractNettyDockerClientTest extends AbstractDockerClientTest { @@ -44,184 +15,4 @@ protected TestDockerCmdExecFactory initTestDockerCmdExecFactory() { return new TestDockerCmdExecFactory( new DockerCmdExecFactoryImpl()); } -// -// protected TestDockerCmdExecFactory dockerCmdExecFactory = -// -// public void beforeTest() throws Exception { -// -// LOG.info("======================= BEFORETEST ======================="); -// LOG.info("Connecting to Docker server"); -// dockerClient = DockerClientBuilder.getInstance(config()).withDockerCmdExecFactory(dockerCmdExecFactory).build(); -// -//// LOG.info("Pulling image 'busybox'"); -//// -//// // need to block until image is pulled completely -//// dockerClient.pullImageCmd("busybox").withTag("latest").exec(new PullImageResultCallback()).awaitSuccess(); -// -// assertNotNull(dockerClient); -// LOG.info("======================= END OF BEFORETEST =======================\n\n"); -// } -// -// private DockerClientConfig config() { -// return config(null); -// } -// -// protected DockerClientConfig config(String password) { -// DockerClientConfig.DockerClientConfigBuilder builder = DockerClientConfig.createDefaultConfigBuilder() -// .withServerAddress("https://index.docker.io/v1/"); -// if (password != null) { -// builder = builder.withPassword(password); -// } -// -// return builder.withVersion(apiVersion).build(); -// } -// -// public void afterTest() { -// LOG.info("======================= END OF AFTERTEST ======================="); -// } -// -// public void beforeMethod(Method method) { -// LOG.info(String.format("################################## STARTING %s ##################################", -// method.getName())); -// } -// -// public void afterMethod(ITestResult result) { -// -// for (String container : dockerCmdExecFactory.getContainerNames()) { -// LOG.info("Cleaning up temporary container {}", container); -// -// try { -// dockerClient.removeContainerCmd(container).withForce(true).exec(); -// } catch (DockerException ignore) { -// // ignore.printStackTrace(); -// } -// } -// -// for (String image : dockerCmdExecFactory.getImageNames()) { -// LOG.info("Cleaning up temporary image with {}", image); -// try { -// dockerClient.removeImageCmd(image).withForce(true).exec(); -// } catch (DockerException ignore) { -// // ignore.printStackTrace(); -// } -// } -// -// LOG.info("################################## END OF {} ##################################\n", result.getName()); -// } -// -// protected String asString(InputStream response) { -// return consumeAsString(response); -// } -// -// public static String consumeAsString(InputStream response) { -// -// StringWriter logwriter = new StringWriter(); -// -// try { -// LineIterator itr = IOUtils.lineIterator(response, "UTF-8"); -// -// while (itr.hasNext()) { -// String line = itr.next(); -// logwriter.write(line + (itr.hasNext() ? "\n" : "")); -// LOG.info("line: " + line); -// } -// response.close(); -// -// return logwriter.toString(); -// } catch (IOException e) { -// throw new RuntimeException(e); -// } finally { -// IOUtils.closeQuietly(response); -// } -// } -// -// // UTIL -// -// /** -// * Checks to see if a specific port is available. -// * -// * @param port -// * the port to check for availability -// */ -// public static Boolean available(int port) { -// if (port < 1100 || port > 60000) { -// throw new IllegalArgumentException("Invalid start port: " + port); -// } -// -// ServerSocket ss = null; -// DatagramSocket ds = null; -// try { -// ss = new ServerSocket(port); -// ss.setReuseAddress(true); -// ds = new DatagramSocket(port); -// ds.setReuseAddress(true); -// return true; -// } catch (IOException ignored) { -// } finally { -// if (ds != null) { -// ds.close(); -// } -// -// if (ss != null) { -// try { -// ss.close(); -// } catch (IOException e) { -// /* should not be thrown */ -// } -// } -// } -// -// return false; -// } -// -// /** -// * Asserts that {@link InspectContainerResponse#getVolumes()} (.Volumes) has {@link VolumeBind}s for -// * the given {@link Volume}s -// */ -// public static void assertContainerHasVolumes(InspectContainerResponse inspectContainerResponse, -// Volume... expectedVolumes) { -// VolumeBind[] volumeBinds = inspectContainerResponse.getVolumes(); -// LOG.info("Inspect .Volumes = [{}]", Joiner.on(", ").join(volumeBinds)); -// -// List volumes = new ArrayList(); -// for (VolumeBind bind : volumeBinds) { -// volumes.add(new Volume(bind.getContainerPath())); -// } -// assertThat(volumes, contains(expectedVolumes)); -// } -// -// protected String containerLog(String containerId) throws Exception { -// return dockerClient.logContainerCmd(containerId).withStdOut(true).exec(new LogContainerTestCallback()) -// .awaitCompletion().toString(); -// } -// -// public static class LogContainerTestCallback extends LogContainerResultCallback { -// protected final StringBuffer log = new StringBuffer(); -// -// @Override -// public void onNext(Frame frame) { -// log.append(new String(frame.getPayload())); -// super.onNext(frame); -// } -// -// @Override -// public String toString() { -// return log.toString(); -// } -// -// @Override -// public LogContainerResultCallback awaitCompletion() throws InterruptedException { -// LogContainerResultCallback result = super.awaitCompletion(); -// System.err.println("awaitCompletion returned"); -// -// return result; -// } -// } -// -// protected String buildImage(File baseDir) throws Exception { -// -// return dockerClient.buildImageCmd(baseDir).withNoCache(true).exec(new BuildImageResultCallback()) -// .awaitImageId(); -// } - } From d5dbb5de459809fdf90467a67ffd07b5595725ed Mon Sep 17 00:00:00 2001 From: Marcus Linke Date: Mon, 14 Dec 2015 21:04:01 +0100 Subject: [PATCH 13/13] Added some javadoc --- .../netty/DockerCmdExecFactoryImpl.java | 25 +++++--- .../dockerjava/netty/InvocationBuilder.java | 6 ++ .../github/dockerjava/netty/MediaType.java | 6 ++ .../github/dockerjava/netty/WebTarget.java | 6 ++ .../handler/FramedResponseStreamHandler.java | 9 +++ .../netty/handler/HttpResponseHandler.java | 6 ++ .../handler/HttpResponseStreamHandler.java | 5 ++ .../netty/handler/JsonRequestHandler.java | 5 ++ .../handler/JsonResponseCallbackHandler.java | 5 ++ .../netty/handler/JsonResponseHandler.java | 62 ------------------- 10 files changed, 65 insertions(+), 70 deletions(-) delete mode 100644 src/main/java/com/github/dockerjava/netty/handler/JsonResponseHandler.java diff --git a/src/main/java/com/github/dockerjava/netty/DockerCmdExecFactoryImpl.java b/src/main/java/com/github/dockerjava/netty/DockerCmdExecFactoryImpl.java index 570f8a663..865389fef 100644 --- a/src/main/java/com/github/dockerjava/netty/DockerCmdExecFactoryImpl.java +++ b/src/main/java/com/github/dockerjava/netty/DockerCmdExecFactoryImpl.java @@ -27,8 +27,6 @@ import javax.net.ssl.SSLParameters; import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import com.github.dockerjava.api.command.AttachContainerCmd; import com.github.dockerjava.api.command.AuthCmd; @@ -70,6 +68,7 @@ import com.github.dockerjava.api.command.VersionCmd; import com.github.dockerjava.api.command.WaitContainerCmd; import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.core.DockerClientImpl; import com.github.dockerjava.netty.exec.AttachContainerCmdExec; import com.github.dockerjava.netty.exec.AuthCmdExec; import com.github.dockerjava.netty.exec.BuildImageCmdExec; @@ -110,17 +109,27 @@ import com.github.dockerjava.netty.exec.WaitContainerCmdExec; /** - * http://stackoverflow.com/questions/33296749/netty-connect-to-unix-domain-socket-failed - * http://netty.io/wiki/native-transports.html - * https://github.com/netty/netty/blob/master/example/src/main/java/io/netty/example/http/snoop/HttpSnoopClient.java - * https://github.com/slandelle/netty-request-chunking/blob/master/src/test/java/slandelle/ChunkingTest.java + * Experimental implementation of {@link DockerCmdExecFactory} that supports http connection hijacking that is needed to + * pass STDIN to the container. * - * @author Marcus Linke + * To use it just pass an instance via {@link DockerClientImpl#withDockerCmdExecFactory(DockerCmdExecFactory)} + * + * @see https://docs.docker.com/engine/reference/api/docker_remote_api_v1.21/#attach-to-a-container + * @see https://docs.docker.com/engine/reference/api/docker_remote_api_v1.21/#exec-start * + * + * @author Marcus Linke */ public class DockerCmdExecFactoryImpl implements DockerCmdExecFactory { - private static final Logger LOGGER = LoggerFactory.getLogger(DockerCmdExecFactoryImpl.class.getName()); + /* + * useful links: + * + * http://stackoverflow.com/questions/33296749/netty-connect-to-unix-domain-socket-failed + * http://netty.io/wiki/native-transports.html + * https://github.com/netty/netty/blob/master/example/src/main/java/io/netty/example/http/snoop/HttpSnoopClient.java + * https://github.com/slandelle/netty-request-chunking/blob/master/src/test/java/slandelle/ChunkingTest.java + */ private DockerClientConfig dockerClientConfig; diff --git a/src/main/java/com/github/dockerjava/netty/InvocationBuilder.java b/src/main/java/com/github/dockerjava/netty/InvocationBuilder.java index 68516100a..a478cf6c1 100644 --- a/src/main/java/com/github/dockerjava/netty/InvocationBuilder.java +++ b/src/main/java/com/github/dockerjava/netty/InvocationBuilder.java @@ -45,6 +45,12 @@ import com.github.dockerjava.netty.handler.HttpResponseStreamHandler; import com.github.dockerjava.netty.handler.JsonResponseCallbackHandler; +/** + * This class is basically a replacement of javax.ws.rs.client.Invocation.Builder to allow simpler + * migration of JAX-RS code to a netty based implementation. + * + * @author Marcus Linke + */ public class InvocationBuilder { public class ResponseCallback extends ResultCallbackTemplate, T> { diff --git a/src/main/java/com/github/dockerjava/netty/MediaType.java b/src/main/java/com/github/dockerjava/netty/MediaType.java index 3341f4a46..a7738604a 100644 --- a/src/main/java/com/github/dockerjava/netty/MediaType.java +++ b/src/main/java/com/github/dockerjava/netty/MediaType.java @@ -1,5 +1,11 @@ package com.github.dockerjava.netty; +/** + * This class is basically a replacement of javax.ws.rs.core.MediaType to allow simpler + * migration of JAX-RS code to a netty based implementation. + * + * @author Marcus Linke + */ public enum MediaType { APPLICATION_JSON("application/json"), diff --git a/src/main/java/com/github/dockerjava/netty/WebTarget.java b/src/main/java/com/github/dockerjava/netty/WebTarget.java index 4362ccab9..7698858a3 100644 --- a/src/main/java/com/github/dockerjava/netty/WebTarget.java +++ b/src/main/java/com/github/dockerjava/netty/WebTarget.java @@ -8,6 +8,12 @@ import org.apache.commons.lang.StringUtils; +/** + * This class is basically a replacement of javax.ws.rs.client.WebTarget to allow simpler + * migration of JAX-RS code to a netty based implementation. + * + * @author Marcus Linke + */ public class WebTarget { private ChannelProvider channelProvider; diff --git a/src/main/java/com/github/dockerjava/netty/handler/FramedResponseStreamHandler.java b/src/main/java/com/github/dockerjava/netty/handler/FramedResponseStreamHandler.java index c1b6a4812..87878f0da 100644 --- a/src/main/java/com/github/dockerjava/netty/handler/FramedResponseStreamHandler.java +++ b/src/main/java/com/github/dockerjava/netty/handler/FramedResponseStreamHandler.java @@ -9,6 +9,15 @@ import com.github.dockerjava.api.model.Frame; import com.github.dockerjava.api.model.StreamType; +/** + * Handler that decodes a docker-raw-stream as described here: + * + * https://docs.docker.com/engine/reference/api/docker_remote_api_v1.21/#attach-to-a-container + * + * It drives the {@link ResultCallback#onNext(Object)} method of the passed {@link ResultCallback}. + * + * @author Marcus Linke + */ public class FramedResponseStreamHandler extends SimpleChannelInboundHandler { private static final int HEADER_SIZE = 8; diff --git a/src/main/java/com/github/dockerjava/netty/handler/HttpResponseHandler.java b/src/main/java/com/github/dockerjava/netty/handler/HttpResponseHandler.java index d5306ba26..2f11ccf38 100644 --- a/src/main/java/com/github/dockerjava/netty/handler/HttpResponseHandler.java +++ b/src/main/java/com/github/dockerjava/netty/handler/HttpResponseHandler.java @@ -23,6 +23,12 @@ import com.github.dockerjava.api.exception.NotModifiedException; import com.github.dockerjava.api.exception.UnauthorizedException; +/** + * Handler that is responsible to handle an incoming {@link HttpResponse}. It evaluates the status code and + * triggers the appropriate lifecycle methods at the passed {@link ResultCallback}. + * + * @author Marcus Linke + */ public class HttpResponseHandler extends SimpleChannelInboundHandler { private HttpResponse response; diff --git a/src/main/java/com/github/dockerjava/netty/handler/HttpResponseStreamHandler.java b/src/main/java/com/github/dockerjava/netty/handler/HttpResponseStreamHandler.java index 59dc8929e..b3800be56 100644 --- a/src/main/java/com/github/dockerjava/netty/handler/HttpResponseStreamHandler.java +++ b/src/main/java/com/github/dockerjava/netty/handler/HttpResponseStreamHandler.java @@ -12,6 +12,11 @@ import com.github.dockerjava.api.async.ResultCallback; +/** + * Handler that converts an incoming byte stream to an {@link InputStream}. + * + * @author marcus + */ public class HttpResponseStreamHandler extends SimpleChannelInboundHandler { private HttpResponseInputStream stream = new HttpResponseInputStream(); diff --git a/src/main/java/com/github/dockerjava/netty/handler/JsonRequestHandler.java b/src/main/java/com/github/dockerjava/netty/handler/JsonRequestHandler.java index 4340e1a38..54ffb2f96 100644 --- a/src/main/java/com/github/dockerjava/netty/handler/JsonRequestHandler.java +++ b/src/main/java/com/github/dockerjava/netty/handler/JsonRequestHandler.java @@ -6,6 +6,11 @@ import com.fasterxml.jackson.databind.ObjectMapper; +/** + * Handler that encodes an outgoing object to JSON. + * + * @author Marcus Linke + */ public class JsonRequestHandler extends MessageToByteEncoder{ private ObjectMapper mapper = new ObjectMapper(); diff --git a/src/main/java/com/github/dockerjava/netty/handler/JsonResponseCallbackHandler.java b/src/main/java/com/github/dockerjava/netty/handler/JsonResponseCallbackHandler.java index 614508a81..b5f76ab73 100644 --- a/src/main/java/com/github/dockerjava/netty/handler/JsonResponseCallbackHandler.java +++ b/src/main/java/com/github/dockerjava/netty/handler/JsonResponseCallbackHandler.java @@ -8,6 +8,11 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.github.dockerjava.api.async.ResultCallback; +/** + * Handler that decodes an incoming byte stream into objects of T and calls {@link ResultCallback#onNext(Object)} + * + * @author Marcus Linke + */ public class JsonResponseCallbackHandler extends SimpleChannelInboundHandler { private static ObjectMapper objectMapper = new ObjectMapper(); diff --git a/src/main/java/com/github/dockerjava/netty/handler/JsonResponseHandler.java b/src/main/java/com/github/dockerjava/netty/handler/JsonResponseHandler.java deleted file mode 100644 index efbd74f1f..000000000 --- a/src/main/java/com/github/dockerjava/netty/handler/JsonResponseHandler.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.github.dockerjava.netty.handler; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; - -import java.util.concurrent.CountDownLatch; - -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; - -public class JsonResponseHandler extends SimpleChannelInboundHandler { - - private static ObjectMapper objectMapper = new ObjectMapper(); - - private TypeReference typeReference; - - private CountDownLatch countDownLatch = new CountDownLatch(1); - - private T object; - - public JsonResponseHandler(TypeReference typeReference) { - this.typeReference = typeReference; - } - - @Override - protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { - System.err.println("AwaitObjectInboundHandler: channelRead0: " + msg); - byte[] buffer = new byte[msg.readableBytes()]; - msg.readBytes(buffer); - - try { - object = objectMapper.readValue(buffer, typeReference); - } catch (Exception e) { - throw new RuntimeException(e); - } - - countDownLatch.countDown(); - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { - System.err.println("AwaitObjectInboundHandler: channelReadComplete"); - super.channelReadComplete(ctx); - } - - public T await() { - try { - countDownLatch.await(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - return object; - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - cause.printStackTrace(); - ctx.close(); - } - -}