From 96c4f531b55eb7b04e1f4e8a03d4047c81b2db46 Mon Sep 17 00:00:00 2001 From: Marcus Linke Date: Fri, 24 Jun 2016 21:25:18 +0200 Subject: [PATCH 1/4] Experiment with jnr-unixsocket and netty wip wip wip wip wip wip wip wip --- pom.xml | 32 +- .../dockerjava/netty/InvocationBuilder.java | 1 + .../netty/NettyDockerCmdExecFactory.java | 79 ++-- .../enxio/channels/NativeSocketChannel.java | 205 ++++++++++ .../jnr/unixsocket/UnixSocketChannel.java | 375 ++++++++++++++++++ .../exec/AttachContainerCmdExecTest.java | 15 +- .../netty/exec/ExecStartCmdExecTest.java | 7 +- .../netty/exec/InfoCmdExecTest.java | 4 + 8 files changed, 671 insertions(+), 47 deletions(-) create mode 100644 src/main/java/jnr/enxio/channels/NativeSocketChannel.java create mode 100644 src/main/java/jnr/unixsocket/UnixSocketChannel.java diff --git a/pom.xml b/pom.xml index b1b6d96a9..b2e67e91f 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,5 @@ - + 4.0.0 @@ -252,6 +253,16 @@ 4.12 test + + com.github.jnr + jnr-unixsocket + 0.12 + + + me.lessis + unisockets-core_2.11 + 0.1.0 + @@ -266,13 +277,13 @@ - - - - - - - + + + + + + + @@ -421,7 +432,7 @@ integration integration-auth - **/*Test.java + **/*ExecTest.java @@ -483,7 +494,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..676339512 100644 --- a/src/main/java/com/github/dockerjava/netty/NettyDockerCmdExecFactory.java +++ b/src/main/java/com/github/dockerjava/netty/NettyDockerCmdExecFactory.java @@ -1,5 +1,19 @@ 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.nio.channels.spi.SelectorProvider; +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 +53,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 +66,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 +107,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,36 +120,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.ChannelFactory; 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.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; +import jnr.enxio.channels.NativeSelectorProvider; +import jnr.unixsocket.UnixSocketAddress; +import jnr.unixsocket.UnixSocketChannel; /** * Experimental implementation of {@link DockerCmdExecFactory} that supports http connection hijacking that is needed to pass STDIN to the @@ -215,22 +216,50 @@ 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() { + final SelectorProvider nativeSelectorProvider = NativeSelectorProvider.getInstance(); + + EventLoopGroup nioEventLoopGroup = new NioEventLoopGroup(0, + new DefaultThreadFactory(threadPrefix), nativeSelectorProvider); + + ChannelFactory factory = new ChannelFactory() { + + @Override + public NioSocketChannel newChannel() { + try { + return new NioSocketChannel(UnixSocketChannel.create()); + } catch (IOException e) { + throw new RuntimeException(); + } + } + }; + + bootstrap.group(nioEventLoopGroup).channelFactory(factory) + .handler(new ChannelInitializer() { @Override - protected void initChannel(final UnixChannel channel) throws Exception { + protected void initChannel(final SocketChannel 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); + } + + UnixSocketAddress address = new UnixSocketAddress(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/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"); } } From 985fee2d95939f5c27f576bd83b4ea8d260d12e1 Mon Sep 17 00:00:00 2001 From: Marcus Linke Date: Sat, 12 Nov 2016 22:09:58 +0100 Subject: [PATCH 2/4] Use https://github.com/netty/netty/pull/6000 --- pom.xml | 49 ++++++++++++++----- .../netty/NettyDockerCmdExecFactory.java | 29 +++-------- 2 files changed, 45 insertions(+), 33 deletions(-) diff --git a/pom.xml b/pom.xml index b2e67e91f..39f84a4d2 100644 --- a/pom.xml +++ b/pom.xml @@ -74,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 @@ -87,6 +87,9 @@ 2.19.1 2.19.1 1.8 + + macos-x86_64 + @@ -246,6 +249,30 @@ netty-transport-native-epoll ${netty.version} linux-x86_64 + + + io.netty + netty-transport-native-unix-common + + + + + io.netty + netty-transport-native-kqueue + ${netty.version} + osx-x86_64 + + + io.netty + netty-transport-native-unix-common + + + + + io.netty + netty-transport-native-unix-common + ${netty.version} + osx-x86_64 junit @@ -253,7 +280,7 @@ 4.12 test - + com.github.jnr jnr-unixsocket 0.12 @@ -277,13 +304,13 @@ - - - - - - - + + + kr.motd.maven + os-maven-plugin + 1.4.0.Final + + @@ -430,7 +457,7 @@ true 1 integration - integration-auth + integration-auth **/*ExecTest.java @@ -494,7 +521,7 @@ true true false - src/test/resources/checkstyle/checkstyle-config.xml diff --git a/src/main/java/com/github/dockerjava/netty/NettyDockerCmdExecFactory.java b/src/main/java/com/github/dockerjava/netty/NettyDockerCmdExecFactory.java index 676339512..7e8873b5c 100644 --- a/src/main/java/com/github/dockerjava/netty/NettyDockerCmdExecFactory.java +++ b/src/main/java/com/github/dockerjava/netty/NettyDockerCmdExecFactory.java @@ -6,7 +6,6 @@ import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; -import java.nio.channels.spi.SelectorProvider; import java.security.Security; import javax.net.ssl.SSLEngine; @@ -122,20 +121,19 @@ import com.github.dockerjava.netty.exec.WaitContainerCmdExec; import io.netty.bootstrap.Bootstrap; -import io.netty.channel.ChannelFactory; import io.netty.channel.ChannelInitializer; import io.netty.channel.EventLoopGroup; +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.handler.codec.http.HttpClientCodec; import io.netty.handler.logging.LoggingHandler; import io.netty.handler.ssl.SslHandler; import io.netty.util.concurrent.DefaultThreadFactory; -import jnr.enxio.channels.NativeSelectorProvider; -import jnr.unixsocket.UnixSocketAddress; -import jnr.unixsocket.UnixSocketChannel; /** * Experimental implementation of {@link DockerCmdExecFactory} that supports http connection hijacking that is needed to pass STDIN to the @@ -221,24 +219,11 @@ private class UnixDomainSocketInitializer implements NettyInitializer { @Override public EventLoopGroup init(Bootstrap bootstrap, DockerClientConfig dockerClientConfig) { - final SelectorProvider nativeSelectorProvider = NativeSelectorProvider.getInstance(); - EventLoopGroup nioEventLoopGroup = new NioEventLoopGroup(0, - new DefaultThreadFactory(threadPrefix), nativeSelectorProvider); + EventLoopGroup nioEventLoopGroup = new KQueueEventLoopGroup(0, + new DefaultThreadFactory(threadPrefix)); - ChannelFactory factory = new ChannelFactory() { - - @Override - public NioSocketChannel newChannel() { - try { - return new NioSocketChannel(UnixSocketChannel.create()); - } catch (IOException e) { - throw new RuntimeException(); - } - } - }; - - bootstrap.group(nioEventLoopGroup).channelFactory(factory) + bootstrap.group(nioEventLoopGroup).channel(KQueueDomainSocketChannel.class) .handler(new ChannelInitializer() { @Override protected void initChannel(final SocketChannel channel) throws Exception { @@ -257,7 +242,7 @@ public DuplexChannel connect(Bootstrap bootstrap) throws InterruptedException { throw new RuntimeException("socket not found: " + path); } - UnixSocketAddress address = new UnixSocketAddress(path); + DomainSocketAddress address = new DomainSocketAddress(path); return (DuplexChannel) bootstrap.connect(address).sync().channel(); } From 42b55de455efdff7d94b3650c3735b4c9c55220b Mon Sep 17 00:00:00 2001 From: Marcus Linke Date: Wed, 14 Dec 2016 21:56:25 +0100 Subject: [PATCH 3/4] wip --- pom.xml | 32 ++++++++----------- .../netty/NettyDockerCmdExecFactory.java | 4 +-- .../netty/exec/AuthCmdExecTest.java | 3 +- 3 files changed, 17 insertions(+), 22 deletions(-) diff --git a/pom.xml b/pom.xml index 39f84a4d2..acf2444ef 100644 --- a/pom.xml +++ b/pom.xml @@ -244,18 +244,12 @@ netty-handler-proxy ${netty.version} - - io.netty - netty-transport-native-epoll - ${netty.version} - linux-x86_64 - - - io.netty - netty-transport-native-unix-common - - - + + + + + + io.netty netty-transport-native-kqueue @@ -304,13 +298,13 @@ - - - kr.motd.maven - os-maven-plugin - 1.4.0.Final - - + + + + + + + diff --git a/src/main/java/com/github/dockerjava/netty/NettyDockerCmdExecFactory.java b/src/main/java/com/github/dockerjava/netty/NettyDockerCmdExecFactory.java index 7e8873b5c..070bbd6b6 100644 --- a/src/main/java/com/github/dockerjava/netty/NettyDockerCmdExecFactory.java +++ b/src/main/java/com/github/dockerjava/netty/NettyDockerCmdExecFactory.java @@ -224,9 +224,9 @@ public EventLoopGroup init(Bootstrap bootstrap, DockerClientConfig dockerClientC new DefaultThreadFactory(threadPrefix)); bootstrap.group(nioEventLoopGroup).channel(KQueueDomainSocketChannel.class) - .handler(new ChannelInitializer() { + .handler(new ChannelInitializer() { @Override - protected void initChannel(final SocketChannel channel) throws Exception { + protected void initChannel(final KQueueDomainSocketChannel channel) throws Exception { channel.pipeline().addLast(new LoggingHandler(getClass())); channel.pipeline().addLast(new HttpClientCodec()); } 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(); } } From 3e7b06fc24e9f97901ccc5a5a039e6dcfc959c1f Mon Sep 17 00:00:00 2001 From: Marcus Linke Date: Wed, 14 Dec 2016 21:58:19 +0100 Subject: [PATCH 4/4] wip --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy