diff --git a/.gitignore b/.gitignore index 8f1fdc779..a705c1129 100644 --- a/.gitignore +++ b/.gitignore @@ -21,4 +21,5 @@ target *.log #Ignore Test Output -test-output \ No newline at end of file +test-output +/.checkstyle diff --git a/pom.xml b/pom.xml index b1b6d96a9..acf2444ef 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,5 @@ - + 4.0.0 @@ -73,7 +74,7 @@ 1.1.7 6.9.10 - 4.1.3.Final + 4.1.7.Final-SNAPSHOT 1.3 1.8 2.3.3 @@ -86,6 +87,9 @@ 2.19.1 2.19.1 1.8 + + macos-x86_64 + @@ -240,11 +244,29 @@ netty-handler-proxy ${netty.version} + + + + + + + + io.netty + netty-transport-native-kqueue + ${netty.version} + osx-x86_64 + + + io.netty + netty-transport-native-unix-common + + + io.netty - netty-transport-native-epoll + netty-transport-native-unix-common ${netty.version} - linux-x86_64 + osx-x86_64 junit @@ -252,6 +274,16 @@ 4.12 test + + com.github.jnr + jnr-unixsocket + 0.12 + + + me.lessis + unisockets-core_2.11 + 0.1.0 + @@ -270,7 +302,7 @@ - + @@ -419,9 +451,9 @@ true 1 integration - integration-auth + integration-auth - **/*Test.java + **/*ExecTest.java @@ -483,7 +515,8 @@ true true false - + src/test/resources/checkstyle/checkstyle-config.xml diff --git a/src/main/java/com/github/dockerjava/netty/InvocationBuilder.java b/src/main/java/com/github/dockerjava/netty/InvocationBuilder.java index 26b950e4e..0f2c737a6 100644 --- a/src/main/java/com/github/dockerjava/netty/InvocationBuilder.java +++ b/src/main/java/com/github/dockerjava/netty/InvocationBuilder.java @@ -322,6 +322,7 @@ public void run() { } // we close the writing side of the socket, but keep the read side open to transfer stdout/stderr + System.out.println("shutdownOutput"); channel.shutdownOutput(); } diff --git a/src/main/java/com/github/dockerjava/netty/NettyDockerCmdExecFactory.java b/src/main/java/com/github/dockerjava/netty/NettyDockerCmdExecFactory.java index cead92e92..070bbd6b6 100644 --- a/src/main/java/com/github/dockerjava/netty/NettyDockerCmdExecFactory.java +++ b/src/main/java/com/github/dockerjava/netty/NettyDockerCmdExecFactory.java @@ -1,5 +1,18 @@ package com.github.dockerjava.netty; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.security.Security; + +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLParameters; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; + import com.github.dockerjava.api.command.AttachContainerCmd; import com.github.dockerjava.api.command.AuthCmd; import com.github.dockerjava.api.command.BuildImageCmd; @@ -39,6 +52,7 @@ import com.github.dockerjava.api.command.RemoveImageCmd; import com.github.dockerjava.api.command.RemoveNetworkCmd; import com.github.dockerjava.api.command.RemoveVolumeCmd; +import com.github.dockerjava.api.command.RenameContainerCmd; import com.github.dockerjava.api.command.RestartContainerCmd; import com.github.dockerjava.api.command.SaveImageCmd; import com.github.dockerjava.api.command.SearchImagesCmd; @@ -51,7 +65,6 @@ import com.github.dockerjava.api.command.UpdateContainerCmd; import com.github.dockerjava.api.command.VersionCmd; import com.github.dockerjava.api.command.WaitContainerCmd; -import com.github.dockerjava.api.command.RenameContainerCmd; import com.github.dockerjava.core.DockerClientConfig; import com.github.dockerjava.core.DockerClientImpl; import com.github.dockerjava.core.SSLConfig; @@ -93,6 +106,7 @@ import com.github.dockerjava.netty.exec.RemoveImageCmdExec; import com.github.dockerjava.netty.exec.RemoveNetworkCmdExec; import com.github.dockerjava.netty.exec.RemoveVolumeCmdExec; +import com.github.dockerjava.netty.exec.RenameContainerCmdExec; import com.github.dockerjava.netty.exec.RestartContainerCmdExec; import com.github.dockerjava.netty.exec.SaveImageCmdExec; import com.github.dockerjava.netty.exec.SearchImagesCmdExec; @@ -105,37 +119,22 @@ import com.github.dockerjava.netty.exec.UpdateContainerCmdExec; import com.github.dockerjava.netty.exec.VersionCmdExec; import com.github.dockerjava.netty.exec.WaitContainerCmdExec; -import com.github.dockerjava.netty.exec.RenameContainerCmdExec; import io.netty.bootstrap.Bootstrap; 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.kqueue.KQueueDomainSocketChannel; +import io.netty.channel.kqueue.KQueueEventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.DuplexChannel; 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.logging.LoggingHandler; import io.netty.handler.ssl.SslHandler; import io.netty.util.concurrent.DefaultThreadFactory; -import org.bouncycastle.jce.provider.BouncyCastleProvider; - -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLParameters; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.security.Security; - -import static com.google.common.base.Preconditions.checkNotNull; - /** * Experimental implementation of {@link DockerCmdExecFactory} that supports http connection hijacking that is needed to pass STDIN to the * container. @@ -215,22 +214,37 @@ private interface NettyInitializer { } private class UnixDomainSocketInitializer implements NettyInitializer { + + final java.io.File path = new java.io.File("/var/run/docker.sock"); + @Override public EventLoopGroup init(Bootstrap bootstrap, DockerClientConfig dockerClientConfig) { - EventLoopGroup epollEventLoopGroup = new EpollEventLoopGroup(0, new DefaultThreadFactory(threadPrefix)); - bootstrap.group(epollEventLoopGroup).channel(EpollDomainSocketChannel.class) - .handler(new ChannelInitializer() { + + EventLoopGroup nioEventLoopGroup = new KQueueEventLoopGroup(0, + new DefaultThreadFactory(threadPrefix)); + + bootstrap.group(nioEventLoopGroup).channel(KQueueDomainSocketChannel.class) + .handler(new ChannelInitializer() { @Override - protected void initChannel(final UnixChannel channel) throws Exception { + protected void initChannel(final KQueueDomainSocketChannel channel) throws Exception { + channel.pipeline().addLast(new LoggingHandler(getClass())); channel.pipeline().addLast(new HttpClientCodec()); } }); - return epollEventLoopGroup; + + return nioEventLoopGroup; } @Override public DuplexChannel connect(Bootstrap bootstrap) throws InterruptedException { - return (DuplexChannel) bootstrap.connect(new DomainSocketAddress("/var/run/docker.sock")).sync().channel(); + + if (!path.exists()) { + throw new RuntimeException("socket not found: " + path); + } + + DomainSocketAddress address = new DomainSocketAddress(path); + + return (DuplexChannel) bootstrap.connect(address).sync().channel(); } } diff --git a/src/main/java/jnr/enxio/channels/NativeSocketChannel.java b/src/main/java/jnr/enxio/channels/NativeSocketChannel.java new file mode 100644 index 000000000..3c7880878 --- /dev/null +++ b/src/main/java/jnr/enxio/channels/NativeSocketChannel.java @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2008 Wayne Meissner + * + * This file is part of the JNR project. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package jnr.enxio.channels; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.nio.ByteBuffer; +import java.nio.channels.ByteChannel; +import java.nio.channels.SocketChannel; +import java.nio.channels.spi.SelectorProvider; + +import jnr.constants.platform.Errno; +import jnr.constants.platform.Shutdown; + +public abstract class NativeSocketChannel extends SocketChannel implements ByteChannel, NativeSelectableChannel { + + private int fd = -1; + + public NativeSocketChannel(int fd) { + this(NativeSelectorProvider.getInstance(), fd); + } + + public NativeSocketChannel() { + super(NativeSelectorProvider.getInstance()); + } + + NativeSocketChannel(SelectorProvider provider, int fd) { + super(provider); + this.fd = fd; + } + + public void setFD(int fd) { + this.fd = fd; + } + + @Override + protected void implCloseSelectableChannel() throws IOException { + System.out.println("implCloseSelectableChannel"); + Native.close(fd); + } + + @Override + protected void implConfigureBlocking(boolean block) throws IOException { + System.out.println("implConfigureBlocking: " + block); + Native.setBlocking(fd, block); + } + + public final int getFD() { + return fd; + } + + public int read(ByteBuffer dst) throws IOException { + // System.out.println("dst.remaining: " + dst.remaining()); + // System.out.println("dst.limit: " + dst.limit()); + + ByteBuffer buffer = ByteBuffer.allocate(dst.remaining()); + + int n = Native.read(fd, buffer); + System.out.println("n: " + n); + + dst.put(buffer.array()); + + switch (n) { + case 0: + return -1; + + case -1: + Errno lastError = Native.getLastError(); + switch (lastError) { + case EAGAIN: + case EWOULDBLOCK: + return 0; + + default: + throw new IOException(Native.getLastErrorString()); + } + + default: { + + return n; + } + } + } + + public int write(ByteBuffer src) throws IOException { + + System.err.println("write:"); + //System.err.println(hexDump(src, "")); + + ByteBuffer buffer = ByteBuffer.allocate(src.remaining()); + + buffer.put(src); + + buffer.position(0); + + int n = Native.write(fd, buffer); + + System.err.println(Native.getLastErrorString()); + + if (n < 0) { + System.err.println("write error"); + throw new IOException(Native.getLastErrorString()); + } + + return n; + } + + public SocketChannel shutdownInput() throws IOException { + System.out.println("shutdownInput"); + int n = Native.shutdown(fd, SHUT_RD); + if (n < 0) { + throw new IOException(Native.getLastErrorString()); + } + return this; + } + + public SocketChannel shutdownOutput() throws IOException { + System.out.println("shutdownOutput"); + int n = Native.shutdown(fd, SHUT_WR); + if (n < 0) { + throw new IOException(Native.getLastErrorString()); + } + return this; + } + + private static final int SHUT_RD = Shutdown.SHUT_RD.intValue(); + private static final int SHUT_WR = Shutdown.SHUT_WR.intValue(); + + public static String hexDump(ByteBuffer buf, String prefix) { + buf = buf.duplicate(); + StringWriter str = new StringWriter(); + PrintWriter out = new PrintWriter(str); + int i = 0; + int len = buf.remaining(); + byte[] line = new byte[16]; + while (i < len) { + if (prefix != null) { + out.print(prefix); + } + out.print(formatInt(i, 16, 8)); + out.print(" "); + int l = Math.min(16, len - i); + buf.get(line, 0, l); + String s = toHexString(line, 0, l, ' '); + out.print(s); + for (int j = s.length(); j < 49; j++) { + out.print(' '); + } + for (int j = 0; j < l; j++) { + int c = line[j] & 0xFF; + if (c < 0x20 || c > 0x7E) { + out.print('.'); + } else { + out.print((char) c); + } + } + out.println(); + i += 16; + } + return str.toString(); + } + + public static String formatInt(int i, int radix, int len) { + String s = Integer.toString(i, radix); + StringBuffer buf = new StringBuffer(); + for (int j = 0; j < len - s.length(); j++) { + buf.append("0"); + } + buf.append(s); + return buf.toString(); + } + + public static String toHexString(byte[] buf, int off, int len, char sep) { + StringBuffer str = new StringBuffer(); + for (int i = 0; i < len; i++) { + str.append(HEX.charAt(buf[i + off] >>> 4 & 0x0F)); + str.append(HEX.charAt(buf[i + off] & 0x0F)); + if (i < len - 1) { + str.append(sep); + } + } + return str.toString(); + } + + static final String HEX = "0123456789abcdef"; + + +} diff --git a/src/main/java/jnr/unixsocket/UnixSocketChannel.java b/src/main/java/jnr/unixsocket/UnixSocketChannel.java new file mode 100644 index 000000000..00e226d8f --- /dev/null +++ b/src/main/java/jnr/unixsocket/UnixSocketChannel.java @@ -0,0 +1,375 @@ +/* + * Copyright (C) 2009 Wayne Meissner + * + * This file is part of the JNR project. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package jnr.unixsocket; + +import java.io.IOException; +import java.net.Socket; +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.SocketChannel; +import java.nio.channels.UnsupportedAddressTypeException; +import java.util.Set; + +import jnr.constants.platform.Errno; +import jnr.constants.platform.ProtocolFamily; +import jnr.constants.platform.Sock; +import jnr.constants.platform.SocketLevel; +import jnr.constants.platform.SocketOption; +import jnr.enxio.channels.NativeSocketChannel; +import jnr.ffi.LastError; +import jnr.ffi.byref.IntByReference; +import scala.Option; + +/** + * A {@link java.nio.channels.Channel} implementation that uses a native unix + * socket + */ +public class UnixSocketChannel extends NativeSocketChannel { + enum State { + UNINITIALIZED, CONNECTED, IDLE, CONNECTING, + } + + private volatile State state; + private UnixSocketAddress remoteAddress = null; + private UnixSocketAddress localAddress = null; + + public static final UnixSocketChannel open() throws IOException { + return new UnixSocketChannel(); + } + + public static final UnixSocketChannel open(UnixSocketAddress remote) throws IOException { + UnixSocketChannel channel = new UnixSocketChannel(); + + try { + channel.connect(remote); + } catch (IOException e) { + channel.close(); + throw e; + } + return channel; + } + + public static final UnixSocketChannel create() throws IOException { + UnixSocketChannel channel = new UnixSocketChannel(); + // channel.configureBlocking(true); + //channel.socket().setKeepAlive(true); + return channel; + } + + public static final UnixSocketChannel[] pair() throws IOException { + int[] sockets = {-1, -1}; + Native.socketpair(ProtocolFamily.PF_UNIX, Sock.SOCK_STREAM, 0, sockets); + return new UnixSocketChannel[] {new UnixSocketChannel(sockets[0]), new UnixSocketChannel(sockets[1])}; + } + + /** + * Create a UnixSocketChannel to wrap an existing file descriptor + * (presumably itself a UNIX socket). + * + * @param fd + * the file descriptor to wrap + * @return the new UnixSocketChannel instance + */ + public static final UnixSocketChannel fromFD(int fd) { + return fromFD(fd); + } + + private UnixSocketChannel() throws IOException { + super(Native.socket(ProtocolFamily.PF_UNIX, Sock.SOCK_STREAM, 0)); + state = State.IDLE; + } + + UnixSocketChannel(int fd) { + super(fd); + state = State.CONNECTED; + } + + UnixSocketChannel(int fd, UnixSocketAddress remote) { + super(fd); + state = State.CONNECTED; + remoteAddress = remote; + } + + private boolean doConnect(SockAddrUnix remote) throws IOException { + if (Native.connect(getFD(), remote, remote.length()) != 0) { + Errno error = Errno.valueOf(LastError.getLastError(jnr.ffi.Runtime.getSystemRuntime())); + + switch (error) { + case EAGAIN: + case EWOULDBLOCK: + return false; + + default: + throw new IOException(error.toString()); + } + } + + // configureBlocking(false); + return true; + } + + public boolean connect(UnixSocketAddress remote) throws IOException { + remoteAddress = remote; + if (!doConnect(remoteAddress.getStruct())) { + + state = State.CONNECTING; + return false; + + } else { + + state = State.CONNECTED; + return true; + } + } + + public boolean isConnected() { + //System.out.println("isConnected: " + state); + return state == State.CONNECTED; + } + + public boolean isConnectionPending() { + return state == State.CONNECTING; + } + + public boolean finishConnect() throws IOException { + switch (state) { + case CONNECTED: + return true; + + case CONNECTING: + if (!doConnect(remoteAddress.getStruct())) { + return false; + } + state = State.CONNECTED; + return true; + + default: + throw new IllegalStateException("socket is not waiting for connect to complete"); + } + } + + public final UnixSocketAddress getRemoteSocketAddress() { + if (state != State.CONNECTED) { + return null; + } + + if (remoteAddress != null) { + return remoteAddress; + } else { + remoteAddress = getpeername(getFD()); + return remoteAddress; + } + } + + public final UnixSocketAddress getLocalSocketAddress() { + if (state != State.CONNECTED) { + return null; + } + + if (localAddress != null) { + return localAddress; + } else { + localAddress = getsockname(getFD()); + return localAddress; + } + } + + /** + * Retrieves the credentials for this UNIX socket. If this socket channel is + * not in a connected state, this method will return null. + * + * See man unix 7; SCM_CREDENTIALS + * + * @throws UnsupportedOperationException + * if the underlying socket library doesn't support the + * SO_PEERCRED option + * + * @return the credentials of the remote; null if not connected + */ + public final Credentials getCredentials() { + if (state != State.CONNECTED) { + return null; + } + + return Credentials.getCredentials(getFD()); + } + + static UnixSocketAddress getpeername(int sockfd) { + UnixSocketAddress remote = new UnixSocketAddress(); + IntByReference len = new IntByReference(remote.getStruct().getMaximumLength()); + + if (Native.libc().getpeername(sockfd, remote.getStruct(), len) < 0) { + throw new Error(Native.getLastErrorString()); + } + + return remote; + } + + static UnixSocketAddress getsockname(int sockfd) { + UnixSocketAddress remote = new UnixSocketAddress(); + IntByReference len = new IntByReference(remote.getStruct().getMaximumLength()); + + if (Native.libc().getsockname(sockfd, remote.getStruct(), len) < 0) { + throw new Error(Native.getLastErrorString()); + } + + return remote; + } + + public boolean getKeepAlive() { + int ret = Native.getsockopt(getFD(), SocketLevel.SOL_SOCKET, SocketOption.SO_KEEPALIVE.intValue()); + return (ret == 1) ? true : false; + } + + public void setKeepAlive(boolean on) { + Native.setsockopt(getFD(), SocketLevel.SOL_SOCKET, SocketOption.SO_KEEPALIVE, on); + } + + public int getSoTimeout() { + return Native.getsockopt(getFD(), SocketLevel.SOL_SOCKET, SocketOption.SO_RCVTIMEO.intValue()); + } + + public void setSoTimeout(int timeout) { + Native.setsockopt(getFD(), SocketLevel.SOL_SOCKET, SocketOption.SO_RCVTIMEO, timeout); + } + + @Override + public SocketAddress getLocalAddress() throws IOException { + return getLocalSocketAddress(); + } + + @Override + public T getOption(java.net.SocketOption arg0) throws IOException { + System.err.println("getOption unsupported"); + throw new UnsupportedOperationException("getOption"); + } + + @Override + public Set> supportedOptions() { + System.err.println("supportedOptions unsupported"); + throw new UnsupportedOperationException("supportedOptions"); + } + + @Override + public SocketChannel bind(SocketAddress local) throws IOException { + System.err.println("bind unsupported"); + throw new UnsupportedOperationException("bind"); + } + + @Override + public boolean connect(SocketAddress remote) throws IOException { + if (remote instanceof UnixSocketAddress) { + return connect(((UnixSocketAddress) remote)); + } else { + throw new UnsupportedAddressTypeException(); + } + } + + @Override + public SocketAddress getRemoteAddress() throws IOException { + return getRemoteSocketAddress(); + } + + @Override + public long read(ByteBuffer[] dsts, int offset, int length) throws IOException { + System.err.println("read unsupported"); + throw new UnsupportedOperationException("read"); + } + + @Override + public SocketChannel setOption(java.net.SocketOption name, T value) throws IOException { + System.err.println("setOption unsupported"); + throw new UnsupportedOperationException("setOption"); + } + + @Override + public Socket socket() { + Option option = Option.apply((unisockets.SocketChannel) null); + + return new unisockets.Socket(this, option); + } + + @Override + public long write(ByteBuffer[] srcs, int offset, int length) throws IOException { + System.out.println("write buffers: " + srcs.length + " offset: " + offset + " length: " + length); + + if (state == State.CONNECTED) { + long result = 0; + int index = 0; + int remaining = 0; + + for (index = offset; index < length; index++) { + remaining += srcs[index].remaining(); + System.out.println("index: " + index + " remaining: " + remaining); + } + + System.out.println("remaining: " + remaining); + + + //ByteBuffer buffer = ByteBuffer.allocate(remaining); + + for (index = offset; index < length; index++) { + //buffer.put(srcs[index]); + System.err.println("bulk write: " + index); + //System.err.println(hexDump(srcs[index], "")); + result += write(srcs[index]); + } + + //buffer.position(0); + + //result = write(buffer); + System.out.println("finally written: " + result); + + return result; + } else if (state == State.IDLE) { + return 0; + } else { + throw new ClosedChannelException(); + } + } + + @Override + public int read(ByteBuffer dst) throws IOException { + System.out.println("read state: " + state); + + if (state == State.CONNECTED) { + return super.read(dst); + } else if (state == State.IDLE) { + return 0; + } else { + throw new ClosedChannelException(); + } + } + + @Override + public int write(ByteBuffer src) throws IOException { + System.out.println("write state: " + state); + + if (state == State.CONNECTED) { + int written = super.write(src); + System.out.println("finally written: " + written); + return written; + } else if (state == State.IDLE) { + return 0; + } else { + throw new ClosedChannelException(); + } + } +} 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 66219dc72..b88ebf95f 100644 --- a/src/test/java/com/github/dockerjava/netty/exec/AttachContainerCmdExecTest.java +++ b/src/test/java/com/github/dockerjava/netty/exec/AttachContainerCmdExecTest.java @@ -6,7 +6,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.io.PipedInputStream; import java.io.PipedOutputStream; import java.lang.reflect.Method; @@ -82,7 +84,7 @@ public void attachContainerWithStdin() throws Exception { String snippet = "hello world"; CreateContainerResponse container = dockerClient.createContainerCmd("busybox") - .withCmd("/bin/sh", "-c", "sleep 1 && read line && echo $line") + .withCmd("/bin/sh", "-c", "sleep 1 && read line && echo $line && sleep 9999") .withTty(false) .withStdinOpen(true) .exec(); @@ -104,20 +106,17 @@ public void onNext(Frame frame) { } }; - PipedOutputStream out = new PipedOutputStream(); - PipedInputStream in = new PipedInputStream(out); + InputStream stdin = new ByteArrayInputStream((snippet + "\n\r").getBytes()); dockerClient.attachContainerCmd(container.getId()) .withStdErr(true) .withStdOut(true) .withFollowStream(true) - .withStdIn(in) + .withStdIn(stdin) .exec(callback); - - out.write((snippet + "\n").getBytes()); - out.flush(); - + callback.awaitCompletion(15, SECONDS); + callback.close(); assertThat(callback.toString(), containsString(snippet)); diff --git a/src/test/java/com/github/dockerjava/netty/exec/AuthCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/AuthCmdExecTest.java index 690d478f4..2e77f456c 100644 --- a/src/test/java/com/github/dockerjava/netty/exec/AuthCmdExecTest.java +++ b/src/test/java/com/github/dockerjava/netty/exec/AuthCmdExecTest.java @@ -46,6 +46,7 @@ public void testAuth() throws Exception { @Test(expectedExceptions = UnauthorizedException.class) public void testAuthInvalid() throws Exception { - DockerClientBuilder.getInstance(config("garbage")).build().authCmd().exec(); + DockerClientBuilder.getInstance(config("garbage")).withDockerCmdExecFactory(dockerCmdExecFactory) + .build().authCmd().exec(); } } 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 52db7fe44..cee0173f7 100644 --- a/src/test/java/com/github/dockerjava/netty/exec/ExecStartCmdExecTest.java +++ b/src/test/java/com/github/dockerjava/netty/exec/ExecStartCmdExecTest.java @@ -1,6 +1,5 @@ package com.github.dockerjava.netty.exec; -import static com.github.dockerjava.core.RemoteApiVersion.VERSION_1_22; import static com.github.dockerjava.core.RemoteApiVersion.VERSION_1_23; import static com.github.dockerjava.utils.TestUtils.getVersion; import static org.hamcrest.MatcherAssert.assertThat; @@ -15,7 +14,6 @@ import java.security.SecureRandom; import java.util.concurrent.TimeUnit; -import com.github.dockerjava.core.RemoteApiVersion; import org.testng.ITestResult; import org.testng.annotations.AfterMethod; import org.testng.annotations.AfterTest; @@ -141,8 +139,9 @@ public void execStartAttachStdin() throws Exception { .exec(new ExecStartResultCallback(stdout, System.err)) .awaitCompletion(5, TimeUnit.SECONDS); - assertTrue(completed, "The process was not finished."); assertEquals(stdout.toString("UTF-8"), "STDIN\n"); + assertTrue(completed, "The process was not finished."); + } @Test() @@ -160,7 +159,7 @@ public void execStartAttachStdinToShell() throws Exception { dockerClient.startContainerCmd(container.getId()).exec(); - InputStream stdin = new ByteArrayInputStream("ls\n".getBytes()); + InputStream stdin = new ByteArrayInputStream("ls\nexit\n".getBytes()); ByteArrayOutputStream stdout = new ByteArrayOutputStream(); diff --git a/src/test/java/com/github/dockerjava/netty/exec/InfoCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/InfoCmdExecTest.java index 850f98a74..7cc8e7f09 100644 --- a/src/test/java/com/github/dockerjava/netty/exec/InfoCmdExecTest.java +++ b/src/test/java/com/github/dockerjava/netty/exec/InfoCmdExecTest.java @@ -55,7 +55,9 @@ public void info() throws DockerException { dockerClient.startContainerCmd(container.getId()).exec(); } + System.out.println(">>>>>>>>>>>>>>>>>>>>>"); Info dockerInfo = dockerClient.infoCmd().exec(); + System.out.println("<<<<<<<<<<<<<<<<<<<<<"); LOG.info(dockerInfo.toString()); assertTrue(dockerInfo.toString().contains("containers")); @@ -67,5 +69,7 @@ public void info() throws DockerException { assertTrue(dockerInfo.getNFd() > 0); assertTrue(dockerInfo.getNGoroutines() > 0); assertTrue(dockerInfo.getNCPU() > 0); + + System.out.println("test finished"); } }