From 1c27a44cdc08317de60a27046103cbea9d2ee9bc Mon Sep 17 00:00:00 2001 From: Wiedemann Matthias Date: Tue, 26 May 2020 10:33:27 +0200 Subject: [PATCH 01/24] ssh module --- .../core/DefaultDockerClientConfig.java | 1 + docker-java-transport-ssh/Readme.md | 47 ++ .../alternativeSSHImplementations.md | 4 + docker-java-transport-ssh/pom.xml | 88 ++++ .../github/dockerjava/jsch/JschLogger.java | 50 +++ .../dockerjava/jsch/SSHDockerConfig.java | 85 ++++ .../dockerjava/jsch/SSHSocketFactory.java | 137 ++++++ .../github/dockerjava/jsch/SocatHandler.java | 124 ++++++ .../jsch/SsshWithOKDockerHttpClient.java | 415 ++++++++++++++++++ .../dockerjava/jsch/SocatHandlerTest.java | 97 ++++ .../jsch/SsshWithOKDockerHttpClientTest.java | 30 ++ .../src/test/resources/Dockerfile | 5 + .../src/test/resources/createDummyFile.bat | 6 + .../test/resources/simplelogger.properties | 7 + pom.xml | 1 + 15 files changed, 1097 insertions(+) create mode 100644 docker-java-transport-ssh/Readme.md create mode 100644 docker-java-transport-ssh/alternativeSSHImplementations.md create mode 100644 docker-java-transport-ssh/pom.xml create mode 100644 docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/JschLogger.java create mode 100644 docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SSHDockerConfig.java create mode 100644 docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SSHSocketFactory.java create mode 100644 docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SocatHandler.java create mode 100644 docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SsshWithOKDockerHttpClient.java create mode 100644 docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/SocatHandlerTest.java create mode 100644 docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/SsshWithOKDockerHttpClientTest.java create mode 100644 docker-java-transport-ssh/src/test/resources/Dockerfile create mode 100644 docker-java-transport-ssh/src/test/resources/createDummyFile.bat create mode 100644 docker-java-transport-ssh/src/test/resources/simplelogger.properties diff --git a/docker-java-core/src/main/java/com/github/dockerjava/core/DefaultDockerClientConfig.java b/docker-java-core/src/main/java/com/github/dockerjava/core/DefaultDockerClientConfig.java index be00cd7ea..4371dba1b 100644 --- a/docker-java-core/src/main/java/com/github/dockerjava/core/DefaultDockerClientConfig.java +++ b/docker-java-core/src/main/java/com/github/dockerjava/core/DefaultDockerClientConfig.java @@ -99,6 +99,7 @@ public class DefaultDockerClientConfig implements Serializable, DockerClientConf private URI checkDockerHostScheme(URI dockerHost) { switch (dockerHost.getScheme()) { + case "ssh": case "tcp": case "unix": case "npipe": diff --git a/docker-java-transport-ssh/Readme.md b/docker-java-transport-ssh/Readme.md new file mode 100644 index 000000000..46c2953ec --- /dev/null +++ b/docker-java-transport-ssh/Readme.md @@ -0,0 +1,47 @@ +# docker-java-transport-ssh + +Docker client implementation which uses [jsch](http://www.jcraft.com/jsch/) library, a java ssh implementation, to connect to the remote +docker host via ssh. + +While native docker cli supports ssh connections since Host docker version 18.09 [1](#1), with different options we can also make +it work for older versions. This library opens the ssh connection and then forwards the docker daemon socket to make it available to the http client. + +The ssh connection configuration relies on basic [ssh config file](https://www.ssh.com/ssh/config/) in ~/.ssh/config. + +## dockerd configurations + +On the remote host, one can connect to the docker daemon in several ways: + +* `docker system dial-stdio` +* `unix:///var/run/docker.sock` (default on linux) https://docs.docker.com/engine/reference/commandline/dockerd/#daemon-socket-option +* `npipe:////./pipe/docker_engine` (default on Windows) https://docs.docker.com/docker-for-windows/faqs/#how-do-i-connect-to-the-remote-docker-engine-api +* `unix:///var/run/docker.sock` (default on macos) https://docs.docker.com/docker-for-mac/faqs/#how-do-i-connect-to-the-remote-docker-engine-api +* tcp 2375 +* tcp with TLS + +## limitations + +__jsch__ + +Since jsch libary from jcraft does not support socket forwarding, a [fork of jsch](https://github.com/mwiede/jsch) is used. + +__windows__ + +Since forwarding socket of windows host is not supported, there is the workaround of starting socat to forward the docker socket to a local tcp port. + +Compare OpenSSH tickets: + * https://github.com/PowerShell/Win32-OpenSSH/issues/435 + * https://github.com/PowerShell/openssh-portable/pull/433 + +## connection variants: + +By setting flags in [SSHDockerConfig](src\main\java\com\github\dockerjava\jsch\SSHDockerConfig.java), one can control how the connection is made. + +* docker system dial-stdio (default) +* direct-streamlocal +* direct-tcpip +* socat + +## references + +[1] docker ssh support https://github.com/docker/cli/pull/1014 diff --git a/docker-java-transport-ssh/alternativeSSHImplementations.md b/docker-java-transport-ssh/alternativeSSHImplementations.md new file mode 100644 index 000000000..8e4adff7d --- /dev/null +++ b/docker-java-transport-ssh/alternativeSSHImplementations.md @@ -0,0 +1,4 @@ +alternative Java ssh implementations: +* https://github.com/apache/mina-sshd +* https://github.com/hierynomus/sshj +* https://github.com/jcabi/jcabi-ssh diff --git a/docker-java-transport-ssh/pom.xml b/docker-java-transport-ssh/pom.xml new file mode 100644 index 000000000..bf700a92e --- /dev/null +++ b/docker-java-transport-ssh/pom.xml @@ -0,0 +1,88 @@ + + + + docker-java-parent + com.github.docker-java + 3.2.2-SNAPSHOT + + 4.0.0 + + docker-java-transport-ssh + + + + ${project.groupId} + docker-java-core + ${project.version} + + + + com.squareup.okhttp3 + okhttp + 3.14.4 + + + + com.github.mwiede + jsch + 0.1.56 + + + + + org.junit.jupiter + junit-jupiter + 5.6.2 + test + + + + org.slf4j + slf4j-simple + 1.7.30 + test + + + + + + + dummy-file-creation + + + windows + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + test-compile + + + + + + + + + + + run + + + + + + + + + + + diff --git a/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/JschLogger.java b/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/JschLogger.java new file mode 100644 index 000000000..0a6c3f72b --- /dev/null +++ b/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/JschLogger.java @@ -0,0 +1,50 @@ +package com.github.dockerjava.jsch; + +import org.slf4j.LoggerFactory; + +public class JschLogger implements com.jcraft.jsch.Logger { + + private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(JschLogger.class); + + @Override + public boolean isEnabled(int level) { + switch (level) { + case DEBUG: + return LOGGER.isDebugEnabled() || LOGGER.isTraceEnabled(); + case INFO: + return LOGGER.isDebugEnabled(); + case WARN: + return LOGGER.isWarnEnabled(); + case ERROR: + case FATAL: + return LOGGER.isErrorEnabled(); + default: + throw new IllegalArgumentException("Unknown log level: " + level); + } + } + + @Override + public void log(int level, String message) { + switch (level) { + case DEBUG: + LOGGER.debug(message); + break; + case INFO: + LOGGER.info(message); + break; + case WARN: + LOGGER.warn(message); + break; + case ERROR: + LOGGER.error(message); + break; + case FATAL: + LOGGER.error("FATAL: {}", message); + break; + default: + throw new IllegalArgumentException("Unknown log level: " + level); + } + } + + +} diff --git a/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SSHDockerConfig.java b/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SSHDockerConfig.java new file mode 100644 index 000000000..9b92c0637 --- /dev/null +++ b/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SSHDockerConfig.java @@ -0,0 +1,85 @@ +package com.github.dockerjava.jsch; + +import com.jcraft.jsch.Session; +import okhttp3.Interceptor; + +import java.io.File; + +public class SSHDockerConfig { + + static final String VAR_RUN_DOCKER_SOCK = "/var/run/docker.sock"; + + private String socketPath = VAR_RUN_DOCKER_SOCK; + private Session session; + private File identityFile; + private Interceptor interceptor; + private boolean useSocat; + private boolean useTcp; + private boolean useSocket; + private Integer tcpPort; + + public Integer getTcpPort() { + return tcpPort; + } + + public void setTcpPort(Integer tcpPort) { + this.tcpPort = tcpPort; + } + + + public String getSocketPath() { + return socketPath; + } + + public void setSocketPath(String socketPath) { + this.socketPath = socketPath; + } + + public Session getSession() { + return session; + } + + public void setSession(Session session) { + this.session = session; + } + + public File getIdentityFile() { + return identityFile; + } + + public void setIdentityFile(File identityFile) { + this.identityFile = identityFile; + } + + public Interceptor getInterceptor() { + return interceptor; + } + + public void setInterceptor(Interceptor interceptor) { + this.interceptor = interceptor; + } + + public boolean isUseSocat() { + return useSocat; + } + + public void setUseSocat(boolean useSocat) { + this.useSocat = useSocat; + } + + public void setUseTcp(boolean useTcp) { + this.useTcp = useTcp; + } + + public boolean isUseTcp() { + return useTcp; + } + + public boolean isUseSocket() { + return useSocket; + } + + public void setUseSocket(boolean useSocket) { + this.useSocket = useSocket; + } +} diff --git a/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SSHSocketFactory.java b/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SSHSocketFactory.java new file mode 100644 index 000000000..6e05c7c39 --- /dev/null +++ b/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SSHSocketFactory.java @@ -0,0 +1,137 @@ +package com.github.dockerjava.jsch; + +import com.github.dockerjava.api.model.Container; +import com.github.dockerjava.api.model.ContainerPort; +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.ChannelDirectStreamLocal; +import com.jcraft.jsch.ChannelExec; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.Session; + +import javax.net.SocketFactory; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.util.Locale; +import java.util.Objects; + +public class SSHSocketFactory extends SocketFactory { + + private final Session session; + private final SSHDockerConfig config; + private Container socatContainer; + + SSHSocketFactory(Session session, SSHDockerConfig config) { + this.session = session; + this.config = config; + } + + @Override + public Socket createSocket() { + return new Socket() { + + private Channel channel; + private InputStream inputStream; + private OutputStream outputStream; + + @Override + public synchronized void close() throws IOException { + if (socatContainer != null) { + try { + SocatHandler.stopSocat(session, socatContainer.getId()); + } catch (JSchException e) { + throw new IOException(e); + } + } + channel.disconnect(); + } + + @Override + public void connect(SocketAddress endpoint) throws IOException { + connect(0); + } + + @Override + public void connect(SocketAddress endpoint, int timeout) throws IOException { + connect(timeout); + } + + @Override + public boolean isConnected() { + return channel.isConnected(); + } + + private void connect(int timeout) throws IOException { + try { + if (config.isUseTcp()) { + channel = session.getStreamForwarder("127.0.0.1", config.getTcpPort() != null ? config.getTcpPort() : 2375); + } else if (config.isUseSocat() || unixSocketOnWindows()) { + // forward docker socket via socat + socatContainer = SocatHandler.startSocat(session); + final ContainerPort containerPort = socatContainer.getPorts()[0]; + Objects.requireNonNull(containerPort); + channel = session.getStreamForwarder(containerPort.getIp(), containerPort.getPublicPort()); + } else if (config.isUseSocket()) { + // directly forward docker socket + channel = session.openChannel("direct-streamlocal@openssh.com"); + ((ChannelDirectStreamLocal) channel).setSocketPath(config.getSocketPath()); + } else { + // only 18.09 and up + channel = session.openChannel("exec"); + ((ChannelExec) channel).setCommand("docker system dial-stdio"); + } + + inputStream = channel.getInputStream(); + outputStream = channel.getOutputStream(); + channel.connect(timeout); + + } catch (JSchException e) { + throw new IOException(e); + } + } + + @Override + public InputStream getInputStream() { + return inputStream; + } + + @Override + public OutputStream getOutputStream() { + return outputStream; + } + }; + } + + private boolean unixSocketOnWindows() { + return config.isUseSocket() && config.getSocketPath().equalsIgnoreCase(SSHDockerConfig.VAR_RUN_DOCKER_SOCK) && isWindowsHost(); + } + + private boolean isWindowsHost() { + final String serverVersion = session.getServerVersion(); + return serverVersion.toLowerCase(Locale.getDefault()).contains("windows"); + } + + @Override + public Socket createSocket(String s, int i) { + throw new UnsupportedOperationException(); + } + + @Override + public Socket createSocket(String s, int i, InetAddress inetAddress, int i1) { + throw new UnsupportedOperationException(); + } + + @Override + public Socket createSocket(InetAddress inetAddress, int i) { + throw new UnsupportedOperationException(); + } + + @Override + public Socket createSocket(InetAddress inetAddress, int i, InetAddress inetAddress1, int i1) { + throw new UnsupportedOperationException(); + } + +} diff --git a/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SocatHandler.java b/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SocatHandler.java new file mode 100644 index 000000000..199fa2333 --- /dev/null +++ b/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SocatHandler.java @@ -0,0 +1,124 @@ +package com.github.dockerjava.jsch; + +import com.github.dockerjava.api.model.Container; +import com.github.dockerjava.api.model.ContainerPort; +import com.jcraft.jsch.ChannelExec; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.Session; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.nio.charset.Charset; + +public class SocatHandler { + + public static final int INTERNAL_SOCAT_PORT = 2377; + private static Logger logger = LoggerFactory.getLogger(SocatHandler.class); + + private SocatHandler() { + } + + public static Container startSocat(Session session) throws JSchException, IOException { + + final String command = " docker run -d " + + " -p 127.0.0.1:0:" + INTERNAL_SOCAT_PORT + + " -v /var/run/docker.sock:/var/run/docker.sock " + + " alpine/socat " + + " tcp-listen:" + INTERNAL_SOCAT_PORT + ",fork,reuseaddr unix-connect:/var/run/docker.sock"; + + final String containerId = runCommand(session, command); + + try { + final Container container = new Container(); + final Field id; + id = container.getClass().getDeclaredField("id"); + id.setAccessible(true); + id.set(container, containerId); + + /* + final String inspectionCommand = String.format("docker inspect " + + "--format=\"{{(index (index .NetworkSettings.Ports \\\"" + INTERNAL_SOCAT_PORT + "/tcp\\\") 0).HostPort}}\" %s", + containerId); + final String publishedPort = runCommand(session, inspectionCommand); + */ + + String portCommand = String.format("docker port %s", containerId); + final String portResult = runCommand(session, portCommand).trim(); + final String publishedPort = portResult.substring(portResult.lastIndexOf(':') + 1); + + final Field ports = container.getClass().getField("ports"); + final ContainerPort containerPort = new ContainerPort() + .withIp("127.0.0.1") + .withPrivatePort(INTERNAL_SOCAT_PORT) + .withPublicPort(Integer.valueOf(publishedPort.trim())); + ports.set(container, new ContainerPort[]{containerPort}); + return container; + + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new RuntimeException(e); + } + + } + + private static String runCommand(Session session, String command) throws JSchException, IOException { + + ChannelExec channel = (ChannelExec) session.openChannel("exec"); + try { + channel.setCommand(command); + logger.debug("running command: {}", command); + + final InputStream in = channel.getInputStream(); + final InputStream errStream = channel.getErrStream(); + + channel.connect(); + + while (true) { + + if (channel.isClosed()) { + + String response = null; + String errorMessage = null; + + while (in.available() > 0) { + byte[] tmp = new byte[1024]; + int i = in.read(tmp, 0, 1024); + if (i < 0) break; + response = new String(tmp, 0, i, Charset.defaultCharset()); + } + + while (errStream.available() > 0) { + byte[] tmp = new byte[1024]; + int i = errStream.read(tmp, 0, 1024); + if (i < 0) break; + errorMessage = new String(tmp, Charset.defaultCharset()); + } + + logger.debug("exit-status: {}", channel.getExitStatus()); + logger.debug("stderr:{}", errorMessage); + logger.debug("stdout: {}", response); + + if (channel.getExitStatus() == 0) { + return response; + } else { + throw new RuntimeException("command ended in exit-status:" + channel.getExitStatus() + + " with error message: " + errorMessage); + } + } + Thread.sleep(50); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException(e); + } finally { + channel.disconnect(); + } + } + + public static void stopSocat(Session session, String containerId) throws JSchException, IOException { + final String command = " docker stop " + containerId; + runCommand(session, command); + } +} diff --git a/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SsshWithOKDockerHttpClient.java b/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SsshWithOKDockerHttpClient.java new file mode 100644 index 000000000..fbb6930ac --- /dev/null +++ b/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SsshWithOKDockerHttpClient.java @@ -0,0 +1,415 @@ +package com.github.dockerjava.jsch; + +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.core.DockerHttpClient; +import com.github.dockerjava.core.SSLConfig; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.OpenSSHConfig; +import com.jcraft.jsch.Session; +import okhttp3.ConnectionPool; +import okhttp3.Dns; +import okhttp3.HttpUrl; +import okhttp3.Interceptor; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.RequestBody; +import okhttp3.ResponseBody; +import okio.BufferedSink; +import okio.Okio; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.X509TrustManager; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.UncheckedIOException; +import java.net.InetAddress; +import java.net.URI; +import java.security.cert.X509Certificate; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +public final class SsshWithOKDockerHttpClient implements DockerHttpClient { + + public static final class Factory { + + private DockerClientConfig dockerClientConfig = null; + + private Integer readTimeout = null; + + private Integer connectTimeout = null; + + private Boolean retryOnConnectionFailure = null; + + final SSHDockerConfig sshDockerConfig = new SSHDockerConfig(); + + public Factory dockerClientConfig(DockerClientConfig value) { + this.dockerClientConfig = value; + return this; + } + + public Factory readTimeout(Integer value) { + this.readTimeout = value; + return this; + } + + public Factory connectTimeout(Integer value) { + this.connectTimeout = value; + return this; + } + + Factory retryOnConnectionFailure(Boolean value) { + this.retryOnConnectionFailure = value; + return this; + } + + /** + * use socket and overwrite default socket path {@link SSHDockerConfig#VAR_RUN_DOCKER_SOCK} + * + * @param socketPath + * @return + */ + public Factory useSocket(String socketPath) { + this.sshDockerConfig.setUseSocket(true); + this.sshDockerConfig.setSocketPath(socketPath); + return this; + } + + /** + * pass {@link Session} if already connected + * + * @param session + * @return + */ + public Factory sshSession(Session session) { + this.sshDockerConfig.setSession(session); + return this; + } + + /** + * set identityFile for public key authentication + * + * @param identityFile + * @return + */ + public Factory identityFile(File identityFile) { + this.sshDockerConfig.setIdentityFile(identityFile); + return this; + } + + /** + * set identityFile from ~/.ssh/ folder for public key authentication + * + * @param privateKey private key filename + * @return + */ + public Factory identity(String privateKey) { + return identityFile(new File(System.getProperty("user.home") + File.separator + ".ssh" + File.separator + privateKey)); + } + + public Factory interceptor(Interceptor interceptor) { + this.sshDockerConfig.setInterceptor(interceptor); + return this; + } + + public Factory useSocket() { + this.sshDockerConfig.setUseSocket(true); + return this; + } + + public Factory useSocat() { + this.sshDockerConfig.setUseSocat(true); + return this; + } + + public Factory useTcp() { + this.sshDockerConfig.setUseTcp(true); + return this; + } + + public Factory useTcp(int port) { + this.sshDockerConfig.setUseTcp(true); + this.sshDockerConfig.setTcpPort(port); + return this; + } + + public SsshWithOKDockerHttpClient build() throws IOException, JSchException { + return new SsshWithOKDockerHttpClient( + dockerClientConfig, + readTimeout, + connectTimeout, + retryOnConnectionFailure, + sshDockerConfig); + } + } + + private static final String SOCKET_SUFFIX = ".socket"; + + final OkHttpClient client; + + final OkHttpClient streamingClient; + + private HttpUrl baseUrl; + + private Session session; + private boolean externalSession = false; + private final File identityFile; + + private SsshWithOKDockerHttpClient( + DockerClientConfig dockerClientConfig, + Integer readTimeout, + Integer connectTimeout, + Boolean retryOnConnectionFailure, + SSHDockerConfig sshDockerConfig) throws IOException, JSchException { + + OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder() + //.addNetworkInterceptor(new HijackingInterceptor()) + .readTimeout(0, TimeUnit.MILLISECONDS) + .retryOnConnectionFailure(true); + + if (sshDockerConfig.getInterceptor() != null) { + clientBuilder.addInterceptor(sshDockerConfig.getInterceptor()); + } + + if (readTimeout != null) { + clientBuilder.readTimeout(readTimeout, TimeUnit.MILLISECONDS); + } + + if (connectTimeout != null) { + clientBuilder.connectTimeout(connectTimeout, TimeUnit.MILLISECONDS); + } + + if (retryOnConnectionFailure != null) { + clientBuilder.retryOnConnectionFailure(retryOnConnectionFailure); + } + + this.session = sshDockerConfig.getSession(); + this.externalSession = sshDockerConfig.getSession() != null; + this.identityFile = sshDockerConfig.getIdentityFile(); + + URI dockerHost = dockerClientConfig.getDockerHost(); + + this.session = connectSSH(dockerHost, connectTimeout != null ? connectTimeout : 0); + + if ("ssh".equals(dockerHost.getScheme())) { + + final SSHSocketFactory socketFactory = new SSHSocketFactory(session, sshDockerConfig); + + clientBuilder.socketFactory(socketFactory); + + clientBuilder + .connectionPool(new ConnectionPool(0, 1, TimeUnit.SECONDS)) + .dns(hostname -> { + if (hostname.endsWith(SOCKET_SUFFIX)) { + return Collections.singletonList(InetAddress.getByAddress(hostname, new byte[]{0, 0, 0, 0})); + } else { + return Dns.SYSTEM.lookup(hostname); + } + }); + } else throw new IllegalArgumentException("this implementation only supports ssh connection scheme."); + + boolean isSSL = false; + SSLConfig sslConfig = dockerClientConfig.getSSLConfig(); + if (sslConfig != null) { + try { + SSLContext sslContext = sslConfig.getSSLContext(); + if (sslContext != null) { + isSSL = true; + clientBuilder.sslSocketFactory(sslContext.getSocketFactory(), new TrustAllX509TrustManager()); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + client = clientBuilder.build(); + + streamingClient = client.newBuilder().build(); + + initBaseUrl(dockerHost, isSSL); + } + + private void initBaseUrl(URI dockerHost, boolean isSSL) { + HttpUrl.Builder baseUrlBuilder; + + switch (dockerHost.getScheme()) { + case "ssh": + // der http Host und Port spielen keine Rolle, da das Socket geforwarded wird.... + baseUrlBuilder = new HttpUrl.Builder() + .scheme("http") + .host("127.0.0.1"); + break; + case "tcp": + baseUrlBuilder = new HttpUrl.Builder() + .scheme(isSSL ? "https" : "http") + .host(dockerHost.getHost()) + .port(dockerHost.getPort()); + break; + default: + baseUrlBuilder = HttpUrl.get(dockerHost.toString()).newBuilder(); + } + baseUrl = baseUrlBuilder.build(); + } + + private RequestBody toRequestBody(Request request) { + InputStream body = request.body(); + if (body != null) { + return new RequestBody() { + @Override + public MediaType contentType() { + return null; + } + + @Override + public void writeTo(BufferedSink sink) throws IOException { + sink.writeAll(Okio.source(body)); + } + }; + } + switch (request.method()) { + case "POST": + return RequestBody.create(null, ""); + default: + return null; + } + } + + @Override + public Response execute(Request request) { + String url = baseUrl.toString(); + if (url.endsWith("/") && request.path().startsWith("/")) { + url = url.substring(0, url.length() - 1); + } + okhttp3.Request.Builder requestBuilder = new okhttp3.Request.Builder() + .url(url + request.path()) + .tag(Request.class, request) + .method(request.method(), toRequestBody(request)); + + request.headers().forEach(requestBuilder::header); + + final OkHttpClient clientToUse; + + if (request.hijackedInput() == null) { + clientToUse = client; + } else { + clientToUse = streamingClient; + } + + try { + return new OkResponse(clientToUse.newCall(requestBuilder.build()).execute()); + } catch (IOException e) { + throw new UncheckedIOException("Error while executing " + request, e); + } + } + + @Override + public void close() throws IOException { + disconnectSSH(); + for (OkHttpClient clientToClose : new OkHttpClient[]{client, streamingClient}) { + clientToClose.dispatcher().cancelAll(); + clientToClose.dispatcher().executorService().shutdown(); + clientToClose.connectionPool().evictAll(); + } + } + + static class OkResponse implements Response { + + static final ThreadLocal CLOSING = ThreadLocal.withInitial(() -> false); + + private final okhttp3.Response response; + + OkResponse(okhttp3.Response response) { + this.response = response; + } + + @Override + public int getStatusCode() { + return response.code(); + } + + @Override + public Map> getHeaders() { + return response.headers().toMultimap(); + } + + @Override + public InputStream getBody() { + ResponseBody body = response.body(); + if (body == null) { + return null; + } + + return body.source().inputStream(); + } + + @Override + public void close() { + boolean previous = CLOSING.get(); + CLOSING.set(true); + try { + response.close(); + } finally { + CLOSING.set(previous); + } + } + } + + static class TrustAllX509TrustManager implements X509TrustManager { + @Override + public void checkClientTrusted(X509Certificate[] x509Certificates, String s) { + + } + + @Override + public void checkServerTrusted(X509Certificate[] x509Certificates, String s) { + + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + } + + private Session connectSSH(URI connectionString, int connectTimeout) throws IOException, JSchException { + + if (session != null && session.isConnected()) { + return session; + } + + final JSch jSch = new JSch(); + JSch.setLogger(new JschLogger()); + + final String configFile = System.getProperty("user.home") + File.separator + ".ssh" + File.separator + "config"; + final File file = new File(configFile); + if (file.exists()) { + final OpenSSHConfig openSSHConfig = OpenSSHConfig.parseFile(file.getAbsolutePath()); + jSch.setConfigRepository(openSSHConfig); + } + + final int port = connectionString.getPort() > 0 ? connectionString.getPort() : 22; + final Session newSession = jSch.getSession(connectionString.getUserInfo(), connectionString.getHost(), port); + + newSession.setConfig("StrictHostKeyChecking", "no"); + // https://stackoverflow.com/questions/10881981/sftp-connection-through-java-asking-for-weird-authentication + newSession.setConfig("PreferredAuthentications", "publickey"); + + if (identityFile != null) { + jSch.addIdentity(identityFile.getAbsolutePath()); + } + + newSession.connect(connectTimeout); + + return newSession; + } + + + private void disconnectSSH() { + if (!externalSession) { + session.disconnect(); + } + } +} diff --git a/docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/SocatHandlerTest.java b/docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/SocatHandlerTest.java new file mode 100644 index 000000000..adca10c44 --- /dev/null +++ b/docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/SocatHandlerTest.java @@ -0,0 +1,97 @@ +package com.github.dockerjava.jsch; + +import com.github.dockerjava.api.model.Container; +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.OpenSSHConfig; +import com.jcraft.jsch.Session; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Timeout; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.fail; + +/** + * This test relies on ~/.ssh/config which needs an entry for Host junit-host + *

+ * config could look like: + *

+ *

+ * Host junit-host
+ * HostName foo
+ * StrictHostKeyChecking no
+ * User bar
+ * IdentityFile ~/.ssh/some_private_key
+ * PreferredAuthentications publickey
+ * 
+ */ +class SocatHandlerTest { + + private static Session session; + private Container container; + + @BeforeAll + static void init() throws JSchException, IOException { + final JSch jSch = new JSch(); + JSch.setLogger(new JschLogger()); + final String configFile = System.getProperty("user.home") + File.separator + ".ssh" + File.separator + "config"; + final File file = new File(configFile); + if (file.exists()) { + final OpenSSHConfig openSSHConfig = OpenSSHConfig.parseFile(file.getAbsolutePath()); + jSch.setConfigRepository(openSSHConfig); + + } + session = jSch.getSession("vm"); + session.connect(500); + } + + @AfterAll + static void close() { + session.disconnect(); + } + + @AfterEach + void stopSocat() throws IOException, JSchException { + if (container != null) { + SocatHandler.stopSocat(session, container.getId()); + } + } + + @org.junit.jupiter.api.Test + @Timeout(value = 10) + void startSocatAndPing() throws IOException, JSchException { + container = SocatHandler.startSocat(session); + assertNotNull(container); + assertEquals("200", ping(container)); + } + + private String ping(Container container) throws JSchException, IOException { + final Channel streamForwarder = session.getStreamForwarder(container.getPorts()[0].getIp(), container.getPorts()[0].getPublicPort()); + streamForwarder.connect(100); + String cmd = "GET /_ping HTTP/1.0\r\n\r\n"; + final PrintWriter printWriter = new PrintWriter(streamForwarder.getOutputStream()); + printWriter.println(cmd); + printWriter.flush(); + BufferedReader reader = new BufferedReader(new InputStreamReader(streamForwarder.getInputStream())); + for (String line; (line = reader.readLine()) != null; ) { + final Matcher matcher = Pattern.compile("HTTP/\\d\\.\\d (\\d+) \\w+").matcher(line); + if (matcher.find()) { + return matcher.group(1); + } + } + fail("could not find response code"); + return null; + } +} diff --git a/docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/SsshWithOKDockerHttpClientTest.java b/docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/SsshWithOKDockerHttpClientTest.java new file mode 100644 index 000000000..1acf3385c --- /dev/null +++ b/docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/SsshWithOKDockerHttpClientTest.java @@ -0,0 +1,30 @@ +package com.github.dockerjava.jsch; + +import com.github.dockerjava.core.DefaultDockerClientConfig; +import com.github.dockerjava.core.DockerClientImpl; +import com.github.dockerjava.core.DockerHttpClient; +import com.jcraft.jsch.JSchException; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +class SsshWithOKDockerHttpClientTest { + + @Test + void ping() throws IOException, JSchException { + + final DefaultDockerClientConfig dockerClientConfig = DefaultDockerClientConfig.createDefaultConfigBuilder() + .withDockerHost("ssh://junit-host") + .build(); + + try (final DockerHttpClient dockerHttpClient = new SsshWithOKDockerHttpClient.Factory().dockerClientConfig(dockerClientConfig).build()) { + + final DockerClientImpl dockerClient = DockerClientImpl.getInstance(dockerClientConfig) + .withHttpClient(dockerHttpClient); + + assertDoesNotThrow(() -> dockerClient.pingCmd().exec()); + } + } +} diff --git a/docker-java-transport-ssh/src/test/resources/Dockerfile b/docker-java-transport-ssh/src/test/resources/Dockerfile new file mode 100644 index 000000000..36f155a78 --- /dev/null +++ b/docker-java-transport-ssh/src/test/resources/Dockerfile @@ -0,0 +1,5 @@ +FROM busybox +RUN mkdir /files +WORKDIR /files +ADD simplelogger.properties simplelogger.properties +ADD dummy.txt aLargeFile.txt diff --git a/docker-java-transport-ssh/src/test/resources/createDummyFile.bat b/docker-java-transport-ssh/src/test/resources/createDummyFile.bat new file mode 100644 index 000000000..4e06975ae --- /dev/null +++ b/docker-java-transport-ssh/src/test/resources/createDummyFile.bat @@ -0,0 +1,6 @@ +@echo off +Setlocal EnableDelayedExpansion +echo %random% > %1 + +for /l %%i in (1,1,1000) do echo !random! >> %1 + diff --git a/docker-java-transport-ssh/src/test/resources/simplelogger.properties b/docker-java-transport-ssh/src/test/resources/simplelogger.properties new file mode 100644 index 000000000..16e5516e3 --- /dev/null +++ b/docker-java-transport-ssh/src/test/resources/simplelogger.properties @@ -0,0 +1,7 @@ +# SLF4J's SimpleLogger configuration file +# Simple implementation of Logger that sends all enabled log messages, for all defined loggers, to System.err. + +# Default logging detail level for all instances of SimpleLogger. +# Must be one of ("trace", "debug", "info", "warn", or "error"). +# If not specified, defaults to "info". +org.slf4j.simpleLogger.defaultLogLevel=debug diff --git a/pom.xml b/pom.xml index 76a0c1bd9..0c2001462 100644 --- a/pom.xml +++ b/pom.xml @@ -93,6 +93,7 @@ docker-java-transport-jersey docker-java-transport-okhttp docker-java-transport-httpclient5 + docker-java-transport-ssh docker-java From a1eaac09f7412d9df0fec7f6fd56e7678ec90b0c Mon Sep 17 00:00:00 2001 From: Wiedemann Matthias Date: Wed, 27 May 2020 22:19:37 +0200 Subject: [PATCH 02/24] added ssh module for testing --- .../dockerjava/jsch/SSHSocketFactory.java | 11 ++- .../jsch/SsshWithOKDockerHttpClient.java | 4 +- ...atHandlerTest.java => SocatHandlerIT.java} | 4 +- .../jsch/SsshWithOKDockerHttpClientIT.java | 69 +++++++++++++++++++ .../jsch/SsshWithOKDockerHttpClientTest.java | 30 -------- docker-java/pom.xml | 7 ++ .../java/com/github/dockerjava/cmd/CmdIT.java | 47 ++++++++++++- 7 files changed, 136 insertions(+), 36 deletions(-) rename docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/{SocatHandlerTest.java => SocatHandlerIT.java} (95%) create mode 100644 docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/SsshWithOKDockerHttpClientIT.java delete mode 100644 docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/SsshWithOKDockerHttpClientTest.java diff --git a/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SSHSocketFactory.java b/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SSHSocketFactory.java index 6e05c7c39..6b41ae513 100644 --- a/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SSHSocketFactory.java +++ b/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SSHSocketFactory.java @@ -7,6 +7,8 @@ import com.jcraft.jsch.ChannelExec; import com.jcraft.jsch.JSchException; import com.jcraft.jsch.Session; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.net.SocketFactory; import java.io.IOException; @@ -20,6 +22,8 @@ public class SSHSocketFactory extends SocketFactory { + private static Logger logger = LoggerFactory.getLogger(SSHSocketFactory.class); + private final Session session; private final SSHDockerConfig config; private Container socatContainer; @@ -67,21 +71,26 @@ public boolean isConnected() { private void connect(int timeout) throws IOException { try { if (config.isUseTcp()) { - channel = session.getStreamForwarder("127.0.0.1", config.getTcpPort() != null ? config.getTcpPort() : 2375); + final int port = config.getTcpPort() != null ? config.getTcpPort() : 2375; + channel = session.getStreamForwarder("127.0.0.1", port); + logger.debug("Using channel direct-tcpip with 127.0.0.1:{}", port); } else if (config.isUseSocat() || unixSocketOnWindows()) { // forward docker socket via socat socatContainer = SocatHandler.startSocat(session); final ContainerPort containerPort = socatContainer.getPorts()[0]; Objects.requireNonNull(containerPort); channel = session.getStreamForwarder(containerPort.getIp(), containerPort.getPublicPort()); + logger.debug("Using channel direct-tcpip with socat on port {}", containerPort.getPublicPort()); } else if (config.isUseSocket()) { // directly forward docker socket channel = session.openChannel("direct-streamlocal@openssh.com"); ((ChannelDirectStreamLocal) channel).setSocketPath(config.getSocketPath()); + logger.debug("Using channel direct-streamlocal on {}", config.getSocketPath()); } else { // only 18.09 and up channel = session.openChannel("exec"); ((ChannelExec) channel).setCommand("docker system dial-stdio"); + logger.debug("Using dialer command"); } inputStream = channel.getInputStream(); diff --git a/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SsshWithOKDockerHttpClient.java b/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SsshWithOKDockerHttpClient.java index fbb6930ac..e862897fb 100644 --- a/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SsshWithOKDockerHttpClient.java +++ b/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SsshWithOKDockerHttpClient.java @@ -192,10 +192,10 @@ private SsshWithOKDockerHttpClient( URI dockerHost = dockerClientConfig.getDockerHost(); - this.session = connectSSH(dockerHost, connectTimeout != null ? connectTimeout : 0); - if ("ssh".equals(dockerHost.getScheme())) { + this.session = connectSSH(dockerHost, connectTimeout != null ? connectTimeout : 0); + final SSHSocketFactory socketFactory = new SSHSocketFactory(session, sshDockerConfig); clientBuilder.socketFactory(socketFactory); diff --git a/docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/SocatHandlerTest.java b/docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/SocatHandlerIT.java similarity index 95% rename from docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/SocatHandlerTest.java rename to docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/SocatHandlerIT.java index adca10c44..7bb20a6df 100644 --- a/docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/SocatHandlerTest.java +++ b/docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/SocatHandlerIT.java @@ -10,6 +10,7 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; import java.io.BufferedReader; import java.io.File; @@ -37,7 +38,8 @@ * PreferredAuthentications publickey * */ -class SocatHandlerTest { +@EnabledIfEnvironmentVariable(named = "DOCKER_HOST", matches = "ssh://.*") +class SocatHandlerIT { private static Session session; private Container container; diff --git a/docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/SsshWithOKDockerHttpClientIT.java b/docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/SsshWithOKDockerHttpClientIT.java new file mode 100644 index 000000000..b6f663d8f --- /dev/null +++ b/docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/SsshWithOKDockerHttpClientIT.java @@ -0,0 +1,69 @@ +package com.github.dockerjava.jsch; + +import com.github.dockerjava.core.DefaultDockerClientConfig; +import com.github.dockerjava.core.DockerClientImpl; +import com.github.dockerjava.core.DockerHttpClient; +import com.jcraft.jsch.JSchException; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +@EnabledIfEnvironmentVariable(named = "DOCKER_HOST", matches = "ssh://.*") +class SsshWithOKDockerHttpClientIT { + + @Test + void pingViaDialer() throws IOException, JSchException { + + final DefaultDockerClientConfig dockerClientConfig = DefaultDockerClientConfig.createDefaultConfigBuilder() + .withDockerHost("ssh://junit-host") + .build(); + + try (final DockerHttpClient dockerHttpClient = new SsshWithOKDockerHttpClient.Factory().dockerClientConfig(dockerClientConfig).build()) { + + final DockerClientImpl dockerClient = DockerClientImpl.getInstance(dockerClientConfig) + .withHttpClient(dockerHttpClient); + + assertDoesNotThrow(() -> dockerClient.pingCmd().exec()); + } + } + + @Test + void pingViaSocket() throws IOException, JSchException { + + final DefaultDockerClientConfig dockerClientConfig = DefaultDockerClientConfig.createDefaultConfigBuilder() + .withDockerHost("ssh://junit-host") + .build(); + + try (final DockerHttpClient dockerHttpClient = new SsshWithOKDockerHttpClient.Factory() + .useSocket() + .dockerClientConfig(dockerClientConfig).build()) { + + final DockerClientImpl dockerClient = DockerClientImpl.getInstance(dockerClientConfig) + .withHttpClient(dockerHttpClient); + + assertDoesNotThrow(() -> dockerClient.pingCmd().exec()); + } + } + + @Test + void pingViaSocat() throws IOException, JSchException { + + final DefaultDockerClientConfig dockerClientConfig = DefaultDockerClientConfig.createDefaultConfigBuilder() + .withDockerHost("ssh://junit-host") + .build(); + + try (final DockerHttpClient dockerHttpClient = new SsshWithOKDockerHttpClient.Factory() + .useSocat() + .dockerClientConfig(dockerClientConfig) + .build()) { + + final DockerClientImpl dockerClient = DockerClientImpl.getInstance(dockerClientConfig) + .withHttpClient(dockerHttpClient); + + assertDoesNotThrow(() -> dockerClient.pingCmd().exec()); + } + } +} diff --git a/docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/SsshWithOKDockerHttpClientTest.java b/docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/SsshWithOKDockerHttpClientTest.java deleted file mode 100644 index 1acf3385c..000000000 --- a/docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/SsshWithOKDockerHttpClientTest.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.github.dockerjava.jsch; - -import com.github.dockerjava.core.DefaultDockerClientConfig; -import com.github.dockerjava.core.DockerClientImpl; -import com.github.dockerjava.core.DockerHttpClient; -import com.jcraft.jsch.JSchException; -import org.junit.jupiter.api.Test; - -import java.io.IOException; - -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; - -class SsshWithOKDockerHttpClientTest { - - @Test - void ping() throws IOException, JSchException { - - final DefaultDockerClientConfig dockerClientConfig = DefaultDockerClientConfig.createDefaultConfigBuilder() - .withDockerHost("ssh://junit-host") - .build(); - - try (final DockerHttpClient dockerHttpClient = new SsshWithOKDockerHttpClient.Factory().dockerClientConfig(dockerClientConfig).build()) { - - final DockerClientImpl dockerClient = DockerClientImpl.getInstance(dockerClientConfig) - .withHttpClient(dockerHttpClient); - - assertDoesNotThrow(() -> dockerClient.pingCmd().exec()); - } - } -} diff --git a/docker-java/pom.xml b/docker-java/pom.xml index b9965ac46..1126fed97 100644 --- a/docker-java/pom.xml +++ b/docker-java/pom.xml @@ -51,6 +51,13 @@ ${project.version} test + + ${project.groupId} + docker-java-transport-ssh + ${project.version} + test + + ch.qos.logback logback-core diff --git a/docker-java/src/test/java/com/github/dockerjava/cmd/CmdIT.java b/docker-java/src/test/java/com/github/dockerjava/cmd/CmdIT.java index ecb66d089..37251e30f 100644 --- a/docker-java/src/test/java/com/github/dockerjava/cmd/CmdIT.java +++ b/docker-java/src/test/java/com/github/dockerjava/cmd/CmdIT.java @@ -2,20 +2,24 @@ import com.github.dockerjava.api.command.DelegatingDockerCmdExecFactory; import com.github.dockerjava.api.command.DockerCmdExecFactory; +import com.github.dockerjava.core.DefaultDockerClientConfig; import com.github.dockerjava.core.DefaultDockerCmdExecFactory; import com.github.dockerjava.core.DockerClientConfig; import com.github.dockerjava.core.DockerClientConfigAware; import com.github.dockerjava.httpclient5.ApacheDockerHttpClient; import com.github.dockerjava.jaxrs.JerseyDockerCmdExecFactory; +import com.github.dockerjava.jsch.SsshWithOKDockerHttpClient; import com.github.dockerjava.junit.DockerRule; import com.github.dockerjava.junit.category.Integration; import com.github.dockerjava.netty.NettyDockerCmdExecFactory; import com.github.dockerjava.okhttp.OkHttpDockerCmdExecFactory; +import com.jcraft.jsch.JSchException; import org.junit.Rule; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import java.io.IOException; import java.util.Arrays; /** @@ -25,6 +29,41 @@ @RunWith(Parameterized.class) public abstract class CmdIT { public enum FactoryType { + SSH(true) { + @Override + public DockerCmdExecFactory createExecFactory() { + class FakeFactory extends DelegatingDockerCmdExecFactory implements DockerClientConfigAware { + + private DefaultDockerCmdExecFactory dockerCmdExecFactory; + + @Override + public final DockerCmdExecFactory getDockerCmdExecFactory() { + return dockerCmdExecFactory; + } + + @Override + public void init(DockerClientConfig dockerClientConfig) { + + final DefaultDockerClientConfig defaultDockerClientConfig = DefaultDockerClientConfig.createDefaultConfigBuilder() + .withDockerHost("ssh://junit-host") + .build(); + + try { + dockerCmdExecFactory = new DefaultDockerCmdExecFactory( + new SsshWithOKDockerHttpClient.Factory() + .dockerClientConfig(defaultDockerClientConfig) + .build(), + defaultDockerClientConfig.getObjectMapper() + ); + } catch (IOException | JSchException e) { + throw new RuntimeException(e); + } + dockerCmdExecFactory.init(defaultDockerClientConfig); + } + } + return new FakeFactory(); + } + }, NETTY(true) { @Override public DockerCmdExecFactory createExecFactory() { @@ -91,7 +130,11 @@ public boolean supportsStdinAttach() { @Parameterized.Parameters(name = "{index}:{0}") public static Iterable data() { - return Arrays.asList(FactoryType.values()); + if (System.getenv("DOCKER_HOST").matches("ssh://.*")) { + return Arrays.asList(FactoryType.values()).subList(0, 1); + } else { + return Arrays.asList(FactoryType.values()).subList(1, FactoryType.values().length); + } } @Parameterized.Parameter @@ -102,6 +145,6 @@ public FactoryType getFactoryType() { } @Rule - public DockerRule dockerRule = new DockerRule( this); + public DockerRule dockerRule = new DockerRule(this); } From fed83d280ce452b65b43ad4269231ee7bc974cac Mon Sep 17 00:00:00 2001 From: Wiedemann Matthias Date: Thu, 28 May 2020 10:54:27 +0200 Subject: [PATCH 03/24] setup ssh --- .ci/setup_ssh_config.sh | 12 ++++++++++++ .github/workflows/ci.yml | 19 +++++-------------- 2 files changed, 17 insertions(+), 14 deletions(-) create mode 100644 .ci/setup_ssh_config.sh diff --git a/.ci/setup_ssh_config.sh b/.ci/setup_ssh_config.sh new file mode 100644 index 000000000..357996451 --- /dev/null +++ b/.ci/setup_ssh_config.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +set -exu + +whoami + +cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys + +ssh localhost + +exit 1 + diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 84f562a8a..4d096e949 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,7 @@ name: CI on: pull_request: {} - push: { branches: [ master ] } + push: { branches: [ ssh-ci ] } jobs: build: @@ -11,9 +11,8 @@ jobs: fail-fast: false matrix: include: - - { name: "default" } - - { name: "over TCP", dockerHost: "tcp://127.0.0.1:2375" } - - { name: "Docker 18.06.3", dockerVersion: "18.06.3~ce~3-0~ubuntu" } + - { name: "ssh-default" , dockerHost: "ssh://junit-host"} + #- { name: "ssh-19.03.9" , dockerVersion: "19.03.9~3-0~ubuntu-bionic", dockerHost: "ssh://junit-host"} steps: - uses: actions/checkout@v1 @@ -26,17 +25,9 @@ jobs: DOCKER_VERSION: ${{matrix.dockerVersion}} DOCKER_HOST: ${{matrix.dockerHost}} run: .ci/setup_docker.sh + - name: ssh config + run: .ci/setup_ssh_config.sh - name: Build with Maven env: DOCKER_HOST: ${{matrix.dockerHost}} run: ./mvnw --no-transfer-progress verify - - name: Aggregate test reports with ciMate - if: always() - continue-on-error: true - env: - CIMATE_PROJECT_ID: lodr9d83 - CIMATE_CI_KEY: "CI / ${{matrix.name}}" - run: | - wget -q https://get.cimate.io/release/linux/cimate - chmod +x cimate - ./cimate "**/TEST-*.xml" From 1b184df79beaeaaa48df6e95065910af961e8d99 Mon Sep 17 00:00:00 2001 From: Wiedemann Matthias Date: Thu, 28 May 2020 10:58:22 +0200 Subject: [PATCH 04/24] setup docker --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4d096e949..6a2f242f2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: fail-fast: false matrix: include: - - { name: "ssh-default" , dockerHost: "ssh://junit-host"} + - { name: "ssh-default" , clientDockerHost: "ssh://junit-host"} #- { name: "ssh-19.03.9" , dockerVersion: "19.03.9~3-0~ubuntu-bionic", dockerHost: "ssh://junit-host"} steps: @@ -29,5 +29,5 @@ jobs: run: .ci/setup_ssh_config.sh - name: Build with Maven env: - DOCKER_HOST: ${{matrix.dockerHost}} + DOCKER_HOST: ${{clientDockerHost}} run: ./mvnw --no-transfer-progress verify From 8c3b2d075208d05335f0828c132a7a9fdda15616 Mon Sep 17 00:00:00 2001 From: Wiedemann Matthias Date: Thu, 28 May 2020 11:10:30 +0200 Subject: [PATCH 05/24] try-and-error --- .ci/setup_ssh_config.sh | 4 +++- .github/workflows/ci.yml | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.ci/setup_ssh_config.sh b/.ci/setup_ssh_config.sh index 357996451..7b462dd97 100644 --- a/.ci/setup_ssh_config.sh +++ b/.ci/setup_ssh_config.sh @@ -4,7 +4,9 @@ set -exu whoami -cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys +which ssh + +ls -lsa ~/.ssh ssh localhost diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6a2f242f2..0d7385e28 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,5 +29,5 @@ jobs: run: .ci/setup_ssh_config.sh - name: Build with Maven env: - DOCKER_HOST: ${{clientDockerHost}} + DOCKER_HOST: ${{matrix.clientDockerHost}} run: ./mvnw --no-transfer-progress verify From d4e08af1a1a478ad6fd8ff6b7d50d536ab813dae Mon Sep 17 00:00:00 2001 From: Wiedemann Matthias Date: Thu, 28 May 2020 11:14:32 +0200 Subject: [PATCH 06/24] x permissions --- .ci/setup_ssh_config.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 .ci/setup_ssh_config.sh diff --git a/.ci/setup_ssh_config.sh b/.ci/setup_ssh_config.sh old mode 100644 new mode 100755 From 795ebd2273de43dce19c9cfbffc8068d38add777 Mon Sep 17 00:00:00 2001 From: Wiedemann Matthias Date: Thu, 28 May 2020 11:48:41 +0200 Subject: [PATCH 07/24] ssh config --- .ci/setup_ssh_config.sh | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/.ci/setup_ssh_config.sh b/.ci/setup_ssh_config.sh index 7b462dd97..810298899 100755 --- a/.ci/setup_ssh_config.sh +++ b/.ci/setup_ssh_config.sh @@ -2,13 +2,25 @@ set -exu -whoami +sudo apt-get upgrade +sudo apt-get update +sudo apt-get install openssh-server +sudo service ssh start -which ssh +mkdir -p ~/.ssh +cd ~/.ssh +ssh-keygen -q -t rsa -N "" -f jsch +cat jsch.pub >> authorized_keys +chmod 640 authorized_keys +sudo service ssh restart -ls -lsa ~/.ssh +cat <> config +Host junit-host + HostName localhost + StrictHostKeyChecking no + IdentityFile ~/.ssh/jsch + PreferredAuthentications publickey +EOT -ssh localhost - -exit 1 +ssh -q junit-host exit From 8ed2bf0b28bd2db48a4162530091db0114d1c095 Mon Sep 17 00:00:00 2001 From: Wiedemann Matthias Date: Thu, 28 May 2020 11:55:10 +0200 Subject: [PATCH 08/24] adding tmate --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0d7385e28..4983eae9e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,6 +25,8 @@ jobs: DOCKER_VERSION: ${{matrix.dockerVersion}} DOCKER_HOST: ${{matrix.dockerHost}} run: .ci/setup_docker.sh + - name: Setup tmate session + uses: mxschmitt/action-tmate@v2 - name: ssh config run: .ci/setup_ssh_config.sh - name: Build with Maven From c9e6d8bbdf71ce8a5d15cd987161d4210737048a Mon Sep 17 00:00:00 2001 From: Wiedemann Matthias Date: Thu, 28 May 2020 14:16:49 +0200 Subject: [PATCH 09/24] sshd already there --- .ci/setup_ssh_config.sh | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/.ci/setup_ssh_config.sh b/.ci/setup_ssh_config.sh index 810298899..2ca423453 100755 --- a/.ci/setup_ssh_config.sh +++ b/.ci/setup_ssh_config.sh @@ -2,17 +2,10 @@ set -exu -sudo apt-get upgrade -sudo apt-get update -sudo apt-get install openssh-server -sudo service ssh start - mkdir -p ~/.ssh cd ~/.ssh ssh-keygen -q -t rsa -N "" -f jsch cat jsch.pub >> authorized_keys -chmod 640 authorized_keys -sudo service ssh restart cat <> config Host junit-host @@ -22,5 +15,8 @@ Host junit-host PreferredAuthentications publickey EOT +chmod go-w $HOME $HOME/.ssh +chmod 600 $HOME/.ssh/authorized_keys + ssh -q junit-host exit From a59581526c93da98536159b9b4f7f6f84aecc5c7 Mon Sep 17 00:00:00 2001 From: Wiedemann Matthias Date: Fri, 29 May 2020 15:19:30 +0200 Subject: [PATCH 10/24] fixing swarm tests --- .../jsch/SsshWithOKDockerHttpClient.java | 4 +- .../dockerjava/cmd/BuildImageCmdIT.java | 7 ++- .../java/com/github/dockerjava/cmd/CmdIT.java | 50 ++++++++++++------- .../dockerjava/cmd/CreateContainerCmdIT.java | 7 ++- .../github/dockerjava/cmd/PullImageCmdIT.java | 5 +- .../github/dockerjava/cmd/PushImageCmdIT.java | 5 +- .../cmd/swarm/CreateServiceCmdExecIT.java | 5 +- .../dockerjava/junit/PrivateRegistryRule.java | 13 +++-- 8 files changed, 58 insertions(+), 38 deletions(-) diff --git a/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SsshWithOKDockerHttpClient.java b/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SsshWithOKDockerHttpClient.java index e862897fb..b31c34de9 100644 --- a/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SsshWithOKDockerHttpClient.java +++ b/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SsshWithOKDockerHttpClient.java @@ -209,7 +209,9 @@ private SsshWithOKDockerHttpClient( return Dns.SYSTEM.lookup(hostname); } }); - } else throw new IllegalArgumentException("this implementation only supports ssh connection scheme."); + } else { + throw new IllegalArgumentException("this implementation only supports ssh connection scheme."); + } boolean isSSL = false; SSLConfig sslConfig = dockerClientConfig.getSSLConfig(); diff --git a/docker-java/src/test/java/com/github/dockerjava/cmd/BuildImageCmdIT.java b/docker-java/src/test/java/com/github/dockerjava/cmd/BuildImageCmdIT.java index cd9b1ef9c..5ca3ac178 100644 --- a/docker-java/src/test/java/com/github/dockerjava/cmd/BuildImageCmdIT.java +++ b/docker-java/src/test/java/com/github/dockerjava/cmd/BuildImageCmdIT.java @@ -11,7 +11,6 @@ import net.jcip.annotations.NotThreadSafe; import org.apache.commons.io.FileUtils; import org.apache.commons.io.filefilter.TrueFileFilter; -import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; @@ -38,9 +37,9 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.emptyString; import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.emptyString; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; import static org.junit.Assume.assumeThat; @@ -52,8 +51,8 @@ public class BuildImageCmdIT extends CmdIT { public static final Logger LOG = LoggerFactory.getLogger(BuildImageCmd.class); - @ClassRule - public static PrivateRegistryRule REGISTRY = new PrivateRegistryRule(); + @Rule + public PrivateRegistryRule REGISTRY = new PrivateRegistryRule(this); @Rule public TemporaryFolder folder = new TemporaryFolder(new File("target/")); diff --git a/docker-java/src/test/java/com/github/dockerjava/cmd/CmdIT.java b/docker-java/src/test/java/com/github/dockerjava/cmd/CmdIT.java index 37251e30f..079f1e2b6 100644 --- a/docker-java/src/test/java/com/github/dockerjava/cmd/CmdIT.java +++ b/docker-java/src/test/java/com/github/dockerjava/cmd/CmdIT.java @@ -20,7 +20,8 @@ import org.junit.runners.Parameterized; import java.io.IOException; -import java.util.Arrays; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * @author Kanstantsin Shautsou @@ -29,7 +30,7 @@ @RunWith(Parameterized.class) public abstract class CmdIT { public enum FactoryType { - SSH(true) { + SSH(true, true) { @Override public DockerCmdExecFactory createExecFactory() { class FakeFactory extends DelegatingDockerCmdExecFactory implements DockerClientConfigAware { @@ -44,17 +45,30 @@ public final DockerCmdExecFactory getDockerCmdExecFactory() { @Override public void init(DockerClientConfig dockerClientConfig) { - final DefaultDockerClientConfig defaultDockerClientConfig = DefaultDockerClientConfig.createDefaultConfigBuilder() - .withDockerHost("ssh://junit-host") + final SsshWithOKDockerHttpClient.Factory factory = new SsshWithOKDockerHttpClient.Factory() + .dockerClientConfig(dockerClientConfig); + + final DefaultDockerClientConfig defaultDockerClientConfig = DefaultDockerClientConfig + .createDefaultConfigBuilder() + .withRegistryUrl(dockerClientConfig.getRegistryUrl()) .build(); - try { - dockerCmdExecFactory = new DefaultDockerCmdExecFactory( - new SsshWithOKDockerHttpClient.Factory() + if (!"ssh".equalsIgnoreCase(defaultDockerClientConfig.getDockerHost().getScheme())) { + throw new RuntimeException("This FactoryType is supposed to test ssh connections."); + } + + if (!dockerClientConfig.getDockerHost().equals(defaultDockerClientConfig.getDockerHost())) { + // Docker Host was overwritten i.e. from com.github.dockerjava.cmd.swarm.SwarmCmdIT.initializeDockerClient + // so we still use ssh connection and use inner binding to tcp + if ("tcp".equalsIgnoreCase(dockerClientConfig.getDockerHost().getScheme())) { + factory .dockerClientConfig(defaultDockerClientConfig) - .build(), - defaultDockerClientConfig.getObjectMapper() - ); + .useTcp(dockerClientConfig.getDockerHost().getPort()); + } + } + + try { + dockerCmdExecFactory = new DefaultDockerCmdExecFactory(factory.build(), defaultDockerClientConfig.getObjectMapper()); } catch (IOException | JSchException e) { throw new RuntimeException(e); } @@ -64,25 +78,25 @@ public void init(DockerClientConfig dockerClientConfig) { return new FakeFactory(); } }, - NETTY(true) { + NETTY(true, false) { @Override public DockerCmdExecFactory createExecFactory() { return new NettyDockerCmdExecFactory().withConnectTimeout(30 * 1000); } }, - JERSEY(false) { + JERSEY(false, false) { @Override public DockerCmdExecFactory createExecFactory() { return new JerseyDockerCmdExecFactory().withConnectTimeout(30 * 1000); } }, - OKHTTP(true) { + OKHTTP(true, false) { @Override public DockerCmdExecFactory createExecFactory() { return new OkHttpDockerCmdExecFactory().withConnectTimeout(30 * 1000); } }, - HTTPCLIENT5(true) { + HTTPCLIENT5(true, false) { @Override public DockerCmdExecFactory createExecFactory() { class FakeFactory extends DelegatingDockerCmdExecFactory implements DockerClientConfigAware { @@ -111,10 +125,12 @@ public void init(DockerClientConfig dockerClientConfig) { private final String subnetPrefix; private final boolean supportsStdinAttach; + private final boolean supportsSSH; - FactoryType(boolean supportsStdinAttach) { + FactoryType(boolean supportsStdinAttach, boolean supportsSSH) { this.subnetPrefix = "10." + (100 + ordinal()) + "."; this.supportsStdinAttach = supportsStdinAttach; + this.supportsSSH = supportsSSH; } public String getSubnetPrefix() { @@ -131,9 +147,9 @@ public boolean supportsStdinAttach() { @Parameterized.Parameters(name = "{index}:{0}") public static Iterable data() { if (System.getenv("DOCKER_HOST").matches("ssh://.*")) { - return Arrays.asList(FactoryType.values()).subList(0, 1); + return Stream.of(FactoryType.values()).filter(f -> f.supportsSSH).collect(Collectors.toList()); } else { - return Arrays.asList(FactoryType.values()).subList(1, FactoryType.values().length); + return Stream.of(FactoryType.values()).filter(f -> !f.supportsSSH).collect(Collectors.toList()); } } diff --git a/docker-java/src/test/java/com/github/dockerjava/cmd/CreateContainerCmdIT.java b/docker-java/src/test/java/com/github/dockerjava/cmd/CreateContainerCmdIT.java index 227c0acc9..7f42a8b24 100644 --- a/docker-java/src/test/java/com/github/dockerjava/cmd/CreateContainerCmdIT.java +++ b/docker-java/src/test/java/com/github/dockerjava/cmd/CreateContainerCmdIT.java @@ -30,7 +30,6 @@ import com.github.dockerjava.utils.TestUtils; import net.jcip.annotations.NotThreadSafe; import org.apache.commons.io.FileUtils; -import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -60,13 +59,13 @@ import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.emptyString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasItemInArray; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.emptyString; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.startsWith; @@ -78,8 +77,8 @@ public class CreateContainerCmdIT extends CmdIT { public static final Logger LOG = LoggerFactory.getLogger(CreateContainerCmdIT.class); - @ClassRule - public static PrivateRegistryRule REGISTRY = new PrivateRegistryRule(); + @Rule + public PrivateRegistryRule REGISTRY = new PrivateRegistryRule(this); @Rule public TemporaryFolder tempDir = new TemporaryFolder(new File("target/")); diff --git a/docker-java/src/test/java/com/github/dockerjava/cmd/PullImageCmdIT.java b/docker-java/src/test/java/com/github/dockerjava/cmd/PullImageCmdIT.java index 539a2b606..d6ae16fd9 100644 --- a/docker-java/src/test/java/com/github/dockerjava/cmd/PullImageCmdIT.java +++ b/docker-java/src/test/java/com/github/dockerjava/cmd/PullImageCmdIT.java @@ -8,7 +8,6 @@ import com.github.dockerjava.api.model.Info; import com.github.dockerjava.core.RemoteApiVersion; import com.github.dockerjava.junit.PrivateRegistryRule; -import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -26,8 +25,8 @@ public class PullImageCmdIT extends CmdIT { private static final Logger LOG = LoggerFactory.getLogger(PullImageCmdIT.class); - @ClassRule - public static PrivateRegistryRule REGISTRY = new PrivateRegistryRule(); + @Rule + public PrivateRegistryRule REGISTRY = new PrivateRegistryRule(this); @Rule public ExpectedException exception = ExpectedException.none(); diff --git a/docker-java/src/test/java/com/github/dockerjava/cmd/PushImageCmdIT.java b/docker-java/src/test/java/com/github/dockerjava/cmd/PushImageCmdIT.java index e05ca151e..fc0cff14c 100644 --- a/docker-java/src/test/java/com/github/dockerjava/cmd/PushImageCmdIT.java +++ b/docker-java/src/test/java/com/github/dockerjava/cmd/PushImageCmdIT.java @@ -7,7 +7,6 @@ import com.github.dockerjava.core.RemoteApiVersion; import com.github.dockerjava.junit.PrivateRegistryRule; import org.junit.Before; -import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -28,8 +27,8 @@ public class PushImageCmdIT extends CmdIT { public static final Logger LOG = LoggerFactory.getLogger(PushImageCmdIT.class); - @ClassRule - public static PrivateRegistryRule REGISTRY = new PrivateRegistryRule(); + @Rule + public PrivateRegistryRule REGISTRY = new PrivateRegistryRule(this); @Rule public ExpectedException exception = ExpectedException.none(); diff --git a/docker-java/src/test/java/com/github/dockerjava/cmd/swarm/CreateServiceCmdExecIT.java b/docker-java/src/test/java/com/github/dockerjava/cmd/swarm/CreateServiceCmdExecIT.java index 66ee0e836..022f53124 100644 --- a/docker-java/src/test/java/com/github/dockerjava/cmd/swarm/CreateServiceCmdExecIT.java +++ b/docker-java/src/test/java/com/github/dockerjava/cmd/swarm/CreateServiceCmdExecIT.java @@ -22,7 +22,6 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import org.junit.Before; -import org.junit.ClassRule; import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; @@ -43,8 +42,8 @@ public class CreateServiceCmdExecIT extends SwarmCmdIT { public static final Logger LOG = LoggerFactory.getLogger(CreateServiceCmdExecIT.class); private static final String SERVICE_NAME = "theservice"; - @ClassRule - public static PrivateRegistryRule REGISTRY = new PrivateRegistryRule(); + @Rule + public PrivateRegistryRule REGISTRY = new PrivateRegistryRule(this); @Rule public ExpectedException exception = ExpectedException.none(); diff --git a/docker-java/src/test/java/com/github/dockerjava/junit/PrivateRegistryRule.java b/docker-java/src/test/java/com/github/dockerjava/junit/PrivateRegistryRule.java index cd7989afd..fed173e21 100644 --- a/docker-java/src/test/java/com/github/dockerjava/junit/PrivateRegistryRule.java +++ b/docker-java/src/test/java/com/github/dockerjava/junit/PrivateRegistryRule.java @@ -7,6 +7,8 @@ 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.cmd.CmdIT; +import com.github.dockerjava.core.DefaultDockerClientConfig; import com.github.dockerjava.core.DockerClientBuilder; import org.junit.rules.ExternalResource; @@ -21,14 +23,15 @@ public class PrivateRegistryRule extends ExternalResource { - private final DockerClient dockerClient; + private final CmdIT testInstance; + private DockerClient dockerClient; private AuthConfig authConfig; private String containerId; - public PrivateRegistryRule() { - this.dockerClient = DockerClientBuilder.getInstance().build(); + public PrivateRegistryRule(CmdIT test) { + this.testInstance = test; } public AuthConfig getAuthConfig() { @@ -65,6 +68,10 @@ public String createTestImage(String tagName) { @Override protected void before() throws Throwable { + this.dockerClient = DockerClientBuilder.getInstance(DefaultDockerClientConfig.createDefaultConfigBuilder().build()) + .withDockerCmdExecFactory(testInstance.getFactoryType().createExecFactory()) + .build(); + int port = 5050; String imageName = "private-registry-image"; From 8131a6361cbc852e67f9111ee22c4138744e3744 Mon Sep 17 00:00:00 2001 From: Wiedemann Matthias Date: Fri, 29 May 2020 16:28:36 +0200 Subject: [PATCH 11/24] extracted client factory --- .../java/com/github/dockerjava/cmd/CmdIT.java | 49 +----------- .../dockerjava/cmd/SSHClientFactory.java | 74 +++++++++++++++++++ .../core/command/DockerfileFixture.java | 2 +- .../core/command/FrameReaderITest.java | 23 ++++-- 4 files changed, 93 insertions(+), 55 deletions(-) create mode 100644 docker-java/src/test/java/com/github/dockerjava/cmd/SSHClientFactory.java diff --git a/docker-java/src/test/java/com/github/dockerjava/cmd/CmdIT.java b/docker-java/src/test/java/com/github/dockerjava/cmd/CmdIT.java index 079f1e2b6..ea3b45957 100644 --- a/docker-java/src/test/java/com/github/dockerjava/cmd/CmdIT.java +++ b/docker-java/src/test/java/com/github/dockerjava/cmd/CmdIT.java @@ -2,24 +2,20 @@ import com.github.dockerjava.api.command.DelegatingDockerCmdExecFactory; import com.github.dockerjava.api.command.DockerCmdExecFactory; -import com.github.dockerjava.core.DefaultDockerClientConfig; import com.github.dockerjava.core.DefaultDockerCmdExecFactory; import com.github.dockerjava.core.DockerClientConfig; import com.github.dockerjava.core.DockerClientConfigAware; import com.github.dockerjava.httpclient5.ApacheDockerHttpClient; import com.github.dockerjava.jaxrs.JerseyDockerCmdExecFactory; -import com.github.dockerjava.jsch.SsshWithOKDockerHttpClient; import com.github.dockerjava.junit.DockerRule; import com.github.dockerjava.junit.category.Integration; import com.github.dockerjava.netty.NettyDockerCmdExecFactory; import com.github.dockerjava.okhttp.OkHttpDockerCmdExecFactory; -import com.jcraft.jsch.JSchException; import org.junit.Rule; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; -import java.io.IOException; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -29,53 +25,12 @@ @Category(Integration.class) @RunWith(Parameterized.class) public abstract class CmdIT { + public enum FactoryType { SSH(true, true) { @Override public DockerCmdExecFactory createExecFactory() { - class FakeFactory extends DelegatingDockerCmdExecFactory implements DockerClientConfigAware { - - private DefaultDockerCmdExecFactory dockerCmdExecFactory; - - @Override - public final DockerCmdExecFactory getDockerCmdExecFactory() { - return dockerCmdExecFactory; - } - - @Override - public void init(DockerClientConfig dockerClientConfig) { - - final SsshWithOKDockerHttpClient.Factory factory = new SsshWithOKDockerHttpClient.Factory() - .dockerClientConfig(dockerClientConfig); - - final DefaultDockerClientConfig defaultDockerClientConfig = DefaultDockerClientConfig - .createDefaultConfigBuilder() - .withRegistryUrl(dockerClientConfig.getRegistryUrl()) - .build(); - - if (!"ssh".equalsIgnoreCase(defaultDockerClientConfig.getDockerHost().getScheme())) { - throw new RuntimeException("This FactoryType is supposed to test ssh connections."); - } - - if (!dockerClientConfig.getDockerHost().equals(defaultDockerClientConfig.getDockerHost())) { - // Docker Host was overwritten i.e. from com.github.dockerjava.cmd.swarm.SwarmCmdIT.initializeDockerClient - // so we still use ssh connection and use inner binding to tcp - if ("tcp".equalsIgnoreCase(dockerClientConfig.getDockerHost().getScheme())) { - factory - .dockerClientConfig(defaultDockerClientConfig) - .useTcp(dockerClientConfig.getDockerHost().getPort()); - } - } - - try { - dockerCmdExecFactory = new DefaultDockerCmdExecFactory(factory.build(), defaultDockerClientConfig.getObjectMapper()); - } catch (IOException | JSchException e) { - throw new RuntimeException(e); - } - dockerCmdExecFactory.init(defaultDockerClientConfig); - } - } - return new FakeFactory(); + return new SSHClientFactory(); } }, NETTY(true, false) { diff --git a/docker-java/src/test/java/com/github/dockerjava/cmd/SSHClientFactory.java b/docker-java/src/test/java/com/github/dockerjava/cmd/SSHClientFactory.java new file mode 100644 index 000000000..19dba0431 --- /dev/null +++ b/docker-java/src/test/java/com/github/dockerjava/cmd/SSHClientFactory.java @@ -0,0 +1,74 @@ +package com.github.dockerjava.cmd; + +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.command.DelegatingDockerCmdExecFactory; +import com.github.dockerjava.api.command.DockerCmdExecFactory; +import com.github.dockerjava.core.DefaultDockerClientConfig; +import com.github.dockerjava.core.DefaultDockerCmdExecFactory; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.core.DockerClientConfigAware; +import com.github.dockerjava.core.DockerClientImpl; +import com.github.dockerjava.jsch.SsshWithOKDockerHttpClient; +import com.jcraft.jsch.JSchException; + +import java.io.IOException; + +public class SSHClientFactory extends DelegatingDockerCmdExecFactory implements DockerClientConfigAware { + + private DefaultDockerCmdExecFactory dockerCmdExecFactory; + private SsshWithOKDockerHttpClient httpClient; + + @Override + public final DockerCmdExecFactory getDockerCmdExecFactory() { + return dockerCmdExecFactory; + } + + @Override + public void init(DockerClientConfig dockerClientConfig) { + + final SsshWithOKDockerHttpClient.Factory factory = new SsshWithOKDockerHttpClient.Factory() + .connectTimeout(30 * 1000) + .dockerClientConfig(dockerClientConfig); + + final DefaultDockerClientConfig defaultDockerClientConfig = DefaultDockerClientConfig + .createDefaultConfigBuilder() + .withRegistryUrl(dockerClientConfig.getRegistryUrl()) + .build(); + + if (!"ssh".equalsIgnoreCase(defaultDockerClientConfig.getDockerHost().getScheme())) { + throw new RuntimeException("This FactoryType is supposed to test ssh connections."); + } + + if (!dockerClientConfig.getDockerHost().equals(defaultDockerClientConfig.getDockerHost())) { + // Docker Host was overwritten i.e. from com.github.dockerjava.cmd.swarm.SwarmCmdIT.initializeDockerClient + // so we still use ssh connection and use inner binding to tcp + if ("tcp".equalsIgnoreCase(dockerClientConfig.getDockerHost().getScheme())) { + factory + .dockerClientConfig(defaultDockerClientConfig) + .useTcp(dockerClientConfig.getDockerHost().getPort()); + } + } + + try { + httpClient = factory.build(); + dockerCmdExecFactory = new DefaultDockerCmdExecFactory(httpClient, defaultDockerClientConfig.getObjectMapper()); + } catch (IOException | JSchException e) { + throw new RuntimeException(e); + } + dockerCmdExecFactory.init(defaultDockerClientConfig); + } + + public SSHClientFactory withDockerClientConfig(DockerClientConfig config) { + init(config); + return this; + } + + public DockerClient build() { + if (httpClient == null) { + init(DefaultDockerClientConfig.createDefaultConfigBuilder() + .withRegistryUrl("https://index.docker.io/v1/") + .build()); + } + return DockerClientImpl.getInstance().withHttpClient(httpClient); + } +} diff --git a/docker-java/src/test/java/com/github/dockerjava/core/command/DockerfileFixture.java b/docker-java/src/test/java/com/github/dockerjava/core/command/DockerfileFixture.java index 104ea9f6f..d7f3f03b1 100644 --- a/docker-java/src/test/java/com/github/dockerjava/core/command/DockerfileFixture.java +++ b/docker-java/src/test/java/com/github/dockerjava/core/command/DockerfileFixture.java @@ -38,7 +38,7 @@ public void open() throws Exception { Image lastCreatedImage = client.listImagesCmd().exec().get(0); - repository = lastCreatedImage.getRepoTags()[0]; + repository = lastCreatedImage.getId(); LOGGER.info("created {} {}", lastCreatedImage.getId(), repository); diff --git a/docker-java/src/test/java/com/github/dockerjava/core/command/FrameReaderITest.java b/docker-java/src/test/java/com/github/dockerjava/core/command/FrameReaderITest.java index 16c456164..b3268ebd2 100644 --- a/docker-java/src/test/java/com/github/dockerjava/core/command/FrameReaderITest.java +++ b/docker-java/src/test/java/com/github/dockerjava/core/command/FrameReaderITest.java @@ -4,6 +4,8 @@ import com.github.dockerjava.api.async.ResultCallback; import com.github.dockerjava.api.model.Frame; import com.github.dockerjava.api.model.StreamType; +import com.github.dockerjava.cmd.SSHClientFactory; +import com.github.dockerjava.core.DefaultDockerClientConfig; import com.github.dockerjava.core.DockerClientBuilder; import com.github.dockerjava.junit.category.Integration; import org.junit.After; @@ -30,11 +32,18 @@ public class FrameReaderITest { @Before public void beforeTest() throws Exception { - dockerClient = DockerClientBuilder.getInstance().build(); + dockerClient = getDockerClient(); dockerfileFixture = new DockerfileFixture(dockerClient, "frameReaderDockerfile"); dockerfileFixture.open(); } + private DockerClient getDockerClient() { + if ("ssh".equalsIgnoreCase(DefaultDockerClientConfig.createDefaultConfigBuilder().build().getDockerHost().getScheme())) { + return new SSHClientFactory().build(); + } + return DockerClientBuilder.getInstance().build(); + } + @After public void deleteDockerContainerImage() throws Exception { dockerfileFixture.close(); @@ -47,13 +56,13 @@ public void canCloseFrameReaderAndReadExpectedLines() throws Exception { // wait for the container to be successfully executed int exitCode = dockerClient.waitContainerCmd(dockerfileFixture.getContainerId()) - .start().awaitStatusCode(); + .start().awaitStatusCode(); assertEquals(0, exitCode); final List loggingFrames = getLoggingFrames(); final Frame outFrame = new Frame(StreamType.STDOUT, "to stdout\n".getBytes()); final Frame errFrame = new Frame(StreamType.STDERR, "to stderr\n".getBytes()); - + assertThat(loggingFrames, containsInAnyOrder(outFrame, errFrame)); assertThat(loggingFrames, hasSize(2)); } @@ -63,10 +72,10 @@ private List getLoggingFrames() throws Exception { FrameReaderITestCallback collectFramesCallback = new FrameReaderITestCallback(); dockerClient.logContainerCmd(dockerfileFixture.getContainerId()).withStdOut(true).withStdErr(true) - .withTailAll() - // we can't follow stream here as it blocks reading from resulting InputStream infinitely - // .withFollowStream() - .exec(collectFramesCallback).awaitCompletion(); + .withTailAll() + // we can't follow stream here as it blocks reading from resulting InputStream infinitely + // .withFollowStream() + .exec(collectFramesCallback).awaitCompletion(); return collectFramesCallback.frames; } From f8911b678a31742b99a7edc63f6e810447e684f4 Mon Sep 17 00:00:00 2001 From: Wiedemann Matthias Date: Fri, 29 May 2020 18:54:25 +0200 Subject: [PATCH 12/24] using DOCKER_HOST setting by default --- .../dockerjava/jsch/SsshWithOKDockerHttpClient.java | 2 ++ .../com/github/dockerjava/jsch/SocatHandlerIT.java | 8 +++++--- .../jsch/SsshWithOKDockerHttpClientIT.java | 12 +++--------- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SsshWithOKDockerHttpClient.java b/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SsshWithOKDockerHttpClient.java index b31c34de9..92f0c3d5c 100644 --- a/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SsshWithOKDockerHttpClient.java +++ b/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SsshWithOKDockerHttpClient.java @@ -30,6 +30,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.concurrent.TimeUnit; public final class SsshWithOKDockerHttpClient implements DockerHttpClient { @@ -137,6 +138,7 @@ public Factory useTcp(int port) { } public SsshWithOKDockerHttpClient build() throws IOException, JSchException { + Objects.requireNonNull(dockerClientConfig, "dockerClientConfig not provided"); return new SsshWithOKDockerHttpClient( dockerClientConfig, readTimeout, diff --git a/docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/SocatHandlerIT.java b/docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/SocatHandlerIT.java index 7bb20a6df..4d3f8d889 100644 --- a/docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/SocatHandlerIT.java +++ b/docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/SocatHandlerIT.java @@ -17,6 +17,8 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; +import java.net.URI; +import java.net.URISyntaxException; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -25,7 +27,7 @@ import static org.junit.jupiter.api.Assertions.fail; /** - * This test relies on ~/.ssh/config which needs an entry for Host junit-host + * This test relies on ~/.ssh/config which needs an entry for Host setup in the env variable DOCKER_HOST *

* config could look like: *

@@ -45,7 +47,7 @@ class SocatHandlerIT { private Container container; @BeforeAll - static void init() throws JSchException, IOException { + static void init() throws JSchException, IOException, URISyntaxException { final JSch jSch = new JSch(); JSch.setLogger(new JschLogger()); final String configFile = System.getProperty("user.home") + File.separator + ".ssh" + File.separator + "config"; @@ -55,7 +57,7 @@ static void init() throws JSchException, IOException { jSch.setConfigRepository(openSSHConfig); } - session = jSch.getSession("vm"); + session = jSch.getSession(new URI(System.getenv("DOCKER_HOST")).getHost()); session.connect(500); } diff --git a/docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/SsshWithOKDockerHttpClientIT.java b/docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/SsshWithOKDockerHttpClientIT.java index b6f663d8f..781a4212d 100644 --- a/docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/SsshWithOKDockerHttpClientIT.java +++ b/docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/SsshWithOKDockerHttpClientIT.java @@ -17,9 +17,7 @@ class SsshWithOKDockerHttpClientIT { @Test void pingViaDialer() throws IOException, JSchException { - final DefaultDockerClientConfig dockerClientConfig = DefaultDockerClientConfig.createDefaultConfigBuilder() - .withDockerHost("ssh://junit-host") - .build(); + final DefaultDockerClientConfig dockerClientConfig = DefaultDockerClientConfig.createDefaultConfigBuilder().build(); try (final DockerHttpClient dockerHttpClient = new SsshWithOKDockerHttpClient.Factory().dockerClientConfig(dockerClientConfig).build()) { @@ -33,9 +31,7 @@ void pingViaDialer() throws IOException, JSchException { @Test void pingViaSocket() throws IOException, JSchException { - final DefaultDockerClientConfig dockerClientConfig = DefaultDockerClientConfig.createDefaultConfigBuilder() - .withDockerHost("ssh://junit-host") - .build(); + final DefaultDockerClientConfig dockerClientConfig = DefaultDockerClientConfig.createDefaultConfigBuilder().build(); try (final DockerHttpClient dockerHttpClient = new SsshWithOKDockerHttpClient.Factory() .useSocket() @@ -51,9 +47,7 @@ void pingViaSocket() throws IOException, JSchException { @Test void pingViaSocat() throws IOException, JSchException { - final DefaultDockerClientConfig dockerClientConfig = DefaultDockerClientConfig.createDefaultConfigBuilder() - .withDockerHost("ssh://junit-host") - .build(); + final DefaultDockerClientConfig dockerClientConfig = DefaultDockerClientConfig.createDefaultConfigBuilder().build(); try (final DockerHttpClient dockerHttpClient = new SsshWithOKDockerHttpClient.Factory() .useSocat() From effaa50747db2cc102207017844da64b982debe8 Mon Sep 17 00:00:00 2001 From: Wiedemann Matthias Date: Fri, 29 May 2020 19:21:47 +0200 Subject: [PATCH 13/24] rm tmate --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4983eae9e..e7df6c230 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,8 +25,8 @@ jobs: DOCKER_VERSION: ${{matrix.dockerVersion}} DOCKER_HOST: ${{matrix.dockerHost}} run: .ci/setup_docker.sh - - name: Setup tmate session - uses: mxschmitt/action-tmate@v2 + #- name: Setup tmate session + # uses: mxschmitt/action-tmate@v2 - name: ssh config run: .ci/setup_ssh_config.sh - name: Build with Maven From b683ca106effacb2122bfe0282a633a527421a5f Mon Sep 17 00:00:00 2001 From: Wiedemann Matthias Date: Wed, 24 Jun 2020 22:17:27 +0200 Subject: [PATCH 14/24] merged ssh-ci --- .github/workflows/ci.yml | 20 ++++++--- .../java/com/github/dockerjava/cmd/CmdIT.java | 21 +-------- .../dockerjava/cmd/SSHClientFactory.java | 37 ++++++++------- .../dockerjava/junit/PrivateRegistryRule.java | 45 +++++++++---------- 4 files changed, 55 insertions(+), 68 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7d3890309..43807b745 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,7 @@ name: CI on: pull_request: {} - push: { branches: [ ssh-ci ] } + push: { branches: [ master ] } jobs: build: @@ -11,8 +11,11 @@ jobs: fail-fast: false matrix: include: + - { name: "default" } + - { name: "over TCP", dockerHost: "tcp://127.0.0.1:2375" } + - { name: "Docker 18.06.3", dockerVersion: "18.06.3~ce~3-0~ubuntu" } - { name: "ssh-default" , clientDockerHost: "ssh://junit-host"} - #- { name: "ssh-19.03.9" , dockerVersion: "19.03.9~3-0~ubuntu-bionic", dockerHost: "ssh://junit-host"} + - { name: "ssh-19.03.9" , dockerVersion: "19.03.9~3-0~ubuntu-bionic", clientDockerHost: "ssh://junit-host"} steps: - uses: actions/checkout@v2 @@ -25,11 +28,16 @@ jobs: DOCKER_VERSION: ${{matrix.dockerVersion}} DOCKER_HOST: ${{matrix.dockerHost}} run: .ci/setup_docker.sh - #- name: Setup tmate session - # uses: mxschmitt/action-tmate@v2 - - name: ssh config + - name: Create ssh config run: .ci/setup_ssh_config.sh - - name: Build with Maven + if: startsWith(matrix.name,'ssh') + - name: Build with Maven (SSH) env: DOCKER_HOST: ${{matrix.clientDockerHost}} run: ./mvnw --no-transfer-progress verify + if: startsWith(matrix.name,'ssh') + - name: Build with Maven + env: + DOCKER_HOST: ${{matrix.dockerHost}} + run: ./mvnw --no-transfer-progress verify + if: !startsWith(matrix.name,'ssh') diff --git a/docker-java/src/test/java/com/github/dockerjava/cmd/CmdIT.java b/docker-java/src/test/java/com/github/dockerjava/cmd/CmdIT.java index 1e0bc811f..718163763 100644 --- a/docker-java/src/test/java/com/github/dockerjava/cmd/CmdIT.java +++ b/docker-java/src/test/java/com/github/dockerjava/cmd/CmdIT.java @@ -1,13 +1,11 @@ package com.github.dockerjava.cmd; -import com.github.dockerjava.core.DefaultDockerClientConfig; import com.github.dockerjava.core.DockerClientBuilder; import com.github.dockerjava.core.DockerClientConfig; import com.github.dockerjava.core.DockerClientImpl; import com.github.dockerjava.core.DockerRule; import com.github.dockerjava.httpclient5.ApacheDockerHttpClient; import com.github.dockerjava.jaxrs.JerseyDockerHttpClient; -import com.github.dockerjava.jsch.SsshWithOKDockerHttpClient; import com.github.dockerjava.junit.category.Integration; import com.github.dockerjava.netty.NettyDockerCmdExecFactory; import com.github.dockerjava.okhttp.OkDockerHttpClient; @@ -18,7 +16,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import java.util.Arrays; /** * @author Kanstantsin Shautsou @@ -31,23 +28,9 @@ public enum FactoryType { SSH(true, true) { @Override public DockerClientImpl createDockerClient(DockerClientConfig config) { - final DefaultDockerClientConfig dockerClientConfig = DefaultDockerClientConfig.createDefaultConfigBuilder() - .withDockerHost("ssh://junit-host") + return (DockerClientImpl) new SSHClientFactory(). + withDockerClientConfig(config) .build(); - try { - return (DockerClientImpl) DockerClientBuilder.getInstance(config) - .withDockerHttpClient( - new TrackingDockerHttpClient( - new SsshWithOKDockerHttpClient.Factory() - .dockerClientConfig(dockerClientConfig) - .connectTimeout(30 * 100) - .build() - ) - ) - .build(); - } catch (Exception e) { - throw new RuntimeException(e); - } } }, NETTY(true, false) { diff --git a/docker-java/src/test/java/com/github/dockerjava/cmd/SSHClientFactory.java b/docker-java/src/test/java/com/github/dockerjava/cmd/SSHClientFactory.java index 19dba0431..260a1f4ed 100644 --- a/docker-java/src/test/java/com/github/dockerjava/cmd/SSHClientFactory.java +++ b/docker-java/src/test/java/com/github/dockerjava/cmd/SSHClientFactory.java @@ -1,27 +1,19 @@ package com.github.dockerjava.cmd; import com.github.dockerjava.api.DockerClient; -import com.github.dockerjava.api.command.DelegatingDockerCmdExecFactory; -import com.github.dockerjava.api.command.DockerCmdExecFactory; import com.github.dockerjava.core.DefaultDockerClientConfig; -import com.github.dockerjava.core.DefaultDockerCmdExecFactory; +import com.github.dockerjava.core.DockerClientBuilder; import com.github.dockerjava.core.DockerClientConfig; import com.github.dockerjava.core.DockerClientConfigAware; -import com.github.dockerjava.core.DockerClientImpl; import com.github.dockerjava.jsch.SsshWithOKDockerHttpClient; +import com.github.dockerjava.transport.DockerHttpClient; import com.jcraft.jsch.JSchException; import java.io.IOException; -public class SSHClientFactory extends DelegatingDockerCmdExecFactory implements DockerClientConfigAware { +public class SSHClientFactory implements DockerClientConfigAware { - private DefaultDockerCmdExecFactory dockerCmdExecFactory; - private SsshWithOKDockerHttpClient httpClient; - - @Override - public final DockerCmdExecFactory getDockerCmdExecFactory() { - return dockerCmdExecFactory; - } + private DockerHttpClient httpClient; @Override public void init(DockerClientConfig dockerClientConfig) { @@ -51,24 +43,31 @@ public void init(DockerClientConfig dockerClientConfig) { try { httpClient = factory.build(); - dockerCmdExecFactory = new DefaultDockerCmdExecFactory(httpClient, defaultDockerClientConfig.getObjectMapper()); } catch (IOException | JSchException e) { throw new RuntimeException(e); } - dockerCmdExecFactory.init(defaultDockerClientConfig); } - public SSHClientFactory withDockerClientConfig(DockerClientConfig config) { + SSHClientFactory withDockerClientConfig(DockerClientConfig config) { init(config); return this; } public DockerClient build() { + + final DefaultDockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder() + .withRegistryUrl("https://index.docker.io/v1/") + .build(); + if (httpClient == null) { - init(DefaultDockerClientConfig.createDefaultConfigBuilder() - .withRegistryUrl("https://index.docker.io/v1/") - .build()); + init(config); } - return DockerClientImpl.getInstance().withHttpClient(httpClient); + + return DockerClientBuilder.getInstance() + .withDockerHttpClient( + new TrackingDockerHttpClient( + httpClient + ) + ).build(); } } diff --git a/docker-java/src/test/java/com/github/dockerjava/junit/PrivateRegistryRule.java b/docker-java/src/test/java/com/github/dockerjava/junit/PrivateRegistryRule.java index db5f1c604..60131ea86 100644 --- a/docker-java/src/test/java/com/github/dockerjava/junit/PrivateRegistryRule.java +++ b/docker-java/src/test/java/com/github/dockerjava/junit/PrivateRegistryRule.java @@ -9,7 +9,6 @@ import com.github.dockerjava.api.model.Ports; import com.github.dockerjava.cmd.CmdIT; import com.github.dockerjava.core.DefaultDockerClientConfig; -import com.github.dockerjava.core.DockerClientBuilder; import com.github.dockerjava.core.DockerRule; import org.junit.rules.ExternalResource; @@ -43,9 +42,9 @@ public String createPrivateImage(String tagName) throws InterruptedException { String imgNameWithTag = createTestImage(tagName); dockerClient.pushImageCmd(imgNameWithTag) - .withAuthConfig(authConfig) - .start() - .awaitCompletion(30, TimeUnit.SECONDS); + .withAuthConfig(authConfig) + .start() + .awaitCompletion(30, TimeUnit.SECONDS); dockerClient.removeImageCmd(imgNameWithTag).exec(); @@ -69,9 +68,7 @@ public String createTestImage(String tagName) { @Override protected void before() throws Throwable { - this.dockerClient = DockerClientBuilder.getInstance(DefaultDockerClientConfig.createDefaultConfigBuilder().build()) - .withDockerCmdExecFactory(testInstance.getFactoryType().createExecFactory()) - .build(); + this.dockerClient = testInstance.getFactoryType().createDockerClient(DefaultDockerClientConfig.createDefaultConfigBuilder().build()); int port = 5050; @@ -80,26 +77,26 @@ protected void before() throws Throwable { File baseDir = new File(DockerRule.class.getResource("/privateRegistry").getFile()); String registryImageId = dockerClient.buildImageCmd(baseDir) - .withNoCache(true) - .start() - .awaitImageId(); + .withNoCache(true) + .start() + .awaitImageId(); InspectImageResponse inspectImageResponse = dockerClient.inspectImageCmd(registryImageId).exec(); assertThat(inspectImageResponse, not(nullValue())); DockerRule.LOG.info("Image Inspect: {}", inspectImageResponse.toString()); dockerClient.tagImageCmd(registryImageId, imageName, "2") - .withForce().exec(); + .withForce().exec(); // see https://github.com/docker/distribution/blob/master/docs/deploying.md#native-basic-auth CreateContainerResponse testregistry = dockerClient - .createContainerCmd(imageName + ":2") - .withHostConfig(newHostConfig() - .withPortBindings(new PortBinding(Ports.Binding.bindPort(port), 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(); + .createContainerCmd(imageName + ":2") + .withHostConfig(newHostConfig() + .withPortBindings(new PortBinding(Ports.Binding.bindPort(port), 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(); containerId = testregistry.getId(); dockerClient.startContainerCmd(containerId).exec(); @@ -109,18 +106,18 @@ protected void before() throws Throwable { // credentials as configured in /auth/htpasswd authConfig = new AuthConfig() - .withUsername("testuser") - .withPassword("testpassword") - .withRegistryAddress("localhost:" + port); + .withUsername("testuser") + .withPassword("testpassword") + .withRegistryAddress("localhost:" + port); } @Override protected void after() { if (containerId != null) { dockerClient.removeContainerCmd(containerId) - .withForce(true) - .withRemoveVolumes(true) - .exec(); + .withForce(true) + .withRemoveVolumes(true) + .exec(); } } } From f81f7d6009e5aade93b468103dd267e691c63daa Mon Sep 17 00:00:00 2001 From: Wiedemann Matthias Date: Wed, 24 Jun 2020 22:22:42 +0200 Subject: [PATCH 15/24] negate condition --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 43807b745..5ec00c4e2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,4 +40,4 @@ jobs: env: DOCKER_HOST: ${{matrix.dockerHost}} run: ./mvnw --no-transfer-progress verify - if: !startsWith(matrix.name,'ssh') + if: startsWith(matrix.name,'ssh')!=true From 28862fb33d17ef62be735c0e0d9783f719bce0ee Mon Sep 17 00:00:00 2001 From: Wiedemann Matthias Date: Wed, 24 Jun 2020 23:31:38 +0200 Subject: [PATCH 16/24] running all ssh test and publish results --- .github/workflows/ci.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5ec00c4e2..d44078d3b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: - { name: "over TCP", dockerHost: "tcp://127.0.0.1:2375" } - { name: "Docker 18.06.3", dockerVersion: "18.06.3~ce~3-0~ubuntu" } - { name: "ssh-default" , clientDockerHost: "ssh://junit-host"} - - { name: "ssh-19.03.9" , dockerVersion: "19.03.9~3-0~ubuntu-bionic", clientDockerHost: "ssh://junit-host"} + - { name: "ssh-19.03.9" , dockerVersion: "5:19.03.12~3-0~ubuntu-bionic", clientDockerHost: "ssh://junit-host"} steps: - uses: actions/checkout@v2 @@ -34,10 +34,16 @@ jobs: - name: Build with Maven (SSH) env: DOCKER_HOST: ${{matrix.clientDockerHost}} - run: ./mvnw --no-transfer-progress verify + run: ./mvnw --no-transfer-progress -fae verify if: startsWith(matrix.name,'ssh') - name: Build with Maven env: DOCKER_HOST: ${{matrix.dockerHost}} run: ./mvnw --no-transfer-progress verify - if: startsWith(matrix.name,'ssh')!=true + # if: startsWith(matrix.name,'ssh')!=true + if: false + - uses: actions/upload-artifact@v2 + with: + name: ${{ matrix.name }}-testresults + path: **/TEST-*.xml + if: always() From f443455f014a6a8e3085227d31e2afcb0b4470e0 Mon Sep 17 00:00:00 2001 From: Wiedemann Matthias Date: Wed, 24 Jun 2020 23:32:44 +0200 Subject: [PATCH 17/24] syntax --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d44078d3b..61622780c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,5 +45,5 @@ jobs: - uses: actions/upload-artifact@v2 with: name: ${{ matrix.name }}-testresults - path: **/TEST-*.xml + path: '**/TEST-*.xml' if: always() From 39ddef098d17bda177146169b88697684dee3168 Mon Sep 17 00:00:00 2001 From: Wiedemann Matthias Date: Thu, 25 Jun 2020 00:23:41 +0200 Subject: [PATCH 18/24] ignore test failures --- docker-java/pom.xml | 2 ++ docker-java/src/test/resources/logback.xml | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docker-java/pom.xml b/docker-java/pom.xml index 706d93446..5674a6160 100644 --- a/docker-java/pom.xml +++ b/docker-java/pom.xml @@ -142,6 +142,7 @@ false 3 com.github.dockerjava.junit.category.Integration + true @@ -161,6 +162,7 @@ false 5 com.github.dockerjava.junit.category.Integration + true diff --git a/docker-java/src/test/resources/logback.xml b/docker-java/src/test/resources/logback.xml index b4309b868..d2c2090ad 100644 --- a/docker-java/src/test/resources/logback.xml +++ b/docker-java/src/test/resources/logback.xml @@ -9,6 +9,7 @@ + @@ -16,4 +17,4 @@ - \ No newline at end of file + From 48099d253f97004c286c0c43471c0a63346aeb12 Mon Sep 17 00:00:00 2001 From: Wiedemann Matthias Date: Thu, 25 Jun 2020 22:31:28 +0200 Subject: [PATCH 19/24] reverted testFailureIgnore, added failsafe plugin --- docker-java-transport-ssh/pom.xml | 17 +++++++++++++++++ docker-java/pom.xml | 2 -- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/docker-java-transport-ssh/pom.xml b/docker-java-transport-ssh/pom.xml index 2c3b473cb..5411c5d48 100644 --- a/docker-java-transport-ssh/pom.xml +++ b/docker-java-transport-ssh/pom.xml @@ -84,5 +84,22 @@ + + + + org.apache.maven.plugins + maven-failsafe-plugin + ${maven-failsafe-plugin.version} + + + + integration-test + verify + + + + + + diff --git a/docker-java/pom.xml b/docker-java/pom.xml index 5674a6160..706d93446 100644 --- a/docker-java/pom.xml +++ b/docker-java/pom.xml @@ -142,7 +142,6 @@ false 3 com.github.dockerjava.junit.category.Integration - true @@ -162,7 +161,6 @@ false 5 com.github.dockerjava.junit.category.Integration - true From d556b30733dc4e3f5f01f5f5e82c65a04347fe82 Mon Sep 17 00:00:00 2001 From: Wiedemann Matthias Date: Mon, 20 Jul 2020 21:53:26 +0200 Subject: [PATCH 20/24] Refactoring and fix for Attch-Stdin Test --- docker-java-transport-ssh/pom.xml | 37 --- .../dockerjava/jsch/HijackingInterceptor.java | 63 +++++ .../dockerjava/jsch/JSchSocketFactory.java | 44 ++++ ...ockerConfig.java => JschDockerConfig.java} | 22 +- ...pClient.java => JschDockerHttpClient.java} | 217 ++++++++++-------- .../github/dockerjava/jsch/JschSocket.java | 137 +++++++++++ .../dockerjava/jsch/SSHSocketFactory.java | 146 ------------ .../github/dockerjava/jsch/SocatHandler.java | 45 ++-- ...entIT.java => JschDockerHttpClientIT.java} | 20 +- .../dockerjava/jsch/SocatHandlerIT.java | 2 +- .../java/com/github/dockerjava/cmd/CmdIT.java | 4 +- .../dockerjava/cmd/SSHClientFactory.java | 12 +- 12 files changed, 426 insertions(+), 323 deletions(-) create mode 100644 docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/HijackingInterceptor.java create mode 100644 docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/JSchSocketFactory.java rename docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/{SSHDockerConfig.java => JschDockerConfig.java} (78%) rename docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/{SsshWithOKDockerHttpClient.java => JschDockerHttpClient.java} (61%) create mode 100644 docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/JschSocket.java delete mode 100644 docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SSHSocketFactory.java rename docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/{SsshWithOKDockerHttpClientIT.java => JschDockerHttpClientIT.java} (71%) diff --git a/docker-java-transport-ssh/pom.xml b/docker-java-transport-ssh/pom.xml index 5411c5d48..ad5fb13e9 100644 --- a/docker-java-transport-ssh/pom.xml +++ b/docker-java-transport-ssh/pom.xml @@ -47,43 +47,6 @@ - - - dummy-file-creation - - - windows - - - - - - org.apache.maven.plugins - maven-antrun-plugin - - - test-compile - - - - - - - - - - - run - - - - - - - - - diff --git a/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/HijackingInterceptor.java b/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/HijackingInterceptor.java new file mode 100644 index 000000000..00da33de2 --- /dev/null +++ b/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/HijackingInterceptor.java @@ -0,0 +1,63 @@ +package com.github.dockerjava.jsch; + +import com.github.dockerjava.transport.DockerHttpClient; +import okhttp3.Interceptor; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.internal.connection.Exchange; +import okhttp3.internal.http.RealInterceptorChain; +import okhttp3.internal.ws.RealWebSocket; +import okio.BufferedSink; + +import java.io.IOException; +import java.io.InputStream; + +class HijackingInterceptor implements Interceptor { + + @Override + public Response intercept(Chain chain) throws IOException { + Request request = chain.request(); + Response response = chain.proceed(request); + if (!response.isSuccessful()) { + return response; + } + + DockerHttpClient.Request originalRequest = request.tag(DockerHttpClient.Request.class); + + if (originalRequest == null) { + // WTF? + return response; + } + + InputStream stdin = originalRequest.hijackedInput(); + + if (stdin == null) { + return response; + } + + chain.call().timeout().clearTimeout().clearDeadline(); + + Exchange exchange = ((RealInterceptorChain) chain).exchange(); + Thread thread; + try (RealWebSocket.Streams streams = exchange.newWebSocketStreams()) { + thread = new Thread(() -> { + try (BufferedSink sink = streams.sink) { + while (sink.isOpen()) { + int aByte = stdin.read(); + if (aByte < 0) { + break; + } + sink.writeByte(aByte); + sink.flush(); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + } + thread.setName("jsch-hijack-streaming-" + System.identityHashCode(request)); + thread.setDaemon(true); + thread.start(); + return response; + } +} diff --git a/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/JSchSocketFactory.java b/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/JSchSocketFactory.java new file mode 100644 index 000000000..a893cf2ff --- /dev/null +++ b/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/JSchSocketFactory.java @@ -0,0 +1,44 @@ +package com.github.dockerjava.jsch; + +import com.jcraft.jsch.Session; + +import javax.net.SocketFactory; +import java.net.InetAddress; +import java.net.Socket; + +public class JSchSocketFactory extends SocketFactory { + + private final Session session; + private final JschDockerConfig config; + + JSchSocketFactory(Session session, JschDockerConfig config) { + this.session = session; + this.config = config; + } + + @Override + public Socket createSocket() { + return new JschSocket(session, config); + } + + @Override + public Socket createSocket(String s, int i) { + throw new UnsupportedOperationException(); + } + + @Override + public Socket createSocket(String s, int i, InetAddress inetAddress, int i1) { + throw new UnsupportedOperationException(); + } + + @Override + public Socket createSocket(InetAddress inetAddress, int i) { + throw new UnsupportedOperationException(); + } + + @Override + public Socket createSocket(InetAddress inetAddress, int i, InetAddress inetAddress1, int i1) { + throw new UnsupportedOperationException(); + } + +} diff --git a/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SSHDockerConfig.java b/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/JschDockerConfig.java similarity index 78% rename from docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SSHDockerConfig.java rename to docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/JschDockerConfig.java index 9b92c0637..7863d39f1 100644 --- a/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SSHDockerConfig.java +++ b/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/JschDockerConfig.java @@ -4,8 +4,9 @@ import okhttp3.Interceptor; import java.io.File; +import java.util.Hashtable; -public class SSHDockerConfig { +class JschDockerConfig { static final String VAR_RUN_DOCKER_SOCK = "/var/run/docker.sock"; @@ -17,6 +18,8 @@ public class SSHDockerConfig { private boolean useTcp; private boolean useSocket; private Integer tcpPort; + private Hashtable jschConfig; + private String socatFlags; public Integer getTcpPort() { return tcpPort; @@ -26,7 +29,6 @@ public void setTcpPort(Integer tcpPort) { this.tcpPort = tcpPort; } - public String getSocketPath() { return socketPath; } @@ -82,4 +84,20 @@ public boolean isUseSocket() { public void setUseSocket(boolean useSocket) { this.useSocket = useSocket; } + + public void setJschConfig(Hashtable jschConfig) { + this.jschConfig = jschConfig; + } + + public Hashtable getJschConfig() { + return jschConfig; + } + + public String getSocatFlags() { + return socatFlags != null ? socatFlags : ""; + } + + public void setSocatFlags(String socatFlags) { + this.socatFlags = socatFlags; + } } diff --git a/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SsshWithOKDockerHttpClient.java b/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/JschDockerHttpClient.java similarity index 61% rename from docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SsshWithOKDockerHttpClient.java rename to docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/JschDockerHttpClient.java index fb8cae8d1..8ab4712a2 100644 --- a/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SsshWithOKDockerHttpClient.java +++ b/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/JschDockerHttpClient.java @@ -1,12 +1,12 @@ package com.github.dockerjava.jsch; -import com.github.dockerjava.core.DockerClientConfig; -import com.github.dockerjava.core.SSLConfig; import com.github.dockerjava.transport.DockerHttpClient; +import com.github.dockerjava.transport.SSLConfig; import com.jcraft.jsch.JSch; import com.jcraft.jsch.JSchException; import com.jcraft.jsch.OpenSSHConfig; import com.jcraft.jsch.Session; +import okhttp3.Call; import okhttp3.ConnectionPool; import okhttp3.Dns; import okhttp3.HttpUrl; @@ -17,6 +17,8 @@ import okhttp3.ResponseBody; import okio.BufferedSink; import okio.Okio; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.X509TrustManager; @@ -28,16 +30,21 @@ import java.net.URI; import java.security.cert.X509Certificate; import java.util.Collections; +import java.util.Hashtable; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.TimeUnit; -public final class SsshWithOKDockerHttpClient implements DockerHttpClient { +public final class JschDockerHttpClient implements DockerHttpClient { - public static final class Factory { + private static final Logger LOGGER = LoggerFactory.getLogger(JschDockerHttpClient.class); - private DockerClientConfig dockerClientConfig = null; + public static final class Builder { + + private URI dockerHost = null; + + private SSLConfig sslConfig = null; private Integer readTimeout = null; @@ -45,37 +52,42 @@ public static final class Factory { private Boolean retryOnConnectionFailure = null; - final SSHDockerConfig sshDockerConfig = new SSHDockerConfig(); + final JschDockerConfig jschDockerConfig = new JschDockerConfig(); + + public Builder dockerHost(URI value) { + this.dockerHost = Objects.requireNonNull(value, "dockerHost"); + return this; + } - public Factory dockerClientConfig(DockerClientConfig value) { - this.dockerClientConfig = value; + public Builder sslConfig(SSLConfig sslConfig) { + this.sslConfig = sslConfig; return this; } - public Factory readTimeout(Integer value) { + public Builder readTimeout(Integer value) { this.readTimeout = value; return this; } - public Factory connectTimeout(Integer value) { + public Builder connectTimeout(Integer value) { this.connectTimeout = value; return this; } - Factory retryOnConnectionFailure(Boolean value) { + Builder retryOnConnectionFailure(Boolean value) { this.retryOnConnectionFailure = value; return this; } /** - * use socket and overwrite default socket path {@link SSHDockerConfig#VAR_RUN_DOCKER_SOCK} + * use socket and overwrite default socket path {@link JschDockerConfig#VAR_RUN_DOCKER_SOCK} * * @param socketPath * @return */ - public Factory useSocket(String socketPath) { - this.sshDockerConfig.setUseSocket(true); - this.sshDockerConfig.setSocketPath(socketPath); + public Builder useSocket(String socketPath) { + this.jschDockerConfig.setUseSocket(true); + this.jschDockerConfig.setSocketPath(socketPath); return this; } @@ -85,8 +97,8 @@ public Factory useSocket(String socketPath) { * @param session * @return */ - public Factory sshSession(Session session) { - this.sshDockerConfig.setSession(session); + public Builder sshSession(Session session) { + this.jschDockerConfig.setSession(session); return this; } @@ -96,8 +108,8 @@ public Factory sshSession(Session session) { * @param identityFile * @return */ - public Factory identityFile(File identityFile) { - this.sshDockerConfig.setIdentityFile(identityFile); + public Builder identityFile(File identityFile) { + this.jschDockerConfig.setIdentityFile(identityFile); return this; } @@ -107,44 +119,67 @@ public Factory identityFile(File identityFile) { * @param privateKey private key filename * @return */ - public Factory identity(String privateKey) { + public Builder identity(String privateKey) { return identityFile(new File(System.getProperty("user.home") + File.separator + ".ssh" + File.separator + privateKey)); } - public Factory interceptor(Interceptor interceptor) { - this.sshDockerConfig.setInterceptor(interceptor); + /** + * pass config which is used for {@link Session#setConfig(Hashtable)} + * + * @param jschConfig + * @return + */ + public Builder jschConfig(Hashtable jschConfig) { + this.jschDockerConfig.setJschConfig(jschConfig); + return this; + } + + public Builder interceptor(Interceptor interceptor) { + this.jschDockerConfig.setInterceptor(interceptor); return this; } - public Factory useSocket() { - this.sshDockerConfig.setUseSocket(true); + public Builder useSocket() { + this.jschDockerConfig.setUseSocket(true); return this; } - public Factory useSocat() { - this.sshDockerConfig.setUseSocat(true); + public Builder useSocat() { + this.jschDockerConfig.setUseSocat(true); + return this; + } + + /** + * allows to set additional flags for the socat call, i.e. -v + * + * @param socatFlags + * @return + */ + public Builder socatFlags(String socatFlags) { + this.jschDockerConfig.setSocatFlags(socatFlags); return this; } - public Factory useTcp() { - this.sshDockerConfig.setUseTcp(true); + public Builder useTcp() { + this.jschDockerConfig.setUseTcp(true); return this; } - public Factory useTcp(int port) { - this.sshDockerConfig.setUseTcp(true); - this.sshDockerConfig.setTcpPort(port); + public Builder useTcp(int port) { + this.jschDockerConfig.setUseTcp(true); + this.jschDockerConfig.setTcpPort(port); return this; } - public SsshWithOKDockerHttpClient build() throws IOException, JSchException { - Objects.requireNonNull(dockerClientConfig, "dockerClientConfig not provided"); - return new SsshWithOKDockerHttpClient( - dockerClientConfig, + public JschDockerHttpClient build() throws IOException, JSchException { + Objects.requireNonNull(dockerHost, "dockerHost not provided"); + return new JschDockerHttpClient( + dockerHost, + sslConfig, readTimeout, connectTimeout, retryOnConnectionFailure, - sshDockerConfig); + jschDockerConfig); } } @@ -158,22 +193,22 @@ public SsshWithOKDockerHttpClient build() throws IOException, JSchException { private Session session; private boolean externalSession = false; - private final File identityFile; - private SsshWithOKDockerHttpClient( - DockerClientConfig dockerClientConfig, + private JschDockerHttpClient( + URI dockerHostUri, + SSLConfig sslConfig, Integer readTimeout, Integer connectTimeout, Boolean retryOnConnectionFailure, - SSHDockerConfig sshDockerConfig) throws IOException, JSchException { + JschDockerConfig jschDockerConfig) throws IOException, JSchException { OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder() - //.addNetworkInterceptor(new HijackingInterceptor()) + .addNetworkInterceptor(new HijackingInterceptor()) .readTimeout(0, TimeUnit.MILLISECONDS) .retryOnConnectionFailure(true); - if (sshDockerConfig.getInterceptor() != null) { - clientBuilder.addInterceptor(sshDockerConfig.getInterceptor()); + if (jschDockerConfig.getInterceptor() != null) { + clientBuilder.addInterceptor(jschDockerConfig.getInterceptor()); } if (readTimeout != null) { @@ -188,17 +223,14 @@ private SsshWithOKDockerHttpClient( clientBuilder.retryOnConnectionFailure(retryOnConnectionFailure); } - this.session = sshDockerConfig.getSession(); - this.externalSession = sshDockerConfig.getSession() != null; - this.identityFile = sshDockerConfig.getIdentityFile(); - - URI dockerHost = dockerClientConfig.getDockerHost(); + this.session = jschDockerConfig.getSession(); + this.externalSession = jschDockerConfig.getSession() != null; - if ("ssh".equals(dockerHost.getScheme())) { + if ("ssh".equals(dockerHostUri.getScheme())) { - this.session = connectSSH(dockerHost, connectTimeout != null ? connectTimeout : 0); + this.session = connectSSH(dockerHostUri, connectTimeout != null ? connectTimeout : 0, jschDockerConfig); - final SSHSocketFactory socketFactory = new SSHSocketFactory(session, sshDockerConfig); + final JSchSocketFactory socketFactory = new JSchSocketFactory(session, jschDockerConfig); clientBuilder.socketFactory(socketFactory); @@ -215,13 +247,10 @@ private SsshWithOKDockerHttpClient( throw new IllegalArgumentException("this implementation only supports ssh connection scheme."); } - boolean isSSL = false; - SSLConfig sslConfig = dockerClientConfig.getSSLConfig(); if (sslConfig != null) { try { SSLContext sslContext = sslConfig.getSSLContext(); if (sslContext != null) { - isSSL = true; clientBuilder.sslSocketFactory(sslContext.getSocketFactory(), new TrustAllX509TrustManager()); } } catch (Exception e) { @@ -233,29 +262,8 @@ private SsshWithOKDockerHttpClient( streamingClient = client.newBuilder().build(); - initBaseUrl(dockerHost, isSSL); - } - - private void initBaseUrl(URI dockerHost, boolean isSSL) { - HttpUrl.Builder baseUrlBuilder; - - switch (dockerHost.getScheme()) { - case "ssh": - // der http Host und Port spielen keine Rolle, da das Socket geforwarded wird.... - baseUrlBuilder = new HttpUrl.Builder() - .scheme("http") - .host("127.0.0.1"); - break; - case "tcp": - baseUrlBuilder = new HttpUrl.Builder() - .scheme(isSSL ? "https" : "http") - .host(dockerHost.getHost()) - .port(dockerHost.getPort()); - break; - default: - baseUrlBuilder = HttpUrl.get(dockerHost.toString()).newBuilder(); - } - baseUrl = baseUrlBuilder.build(); + // we always use socketFactory, therefore we only need: + baseUrl = new HttpUrl.Builder().scheme("http").host("127.0.0.1").build(); } private RequestBody toRequestBody(Request request) { @@ -273,16 +281,15 @@ public void writeTo(BufferedSink sink) throws IOException { } }; } - switch (request.method()) { - case "POST": - return RequestBody.create(null, ""); - default: - return null; + if ("POST".equals(request.method())) { + return RequestBody.create(null, ""); } + return null; } @Override public Response execute(Request request) { + String url = baseUrl.toString(); if (url.endsWith("/") && request.path().startsWith("/")) { url = url.substring(0, url.length() - 1); @@ -302,20 +309,25 @@ public Response execute(Request request) { clientToUse = streamingClient; } + Call call = clientToUse.newCall(requestBuilder.build()); try { - return new OkResponse(clientToUse.newCall(requestBuilder.build()).execute()); + return new OkResponse(call); } catch (IOException e) { + call.cancel(); throw new UncheckedIOException("Error while executing " + request, e); } } @Override - public void close() throws IOException { - disconnectSSH(); - for (OkHttpClient clientToClose : new OkHttpClient[]{client, streamingClient}) { - clientToClose.dispatcher().cancelAll(); - clientToClose.dispatcher().executorService().shutdown(); - clientToClose.connectionPool().evictAll(); + public void close() { + try { + disconnectSSH(); + } finally { + for (OkHttpClient clientToClose : new OkHttpClient[]{client, streamingClient}) { + clientToClose.dispatcher().cancelAll(); + clientToClose.dispatcher().executorService().shutdown(); + clientToClose.connectionPool().evictAll(); + } } } @@ -323,10 +335,13 @@ static class OkResponse implements Response { static final ThreadLocal CLOSING = ThreadLocal.withInitial(() -> false); + private final Call call; + private final okhttp3.Response response; - OkResponse(okhttp3.Response response) { - this.response = response; + OkResponse(Call call) throws IOException { + this.call = call; + this.response = call.execute(); } @Override @@ -354,7 +369,10 @@ public void close() { boolean previous = CLOSING.get(); CLOSING.set(true); try { + call.cancel(); response.close(); + } catch (Exception | AssertionError e) { + LOGGER.debug("Failed to close the response", e); } finally { CLOSING.set(previous); } @@ -364,12 +382,12 @@ public void close() { static class TrustAllX509TrustManager implements X509TrustManager { @Override public void checkClientTrusted(X509Certificate[] x509Certificates, String s) { - + throw new UnsupportedOperationException(); } @Override public void checkServerTrusted(X509Certificate[] x509Certificates, String s) { - + throw new UnsupportedOperationException(); } @Override @@ -378,7 +396,8 @@ public X509Certificate[] getAcceptedIssuers() { } } - private Session connectSSH(URI connectionString, int connectTimeout) throws IOException, JSchException { + private Session connectSSH(URI connectionString, int connectTimeout, JschDockerConfig jschDockerConfig) + throws IOException, JSchException { if (session != null && session.isConnected()) { return session; @@ -397,12 +416,15 @@ private Session connectSSH(URI connectionString, int connectTimeout) throws IOEx final int port = connectionString.getPort() > 0 ? connectionString.getPort() : 22; final Session newSession = jSch.getSession(connectionString.getUserInfo(), connectionString.getHost(), port); - newSession.setConfig("StrictHostKeyChecking", "no"); // https://stackoverflow.com/questions/10881981/sftp-connection-through-java-asking-for-weird-authentication newSession.setConfig("PreferredAuthentications", "publickey"); - if (identityFile != null) { - jSch.addIdentity(identityFile.getAbsolutePath()); + if (jschDockerConfig.getJschConfig() != null) { + newSession.setConfig(jschDockerConfig.getJschConfig()); + } + + if (jschDockerConfig.getIdentityFile() != null) { + jSch.addIdentity(jschDockerConfig.getIdentityFile().getAbsolutePath()); } newSession.connect(connectTimeout); @@ -410,7 +432,6 @@ private Session connectSSH(URI connectionString, int connectTimeout) throws IOEx return newSession; } - private void disconnectSSH() { if (!externalSession) { session.disconnect(); diff --git a/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/JschSocket.java b/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/JschSocket.java new file mode 100644 index 000000000..18d4f9481 --- /dev/null +++ b/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/JschSocket.java @@ -0,0 +1,137 @@ +package com.github.dockerjava.jsch; + +import com.github.dockerjava.api.model.Container; +import com.github.dockerjava.api.model.ContainerPort; +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.ChannelDirectStreamLocal; +import com.jcraft.jsch.ChannelExec; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.Session; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import java.net.SocketAddress; +import java.util.Locale; +import java.util.Objects; + +import static com.github.dockerjava.jsch.JschDockerHttpClient.OkResponse; + +class JschSocket extends Socket { + + private static Logger logger = LoggerFactory.getLogger(JschSocket.class); + + private final JschDockerConfig config; + private final Session session; + + private Channel channel; + private InputStream inputStream; + private OutputStream outputStream; + private Container socatContainer; + + JschSocket(Session session, JschDockerConfig config) { + this.session = session; + this.config = config; + } + + @Override + public void connect(SocketAddress endpoint) throws IOException { + connect(0); + } + + @Override + public void connect(SocketAddress endpoint, int timeout) throws IOException { + connect(timeout); + } + + @Override + public boolean isConnected() { + return channel.isConnected(); + } + + @Override + public boolean isClosed() { + return channel != null && channel.isClosed(); + } + + private void connect(int timeout) throws IOException { + try { + if (config.isUseTcp()) { + final int port = config.getTcpPort() != null ? config.getTcpPort() : 2375; + channel = session.getStreamForwarder("127.0.0.1", port); + logger.debug("Using channel direct-tcpip with 127.0.0.1:{}", port); + } else if (config.isUseSocat() || unixSocketOnWindows()) { + // forward docker socket via socat + socatContainer = SocatHandler.startSocat(session, config.getSocatFlags()); + final ContainerPort containerPort = socatContainer.getPorts()[0]; + Objects.requireNonNull(containerPort); + channel = session.getStreamForwarder(containerPort.getIp(), containerPort.getPublicPort()); + logger.debug("Using channel direct-tcpip with socat on port {}", containerPort.getPublicPort()); + } else if (config.isUseSocket()) { + // directly forward docker socket + channel = session.openChannel("direct-streamlocal@openssh.com"); + ((ChannelDirectStreamLocal) channel).setSocketPath(config.getSocketPath()); + logger.debug("Using channel direct-streamlocal on {}", config.getSocketPath()); + } else { + // only 18.09 and up + channel = session.openChannel("exec"); + ((ChannelExec) channel).setCommand("docker system dial-stdio"); + logger.debug("Using dialer command"); + } + + inputStream = channel.getInputStream(); + outputStream = channel.getOutputStream(); + + channel.connect(timeout); + + } catch (JSchException e) { + throw new IOException(e); + } + } + + @Override + public synchronized void close() throws IOException { + if (socatContainer != null) { + try { + SocatHandler.stopSocat(session, socatContainer.getId()); + } catch (JSchException e) { + throw new IOException(e); + } + } + channel.disconnect(); + } + + @Override + public InputStream getInputStream() { + return new FilterInputStream(inputStream) { + + @Override + public int read(byte[] b, int off, int len) throws IOException { + if (Boolean.TRUE.equals(OkResponse.CLOSING.get())) { + return 0; + } + + return super.read(b, off, len); + + } + }; + } + + @Override + public OutputStream getOutputStream() { + return outputStream; + } + + private boolean unixSocketOnWindows() { + return config.isUseSocket() && config.getSocketPath().equalsIgnoreCase(JschDockerConfig.VAR_RUN_DOCKER_SOCK) && isWindowsHost(); + } + + private boolean isWindowsHost() { + final String serverVersion = session.getServerVersion(); + return serverVersion.toLowerCase(Locale.getDefault()).contains("windows"); + } +} diff --git a/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SSHSocketFactory.java b/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SSHSocketFactory.java deleted file mode 100644 index 6b41ae513..000000000 --- a/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SSHSocketFactory.java +++ /dev/null @@ -1,146 +0,0 @@ -package com.github.dockerjava.jsch; - -import com.github.dockerjava.api.model.Container; -import com.github.dockerjava.api.model.ContainerPort; -import com.jcraft.jsch.Channel; -import com.jcraft.jsch.ChannelDirectStreamLocal; -import com.jcraft.jsch.ChannelExec; -import com.jcraft.jsch.JSchException; -import com.jcraft.jsch.Session; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.net.SocketFactory; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.InetAddress; -import java.net.Socket; -import java.net.SocketAddress; -import java.util.Locale; -import java.util.Objects; - -public class SSHSocketFactory extends SocketFactory { - - private static Logger logger = LoggerFactory.getLogger(SSHSocketFactory.class); - - private final Session session; - private final SSHDockerConfig config; - private Container socatContainer; - - SSHSocketFactory(Session session, SSHDockerConfig config) { - this.session = session; - this.config = config; - } - - @Override - public Socket createSocket() { - return new Socket() { - - private Channel channel; - private InputStream inputStream; - private OutputStream outputStream; - - @Override - public synchronized void close() throws IOException { - if (socatContainer != null) { - try { - SocatHandler.stopSocat(session, socatContainer.getId()); - } catch (JSchException e) { - throw new IOException(e); - } - } - channel.disconnect(); - } - - @Override - public void connect(SocketAddress endpoint) throws IOException { - connect(0); - } - - @Override - public void connect(SocketAddress endpoint, int timeout) throws IOException { - connect(timeout); - } - - @Override - public boolean isConnected() { - return channel.isConnected(); - } - - private void connect(int timeout) throws IOException { - try { - if (config.isUseTcp()) { - final int port = config.getTcpPort() != null ? config.getTcpPort() : 2375; - channel = session.getStreamForwarder("127.0.0.1", port); - logger.debug("Using channel direct-tcpip with 127.0.0.1:{}", port); - } else if (config.isUseSocat() || unixSocketOnWindows()) { - // forward docker socket via socat - socatContainer = SocatHandler.startSocat(session); - final ContainerPort containerPort = socatContainer.getPorts()[0]; - Objects.requireNonNull(containerPort); - channel = session.getStreamForwarder(containerPort.getIp(), containerPort.getPublicPort()); - logger.debug("Using channel direct-tcpip with socat on port {}", containerPort.getPublicPort()); - } else if (config.isUseSocket()) { - // directly forward docker socket - channel = session.openChannel("direct-streamlocal@openssh.com"); - ((ChannelDirectStreamLocal) channel).setSocketPath(config.getSocketPath()); - logger.debug("Using channel direct-streamlocal on {}", config.getSocketPath()); - } else { - // only 18.09 and up - channel = session.openChannel("exec"); - ((ChannelExec) channel).setCommand("docker system dial-stdio"); - logger.debug("Using dialer command"); - } - - inputStream = channel.getInputStream(); - outputStream = channel.getOutputStream(); - channel.connect(timeout); - - } catch (JSchException e) { - throw new IOException(e); - } - } - - @Override - public InputStream getInputStream() { - return inputStream; - } - - @Override - public OutputStream getOutputStream() { - return outputStream; - } - }; - } - - private boolean unixSocketOnWindows() { - return config.isUseSocket() && config.getSocketPath().equalsIgnoreCase(SSHDockerConfig.VAR_RUN_DOCKER_SOCK) && isWindowsHost(); - } - - private boolean isWindowsHost() { - final String serverVersion = session.getServerVersion(); - return serverVersion.toLowerCase(Locale.getDefault()).contains("windows"); - } - - @Override - public Socket createSocket(String s, int i) { - throw new UnsupportedOperationException(); - } - - @Override - public Socket createSocket(String s, int i, InetAddress inetAddress, int i1) { - throw new UnsupportedOperationException(); - } - - @Override - public Socket createSocket(InetAddress inetAddress, int i) { - throw new UnsupportedOperationException(); - } - - @Override - public Socket createSocket(InetAddress inetAddress, int i, InetAddress inetAddress1, int i1) { - throw new UnsupportedOperationException(); - } - -} diff --git a/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SocatHandler.java b/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SocatHandler.java index 199fa2333..2e1c6f624 100644 --- a/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SocatHandler.java +++ b/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/SocatHandler.java @@ -21,12 +21,12 @@ public class SocatHandler { private SocatHandler() { } - public static Container startSocat(Session session) throws JSchException, IOException { + public static Container startSocat(Session session, String socatFlags) throws JSchException, IOException { final String command = " docker run -d " + " -p 127.0.0.1:0:" + INTERNAL_SOCAT_PORT + " -v /var/run/docker.sock:/var/run/docker.sock " + - " alpine/socat " + + " alpine/socat " + socatFlags + " tcp-listen:" + INTERNAL_SOCAT_PORT + ",fork,reuseaddr unix-connect:/var/run/docker.sock"; final String containerId = runCommand(session, command); @@ -75,36 +75,37 @@ private static String runCommand(Session session, String command) throws JSchExc channel.connect(); - while (true) { + StringBuilder outputBuffer = new StringBuilder(); + StringBuilder errorBuffer = new StringBuilder(); - if (channel.isClosed()) { + byte[] tmp = new byte[1024]; + while (true) { - String response = null; - String errorMessage = null; + while (in.available() > 0) { + int i = in.read(tmp, 0, 1024); + if (i < 0) break; + outputBuffer.append(new String(tmp, 0, i, Charset.defaultCharset())); + } - while (in.available() > 0) { - byte[] tmp = new byte[1024]; - int i = in.read(tmp, 0, 1024); - if (i < 0) break; - response = new String(tmp, 0, i, Charset.defaultCharset()); - } + while (errStream.available() > 0) { + int i = errStream.read(tmp, 0, 1024); + if (i < 0) break; + errorBuffer.append(new String(tmp, Charset.defaultCharset())); + } - while (errStream.available() > 0) { - byte[] tmp = new byte[1024]; - int i = errStream.read(tmp, 0, 1024); - if (i < 0) break; - errorMessage = new String(tmp, Charset.defaultCharset()); - } + if (channel.isClosed()) { + // https://stackoverflow.com/a/47554723/2290153 + if ((in.available() > 0) || (errStream.available() > 0)) continue; logger.debug("exit-status: {}", channel.getExitStatus()); - logger.debug("stderr:{}", errorMessage); - logger.debug("stdout: {}", response); + logger.debug("stderr:{}", errorBuffer); + logger.debug("stdout: {}", outputBuffer); if (channel.getExitStatus() == 0) { - return response; + return outputBuffer.toString(); } else { throw new RuntimeException("command ended in exit-status:" + channel.getExitStatus() + - " with error message: " + errorMessage); + " with error message: " + errorBuffer.toString()); } } Thread.sleep(50); diff --git a/docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/SsshWithOKDockerHttpClientIT.java b/docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/JschDockerHttpClientIT.java similarity index 71% rename from docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/SsshWithOKDockerHttpClientIT.java rename to docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/JschDockerHttpClientIT.java index a7b17074d..7045fbab3 100644 --- a/docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/SsshWithOKDockerHttpClientIT.java +++ b/docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/JschDockerHttpClientIT.java @@ -13,16 +13,17 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; @EnabledIfEnvironmentVariable(named = "DOCKER_HOST", matches = "ssh://.*") -class SsshWithOKDockerHttpClientIT { +class JschDockerHttpClientIT { @Test void pingViaDialer() throws IOException, JSchException { final DefaultDockerClientConfig dockerClientConfig = DefaultDockerClientConfig.createDefaultConfigBuilder().build(); - try (final DockerHttpClient dockerHttpClient = new SsshWithOKDockerHttpClient.Factory().dockerClientConfig(dockerClientConfig).build()) { + try (final DockerHttpClient dockerHttpClient = new JschDockerHttpClient.Builder() + .dockerHost(dockerClientConfig.getDockerHost()).build()) { - final DockerClient dockerClient = DockerClientImpl.getInstance(dockerClientConfig,dockerHttpClient); + final DockerClient dockerClient = DockerClientImpl.getInstance(dockerClientConfig, dockerHttpClient); assertDoesNotThrow(() -> dockerClient.pingCmd().exec()); } @@ -33,11 +34,12 @@ void pingViaSocket() throws IOException, JSchException { final DefaultDockerClientConfig dockerClientConfig = DefaultDockerClientConfig.createDefaultConfigBuilder().build(); - try (final DockerHttpClient dockerHttpClient = new SsshWithOKDockerHttpClient.Factory() + try (final DockerHttpClient dockerHttpClient = new JschDockerHttpClient.Builder() .useSocket() - .dockerClientConfig(dockerClientConfig).build()) { + .dockerHost(dockerClientConfig.getDockerHost()) + .build()) { - final DockerClient dockerClient = DockerClientImpl.getInstance(dockerClientConfig,dockerHttpClient); + final DockerClient dockerClient = DockerClientImpl.getInstance(dockerClientConfig, dockerHttpClient); assertDoesNotThrow(() -> dockerClient.pingCmd().exec()); } @@ -48,12 +50,12 @@ void pingViaSocat() throws IOException, JSchException { final DefaultDockerClientConfig dockerClientConfig = DefaultDockerClientConfig.createDefaultConfigBuilder().build(); - try (final DockerHttpClient dockerHttpClient = new SsshWithOKDockerHttpClient.Factory() + try (final DockerHttpClient dockerHttpClient = new JschDockerHttpClient.Builder() .useSocat() - .dockerClientConfig(dockerClientConfig) + .dockerHost(dockerClientConfig.getDockerHost()) .build()) { - final DockerClient dockerClient = DockerClientImpl.getInstance(dockerClientConfig,dockerHttpClient); + final DockerClient dockerClient = DockerClientImpl.getInstance(dockerClientConfig, dockerHttpClient); assertDoesNotThrow(() -> dockerClient.pingCmd().exec()); } diff --git a/docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/SocatHandlerIT.java b/docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/SocatHandlerIT.java index 4d3f8d889..7ab2c7f28 100644 --- a/docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/SocatHandlerIT.java +++ b/docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/SocatHandlerIT.java @@ -76,7 +76,7 @@ void stopSocat() throws IOException, JSchException { @org.junit.jupiter.api.Test @Timeout(value = 10) void startSocatAndPing() throws IOException, JSchException { - container = SocatHandler.startSocat(session); + container = SocatHandler.startSocat(session, ""); assertNotNull(container); assertEquals("200", ping(container)); } diff --git a/docker-java/src/test/java/com/github/dockerjava/cmd/CmdIT.java b/docker-java/src/test/java/com/github/dockerjava/cmd/CmdIT.java index 718163763..1c7e34101 100644 --- a/docker-java/src/test/java/com/github/dockerjava/cmd/CmdIT.java +++ b/docker-java/src/test/java/com/github/dockerjava/cmd/CmdIT.java @@ -28,8 +28,8 @@ public enum FactoryType { SSH(true, true) { @Override public DockerClientImpl createDockerClient(DockerClientConfig config) { - return (DockerClientImpl) new SSHClientFactory(). - withDockerClientConfig(config) + return (DockerClientImpl) new SSHClientFactory() + .withDockerClientConfig(config) .build(); } }, diff --git a/docker-java/src/test/java/com/github/dockerjava/cmd/SSHClientFactory.java b/docker-java/src/test/java/com/github/dockerjava/cmd/SSHClientFactory.java index 260a1f4ed..4809f47b4 100644 --- a/docker-java/src/test/java/com/github/dockerjava/cmd/SSHClientFactory.java +++ b/docker-java/src/test/java/com/github/dockerjava/cmd/SSHClientFactory.java @@ -5,7 +5,7 @@ import com.github.dockerjava.core.DockerClientBuilder; import com.github.dockerjava.core.DockerClientConfig; import com.github.dockerjava.core.DockerClientConfigAware; -import com.github.dockerjava.jsch.SsshWithOKDockerHttpClient; +import com.github.dockerjava.jsch.JschDockerHttpClient; import com.github.dockerjava.transport.DockerHttpClient; import com.jcraft.jsch.JSchException; @@ -18,9 +18,9 @@ public class SSHClientFactory implements DockerClientConfigAware { @Override public void init(DockerClientConfig dockerClientConfig) { - final SsshWithOKDockerHttpClient.Factory factory = new SsshWithOKDockerHttpClient.Factory() + final JschDockerHttpClient.Builder builder = new JschDockerHttpClient.Builder() .connectTimeout(30 * 1000) - .dockerClientConfig(dockerClientConfig); + .dockerHost(dockerClientConfig.getDockerHost()); final DefaultDockerClientConfig defaultDockerClientConfig = DefaultDockerClientConfig .createDefaultConfigBuilder() @@ -35,14 +35,14 @@ public void init(DockerClientConfig dockerClientConfig) { // Docker Host was overwritten i.e. from com.github.dockerjava.cmd.swarm.SwarmCmdIT.initializeDockerClient // so we still use ssh connection and use inner binding to tcp if ("tcp".equalsIgnoreCase(dockerClientConfig.getDockerHost().getScheme())) { - factory - .dockerClientConfig(defaultDockerClientConfig) + builder + .dockerHost(defaultDockerClientConfig.getDockerHost()) .useTcp(dockerClientConfig.getDockerHost().getPort()); } } try { - httpClient = factory.build(); + httpClient = builder.build(); } catch (IOException | JSchException e) { throw new RuntimeException(e); } From 0c3c9afc032928ba736842dffb166d14778ac770 Mon Sep 17 00:00:00 2001 From: Wiedemann Matthias Date: Mon, 20 Jul 2020 22:35:08 +0200 Subject: [PATCH 21/24] fixing Attch-Stdin Test revert ci workflow to include non-ssh runs --- .github/workflows/ci.yml | 16 +++++++---- .../dockerjava/jsch/HijackingInterceptor.java | 28 +++++++++---------- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 61622780c..aeb33565b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,10 +40,14 @@ jobs: env: DOCKER_HOST: ${{matrix.dockerHost}} run: ./mvnw --no-transfer-progress verify - # if: startsWith(matrix.name,'ssh')!=true - if: false - - uses: actions/upload-artifact@v2 - with: - name: ${{ matrix.name }}-testresults - path: '**/TEST-*.xml' + if: startsWith(matrix.name,'ssh')!=true + - name: Aggregate test reports with ciMate if: always() + continue-on-error: true + env: + CIMATE_PROJECT_ID: lodr9d83 + CIMATE_CI_KEY: "CI / ${{matrix.name}}" + run: | + wget -q https://get.cimate.io/release/linux/cimate + chmod +x cimate + ./cimate "**/TEST-*.xml" diff --git a/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/HijackingInterceptor.java b/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/HijackingInterceptor.java index 00da33de2..12650dcc5 100644 --- a/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/HijackingInterceptor.java +++ b/docker-java-transport-ssh/src/main/java/com/github/dockerjava/jsch/HijackingInterceptor.java @@ -38,23 +38,21 @@ public Response intercept(Chain chain) throws IOException { chain.call().timeout().clearTimeout().clearDeadline(); Exchange exchange = ((RealInterceptorChain) chain).exchange(); - Thread thread; - try (RealWebSocket.Streams streams = exchange.newWebSocketStreams()) { - thread = new Thread(() -> { - try (BufferedSink sink = streams.sink) { - while (sink.isOpen()) { - int aByte = stdin.read(); - if (aByte < 0) { - break; - } - sink.writeByte(aByte); - sink.flush(); + RealWebSocket.Streams streams = exchange.newWebSocketStreams(); + Thread thread = new Thread(() -> { + try (BufferedSink sink = streams.sink) { + while (sink.isOpen()) { + int aByte = stdin.read(); + if (aByte < 0) { + break; } - } catch (Exception e) { - throw new RuntimeException(e); + sink.writeByte(aByte); + sink.flush(); } - }); - } + } catch (Exception e) { + throw new RuntimeException(e); + } + }); thread.setName("jsch-hijack-streaming-" + System.identityHashCode(request)); thread.setDaemon(true); thread.start(); From 56550fe5450ceb86d268d0a58ad4503472c613b3 Mon Sep 17 00:00:00 2001 From: Wiedemann Matthias Date: Tue, 21 Jul 2020 09:07:04 +0200 Subject: [PATCH 22/24] increased test timeout --- .../test/java/com/github/dockerjava/jsch/SocatHandlerIT.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/SocatHandlerIT.java b/docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/SocatHandlerIT.java index 7ab2c7f28..e428dff45 100644 --- a/docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/SocatHandlerIT.java +++ b/docker-java-transport-ssh/src/test/java/com/github/dockerjava/jsch/SocatHandlerIT.java @@ -55,7 +55,6 @@ static void init() throws JSchException, IOException, URISyntaxException { if (file.exists()) { final OpenSSHConfig openSSHConfig = OpenSSHConfig.parseFile(file.getAbsolutePath()); jSch.setConfigRepository(openSSHConfig); - } session = jSch.getSession(new URI(System.getenv("DOCKER_HOST")).getHost()); session.connect(500); @@ -74,7 +73,7 @@ void stopSocat() throws IOException, JSchException { } @org.junit.jupiter.api.Test - @Timeout(value = 10) + @Timeout(value = 20) void startSocatAndPing() throws IOException, JSchException { container = SocatHandler.startSocat(session, ""); assertNotNull(container); From dde5a886d3d8905fdfd624c90a4f05ca84508489 Mon Sep 17 00:00:00 2001 From: Wiedemann Matthias Date: Tue, 21 Jul 2020 14:50:04 +0200 Subject: [PATCH 23/24] revert Import reordering --- .../dockerjava/api/command/EventsCmd.java | 9 ++++--- .../core/AbstractDockerCmdExecFactory.java | 4 +-- .../core/command/EventsCmdImpl.java | 8 +++--- .../core/command/EventsResultCallback.java | 5 ++-- .../command/LogContainerResultCallback.java | 5 ++-- .../core/exec/PushImageCmdExec.java | 5 ++-- .../core/util/CertificateUtils.java | 27 ++++++++++--------- .../jaxrs/JerseyDockerHttpClient.java | 4 +-- .../dockerjava/cmd/AttachContainerCmdIT.java | 2 +- .../github/dockerjava/cmd/CommitCmdIT.java | 2 +- .../dockerjava/cmd/ConnectToNetworkCmdIT.java | 2 +- .../dockerjava/cmd/ContainerDiffCmdIT.java | 2 +- .../dockerjava/cmd/InspectContainerCmdIT.java | 2 +- .../dockerjava/cmd/UpdateContainerCmdIT.java | 2 +- 14 files changed, 42 insertions(+), 37 deletions(-) diff --git a/docker-java-api/src/main/java/com/github/dockerjava/api/command/EventsCmd.java b/docker-java-api/src/main/java/com/github/dockerjava/api/command/EventsCmd.java index 120249fe4..34a0c5ad5 100644 --- a/docker-java-api/src/main/java/com/github/dockerjava/api/command/EventsCmd.java +++ b/docker-java-api/src/main/java/com/github/dockerjava/api/command/EventsCmd.java @@ -1,13 +1,14 @@ package com.github.dockerjava.api.command; -import com.github.dockerjava.api.model.Event; -import com.github.dockerjava.api.model.EventType; - -import javax.annotation.CheckForNull; import java.util.List; import java.util.Map; import java.util.stream.Stream; +import javax.annotation.CheckForNull; + +import com.github.dockerjava.api.model.Event; +import com.github.dockerjava.api.model.EventType; + /** * Get events */ diff --git a/docker-java-core/src/main/java/com/github/dockerjava/core/AbstractDockerCmdExecFactory.java b/docker-java-core/src/main/java/com/github/dockerjava/core/AbstractDockerCmdExecFactory.java index 861c17c27..e31431741 100644 --- a/docker-java-core/src/main/java/com/github/dockerjava/core/AbstractDockerCmdExecFactory.java +++ b/docker-java-core/src/main/java/com/github/dockerjava/core/AbstractDockerCmdExecFactory.java @@ -94,6 +94,8 @@ import com.github.dockerjava.core.exec.EventsCmdExec; import com.github.dockerjava.core.exec.ExecCreateCmdExec; import com.github.dockerjava.core.exec.ExecStartCmdExec; +import com.github.dockerjava.core.exec.ResizeContainerCmdExec; +import com.github.dockerjava.core.exec.ResizeExecCmdExec; import com.github.dockerjava.core.exec.InfoCmdExec; import com.github.dockerjava.core.exec.InitializeSwarmCmdExec; import com.github.dockerjava.core.exec.InspectContainerCmdExec; @@ -131,8 +133,6 @@ import com.github.dockerjava.core.exec.RemoveSwarmNodeCmdExec; import com.github.dockerjava.core.exec.RemoveVolumeCmdExec; import com.github.dockerjava.core.exec.RenameContainerCmdExec; -import com.github.dockerjava.core.exec.ResizeContainerCmdExec; -import com.github.dockerjava.core.exec.ResizeExecCmdExec; import com.github.dockerjava.core.exec.RestartContainerCmdExec; import com.github.dockerjava.core.exec.SaveImageCmdExec; import com.github.dockerjava.core.exec.SaveImagesCmdExec; diff --git a/docker-java-core/src/main/java/com/github/dockerjava/core/command/EventsCmdImpl.java b/docker-java-core/src/main/java/com/github/dockerjava/core/command/EventsCmdImpl.java index 225d76a03..d6cca6899 100644 --- a/docker-java-core/src/main/java/com/github/dockerjava/core/command/EventsCmdImpl.java +++ b/docker-java-core/src/main/java/com/github/dockerjava/core/command/EventsCmdImpl.java @@ -1,13 +1,13 @@ package com.github.dockerjava.core.command; -import com.github.dockerjava.api.command.EventsCmd; -import com.github.dockerjava.api.model.Event; -import com.github.dockerjava.core.util.FiltersBuilder; +import static com.google.common.base.Preconditions.checkNotNull; import java.util.List; import java.util.Map; -import static com.google.common.base.Preconditions.checkNotNull; +import com.github.dockerjava.api.command.EventsCmd; +import com.github.dockerjava.api.model.Event; +import com.github.dockerjava.core.util.FiltersBuilder; /** * Stream docker events diff --git a/docker-java-core/src/main/java/com/github/dockerjava/core/command/EventsResultCallback.java b/docker-java-core/src/main/java/com/github/dockerjava/core/command/EventsResultCallback.java index 336b69631..e6df7fbc1 100644 --- a/docker-java-core/src/main/java/com/github/dockerjava/core/command/EventsResultCallback.java +++ b/docker-java-core/src/main/java/com/github/dockerjava/core/command/EventsResultCallback.java @@ -3,11 +3,12 @@ */ package com.github.dockerjava.core.command; -import com.github.dockerjava.api.model.Event; -import com.github.dockerjava.core.async.ResultCallbackTemplate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.github.dockerjava.api.model.Event; +import com.github.dockerjava.core.async.ResultCallbackTemplate; + /** * * @author Marcus Linke diff --git a/docker-java-core/src/main/java/com/github/dockerjava/core/command/LogContainerResultCallback.java b/docker-java-core/src/main/java/com/github/dockerjava/core/command/LogContainerResultCallback.java index a7f7eae9c..d80a765ef 100644 --- a/docker-java-core/src/main/java/com/github/dockerjava/core/command/LogContainerResultCallback.java +++ b/docker-java-core/src/main/java/com/github/dockerjava/core/command/LogContainerResultCallback.java @@ -3,11 +3,12 @@ */ package com.github.dockerjava.core.command; -import com.github.dockerjava.api.model.Frame; -import com.github.dockerjava.core.async.ResultCallbackTemplate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.github.dockerjava.api.model.Frame; +import com.github.dockerjava.core.async.ResultCallbackTemplate; + /** * * @author Marcus Linke diff --git a/docker-java-core/src/main/java/com/github/dockerjava/core/exec/PushImageCmdExec.java b/docker-java-core/src/main/java/com/github/dockerjava/core/exec/PushImageCmdExec.java index 64ceae46c..4f4540891 100644 --- a/docker-java-core/src/main/java/com/github/dockerjava/core/exec/PushImageCmdExec.java +++ b/docker-java-core/src/main/java/com/github/dockerjava/core/exec/PushImageCmdExec.java @@ -1,5 +1,8 @@ package com.github.dockerjava.core.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; @@ -8,8 +11,6 @@ import com.github.dockerjava.core.InvocationBuilder; import com.github.dockerjava.core.MediaType; import com.github.dockerjava.core.WebTarget; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; public class PushImageCmdExec extends AbstrAsyncDockerCmdExec implements PushImageCmd.Exec { diff --git a/docker-java-core/src/main/java/com/github/dockerjava/core/util/CertificateUtils.java b/docker-java-core/src/main/java/com/github/dockerjava/core/util/CertificateUtils.java index 864176a8f..7cb1a19d4 100644 --- a/docker-java-core/src/main/java/com/github/dockerjava/core/util/CertificateUtils.java +++ b/docker-java-core/src/main/java/com/github/dockerjava/core/util/CertificateUtils.java @@ -1,18 +1,5 @@ package com.github.dockerjava.core.util; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import org.bouncycastle.asn1.ASN1ObjectIdentifier; -import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; -import org.bouncycastle.cert.X509CertificateHolder; -import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.openssl.PEMKeyPair; -import org.bouncycastle.openssl.PEMParser; -import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.annotation.CheckForNull; import java.io.BufferedReader; import java.io.File; import java.io.IOException; @@ -28,6 +15,20 @@ import java.util.ArrayList; import java.util.List; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.openssl.PEMKeyPair; +import org.bouncycastle.openssl.PEMParser; +import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.CheckForNull; + import static java.util.Objects.requireNonNull; public class CertificateUtils { diff --git a/docker-java-transport-jersey/src/main/java/com/github/dockerjava/jaxrs/JerseyDockerHttpClient.java b/docker-java-transport-jersey/src/main/java/com/github/dockerjava/jaxrs/JerseyDockerHttpClient.java index c949758ec..b6c723f5d 100644 --- a/docker-java-transport-jersey/src/main/java/com/github/dockerjava/jaxrs/JerseyDockerHttpClient.java +++ b/docker-java-transport-jersey/src/main/java/com/github/dockerjava/jaxrs/JerseyDockerHttpClient.java @@ -2,10 +2,10 @@ import com.github.dockerjava.api.exception.DockerClientException; import com.github.dockerjava.api.exception.DockerException; -import com.github.dockerjava.jaxrs.filter.ResponseStatusExceptionFilter; -import com.github.dockerjava.jaxrs.filter.SelectiveLoggingFilter; import com.github.dockerjava.transport.DockerHttpClient; import com.github.dockerjava.transport.SSLConfig; +import com.github.dockerjava.jaxrs.filter.ResponseStatusExceptionFilter; +import com.github.dockerjava.jaxrs.filter.SelectiveLoggingFilter; import org.apache.http.client.config.RequestConfig; import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; diff --git a/docker-java/src/test/java/com/github/dockerjava/cmd/AttachContainerCmdIT.java b/docker-java/src/test/java/com/github/dockerjava/cmd/AttachContainerCmdIT.java index 205371412..055eb640b 100644 --- a/docker-java/src/test/java/com/github/dockerjava/cmd/AttachContainerCmdIT.java +++ b/docker-java/src/test/java/com/github/dockerjava/cmd/AttachContainerCmdIT.java @@ -26,8 +26,8 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.emptyString; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.emptyString; import static org.hamcrest.Matchers.not; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; diff --git a/docker-java/src/test/java/com/github/dockerjava/cmd/CommitCmdIT.java b/docker-java/src/test/java/com/github/dockerjava/cmd/CommitCmdIT.java index 8fb1b796a..39f51adc7 100644 --- a/docker-java/src/test/java/com/github/dockerjava/cmd/CommitCmdIT.java +++ b/docker-java/src/test/java/com/github/dockerjava/cmd/CommitCmdIT.java @@ -13,10 +13,10 @@ import static com.github.dockerjava.core.DockerRule.DEFAULT_IMAGE; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.emptyString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.emptyString; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.startsWith; import static org.testinfected.hamcrest.jpa.HasFieldWithValue.hasField; diff --git a/docker-java/src/test/java/com/github/dockerjava/cmd/ConnectToNetworkCmdIT.java b/docker-java/src/test/java/com/github/dockerjava/cmd/ConnectToNetworkCmdIT.java index 5fa62b08f..b7c28e8bc 100644 --- a/docker-java/src/test/java/com/github/dockerjava/cmd/ConnectToNetworkCmdIT.java +++ b/docker-java/src/test/java/com/github/dockerjava/cmd/ConnectToNetworkCmdIT.java @@ -9,8 +9,8 @@ import net.jcip.annotations.ThreadSafe; import org.junit.Test; -import static com.github.dockerjava.core.DockerRule.DEFAULT_IMAGE; import static com.github.dockerjava.junit.DockerAssume.assumeNotSwarm; +import static com.github.dockerjava.core.DockerRule.DEFAULT_IMAGE; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.core.Is.is; diff --git a/docker-java/src/test/java/com/github/dockerjava/cmd/ContainerDiffCmdIT.java b/docker-java/src/test/java/com/github/dockerjava/cmd/ContainerDiffCmdIT.java index ec3c01237..7ff39f3fa 100644 --- a/docker-java/src/test/java/com/github/dockerjava/cmd/ContainerDiffCmdIT.java +++ b/docker-java/src/test/java/com/github/dockerjava/cmd/ContainerDiffCmdIT.java @@ -14,8 +14,8 @@ import static com.github.dockerjava.core.DockerRule.DEFAULT_IMAGE; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.emptyString; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.emptyString; import static org.hamcrest.Matchers.not; import static org.testinfected.hamcrest.jpa.HasFieldWithValue.hasField; diff --git a/docker-java/src/test/java/com/github/dockerjava/cmd/InspectContainerCmdIT.java b/docker-java/src/test/java/com/github/dockerjava/cmd/InspectContainerCmdIT.java index 2c9fd3217..9e47cfd9f 100644 --- a/docker-java/src/test/java/com/github/dockerjava/cmd/InspectContainerCmdIT.java +++ b/docker-java/src/test/java/com/github/dockerjava/cmd/InspectContainerCmdIT.java @@ -18,10 +18,10 @@ import static com.github.dockerjava.utils.TestUtils.isNotSwarm; import static com.github.dockerjava.utils.TestUtils.isSwarm; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.emptyString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.emptyString; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; diff --git a/docker-java/src/test/java/com/github/dockerjava/cmd/UpdateContainerCmdIT.java b/docker-java/src/test/java/com/github/dockerjava/cmd/UpdateContainerCmdIT.java index 92e612203..b21bbb533 100644 --- a/docker-java/src/test/java/com/github/dockerjava/cmd/UpdateContainerCmdIT.java +++ b/docker-java/src/test/java/com/github/dockerjava/cmd/UpdateContainerCmdIT.java @@ -14,9 +14,9 @@ import java.io.IOException; -import static com.github.dockerjava.core.DockerRule.DEFAULT_IMAGE; import static com.github.dockerjava.core.RemoteApiVersion.VERSION_1_22; import static com.github.dockerjava.junit.DockerMatchers.isGreaterOrEqual; +import static com.github.dockerjava.core.DockerRule.DEFAULT_IMAGE; import static com.github.dockerjava.test.serdes.JSONSamples.testRoundTrip; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; From e8db67ed9473f5cfb6744b90b52649ea03da22a6 Mon Sep 17 00:00:00 2001 From: Wiedemann Matthias Date: Tue, 21 Jul 2020 14:50:04 +0200 Subject: [PATCH 24/24] revert Import reordering --- .../dockerjava/api/command/EventsCmd.java | 9 ++++--- .../core/AbstractDockerCmdExecFactory.java | 4 +-- .../AttachContainerResultCallback.java | 5 ++-- .../core/command/EventsCmdImpl.java | 8 +++--- .../core/command/EventsResultCallback.java | 5 ++-- .../command/LogContainerResultCallback.java | 5 ++-- .../core/exec/PushImageCmdExec.java | 5 ++-- .../core/util/CertificateUtils.java | 27 ++++++++++--------- .../jaxrs/JerseyDockerHttpClient.java | 4 +-- .../dockerjava/cmd/AttachContainerCmdIT.java | 2 +- .../github/dockerjava/cmd/CommitCmdIT.java | 2 +- .../dockerjava/cmd/ConnectToNetworkCmdIT.java | 2 +- .../dockerjava/cmd/ContainerDiffCmdIT.java | 2 +- .../dockerjava/cmd/InspectContainerCmdIT.java | 2 +- .../dockerjava/cmd/UpdateContainerCmdIT.java | 2 +- 15 files changed, 45 insertions(+), 39 deletions(-) diff --git a/docker-java-api/src/main/java/com/github/dockerjava/api/command/EventsCmd.java b/docker-java-api/src/main/java/com/github/dockerjava/api/command/EventsCmd.java index 120249fe4..34a0c5ad5 100644 --- a/docker-java-api/src/main/java/com/github/dockerjava/api/command/EventsCmd.java +++ b/docker-java-api/src/main/java/com/github/dockerjava/api/command/EventsCmd.java @@ -1,13 +1,14 @@ package com.github.dockerjava.api.command; -import com.github.dockerjava.api.model.Event; -import com.github.dockerjava.api.model.EventType; - -import javax.annotation.CheckForNull; import java.util.List; import java.util.Map; import java.util.stream.Stream; +import javax.annotation.CheckForNull; + +import com.github.dockerjava.api.model.Event; +import com.github.dockerjava.api.model.EventType; + /** * Get events */ diff --git a/docker-java-core/src/main/java/com/github/dockerjava/core/AbstractDockerCmdExecFactory.java b/docker-java-core/src/main/java/com/github/dockerjava/core/AbstractDockerCmdExecFactory.java index 861c17c27..e31431741 100644 --- a/docker-java-core/src/main/java/com/github/dockerjava/core/AbstractDockerCmdExecFactory.java +++ b/docker-java-core/src/main/java/com/github/dockerjava/core/AbstractDockerCmdExecFactory.java @@ -94,6 +94,8 @@ import com.github.dockerjava.core.exec.EventsCmdExec; import com.github.dockerjava.core.exec.ExecCreateCmdExec; import com.github.dockerjava.core.exec.ExecStartCmdExec; +import com.github.dockerjava.core.exec.ResizeContainerCmdExec; +import com.github.dockerjava.core.exec.ResizeExecCmdExec; import com.github.dockerjava.core.exec.InfoCmdExec; import com.github.dockerjava.core.exec.InitializeSwarmCmdExec; import com.github.dockerjava.core.exec.InspectContainerCmdExec; @@ -131,8 +133,6 @@ import com.github.dockerjava.core.exec.RemoveSwarmNodeCmdExec; import com.github.dockerjava.core.exec.RemoveVolumeCmdExec; import com.github.dockerjava.core.exec.RenameContainerCmdExec; -import com.github.dockerjava.core.exec.ResizeContainerCmdExec; -import com.github.dockerjava.core.exec.ResizeExecCmdExec; import com.github.dockerjava.core.exec.RestartContainerCmdExec; import com.github.dockerjava.core.exec.SaveImageCmdExec; import com.github.dockerjava.core.exec.SaveImagesCmdExec; diff --git a/docker-java-core/src/main/java/com/github/dockerjava/core/command/AttachContainerResultCallback.java b/docker-java-core/src/main/java/com/github/dockerjava/core/command/AttachContainerResultCallback.java index 2ad322471..1e4d22cc8 100644 --- a/docker-java-core/src/main/java/com/github/dockerjava/core/command/AttachContainerResultCallback.java +++ b/docker-java-core/src/main/java/com/github/dockerjava/core/command/AttachContainerResultCallback.java @@ -3,11 +3,12 @@ */ package com.github.dockerjava.core.command; -import com.github.dockerjava.api.model.Frame; -import com.github.dockerjava.core.async.ResultCallbackTemplate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.github.dockerjava.api.model.Frame; +import com.github.dockerjava.core.async.ResultCallbackTemplate; + /** * * @author Marcus Linke diff --git a/docker-java-core/src/main/java/com/github/dockerjava/core/command/EventsCmdImpl.java b/docker-java-core/src/main/java/com/github/dockerjava/core/command/EventsCmdImpl.java index 225d76a03..d6cca6899 100644 --- a/docker-java-core/src/main/java/com/github/dockerjava/core/command/EventsCmdImpl.java +++ b/docker-java-core/src/main/java/com/github/dockerjava/core/command/EventsCmdImpl.java @@ -1,13 +1,13 @@ package com.github.dockerjava.core.command; -import com.github.dockerjava.api.command.EventsCmd; -import com.github.dockerjava.api.model.Event; -import com.github.dockerjava.core.util.FiltersBuilder; +import static com.google.common.base.Preconditions.checkNotNull; import java.util.List; import java.util.Map; -import static com.google.common.base.Preconditions.checkNotNull; +import com.github.dockerjava.api.command.EventsCmd; +import com.github.dockerjava.api.model.Event; +import com.github.dockerjava.core.util.FiltersBuilder; /** * Stream docker events diff --git a/docker-java-core/src/main/java/com/github/dockerjava/core/command/EventsResultCallback.java b/docker-java-core/src/main/java/com/github/dockerjava/core/command/EventsResultCallback.java index 336b69631..e6df7fbc1 100644 --- a/docker-java-core/src/main/java/com/github/dockerjava/core/command/EventsResultCallback.java +++ b/docker-java-core/src/main/java/com/github/dockerjava/core/command/EventsResultCallback.java @@ -3,11 +3,12 @@ */ package com.github.dockerjava.core.command; -import com.github.dockerjava.api.model.Event; -import com.github.dockerjava.core.async.ResultCallbackTemplate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.github.dockerjava.api.model.Event; +import com.github.dockerjava.core.async.ResultCallbackTemplate; + /** * * @author Marcus Linke diff --git a/docker-java-core/src/main/java/com/github/dockerjava/core/command/LogContainerResultCallback.java b/docker-java-core/src/main/java/com/github/dockerjava/core/command/LogContainerResultCallback.java index a7f7eae9c..d80a765ef 100644 --- a/docker-java-core/src/main/java/com/github/dockerjava/core/command/LogContainerResultCallback.java +++ b/docker-java-core/src/main/java/com/github/dockerjava/core/command/LogContainerResultCallback.java @@ -3,11 +3,12 @@ */ package com.github.dockerjava.core.command; -import com.github.dockerjava.api.model.Frame; -import com.github.dockerjava.core.async.ResultCallbackTemplate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.github.dockerjava.api.model.Frame; +import com.github.dockerjava.core.async.ResultCallbackTemplate; + /** * * @author Marcus Linke diff --git a/docker-java-core/src/main/java/com/github/dockerjava/core/exec/PushImageCmdExec.java b/docker-java-core/src/main/java/com/github/dockerjava/core/exec/PushImageCmdExec.java index 64ceae46c..4f4540891 100644 --- a/docker-java-core/src/main/java/com/github/dockerjava/core/exec/PushImageCmdExec.java +++ b/docker-java-core/src/main/java/com/github/dockerjava/core/exec/PushImageCmdExec.java @@ -1,5 +1,8 @@ package com.github.dockerjava.core.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; @@ -8,8 +11,6 @@ import com.github.dockerjava.core.InvocationBuilder; import com.github.dockerjava.core.MediaType; import com.github.dockerjava.core.WebTarget; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; public class PushImageCmdExec extends AbstrAsyncDockerCmdExec implements PushImageCmd.Exec { diff --git a/docker-java-core/src/main/java/com/github/dockerjava/core/util/CertificateUtils.java b/docker-java-core/src/main/java/com/github/dockerjava/core/util/CertificateUtils.java index 864176a8f..7cb1a19d4 100644 --- a/docker-java-core/src/main/java/com/github/dockerjava/core/util/CertificateUtils.java +++ b/docker-java-core/src/main/java/com/github/dockerjava/core/util/CertificateUtils.java @@ -1,18 +1,5 @@ package com.github.dockerjava.core.util; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import org.bouncycastle.asn1.ASN1ObjectIdentifier; -import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; -import org.bouncycastle.cert.X509CertificateHolder; -import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.openssl.PEMKeyPair; -import org.bouncycastle.openssl.PEMParser; -import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.annotation.CheckForNull; import java.io.BufferedReader; import java.io.File; import java.io.IOException; @@ -28,6 +15,20 @@ import java.util.ArrayList; import java.util.List; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.openssl.PEMKeyPair; +import org.bouncycastle.openssl.PEMParser; +import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.CheckForNull; + import static java.util.Objects.requireNonNull; public class CertificateUtils { diff --git a/docker-java-transport-jersey/src/main/java/com/github/dockerjava/jaxrs/JerseyDockerHttpClient.java b/docker-java-transport-jersey/src/main/java/com/github/dockerjava/jaxrs/JerseyDockerHttpClient.java index c949758ec..b6c723f5d 100644 --- a/docker-java-transport-jersey/src/main/java/com/github/dockerjava/jaxrs/JerseyDockerHttpClient.java +++ b/docker-java-transport-jersey/src/main/java/com/github/dockerjava/jaxrs/JerseyDockerHttpClient.java @@ -2,10 +2,10 @@ import com.github.dockerjava.api.exception.DockerClientException; import com.github.dockerjava.api.exception.DockerException; -import com.github.dockerjava.jaxrs.filter.ResponseStatusExceptionFilter; -import com.github.dockerjava.jaxrs.filter.SelectiveLoggingFilter; import com.github.dockerjava.transport.DockerHttpClient; import com.github.dockerjava.transport.SSLConfig; +import com.github.dockerjava.jaxrs.filter.ResponseStatusExceptionFilter; +import com.github.dockerjava.jaxrs.filter.SelectiveLoggingFilter; import org.apache.http.client.config.RequestConfig; import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; diff --git a/docker-java/src/test/java/com/github/dockerjava/cmd/AttachContainerCmdIT.java b/docker-java/src/test/java/com/github/dockerjava/cmd/AttachContainerCmdIT.java index 205371412..055eb640b 100644 --- a/docker-java/src/test/java/com/github/dockerjava/cmd/AttachContainerCmdIT.java +++ b/docker-java/src/test/java/com/github/dockerjava/cmd/AttachContainerCmdIT.java @@ -26,8 +26,8 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.emptyString; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.emptyString; import static org.hamcrest.Matchers.not; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; diff --git a/docker-java/src/test/java/com/github/dockerjava/cmd/CommitCmdIT.java b/docker-java/src/test/java/com/github/dockerjava/cmd/CommitCmdIT.java index 8fb1b796a..39f51adc7 100644 --- a/docker-java/src/test/java/com/github/dockerjava/cmd/CommitCmdIT.java +++ b/docker-java/src/test/java/com/github/dockerjava/cmd/CommitCmdIT.java @@ -13,10 +13,10 @@ import static com.github.dockerjava.core.DockerRule.DEFAULT_IMAGE; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.emptyString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.emptyString; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.startsWith; import static org.testinfected.hamcrest.jpa.HasFieldWithValue.hasField; diff --git a/docker-java/src/test/java/com/github/dockerjava/cmd/ConnectToNetworkCmdIT.java b/docker-java/src/test/java/com/github/dockerjava/cmd/ConnectToNetworkCmdIT.java index 5fa62b08f..b7c28e8bc 100644 --- a/docker-java/src/test/java/com/github/dockerjava/cmd/ConnectToNetworkCmdIT.java +++ b/docker-java/src/test/java/com/github/dockerjava/cmd/ConnectToNetworkCmdIT.java @@ -9,8 +9,8 @@ import net.jcip.annotations.ThreadSafe; import org.junit.Test; -import static com.github.dockerjava.core.DockerRule.DEFAULT_IMAGE; import static com.github.dockerjava.junit.DockerAssume.assumeNotSwarm; +import static com.github.dockerjava.core.DockerRule.DEFAULT_IMAGE; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.core.Is.is; diff --git a/docker-java/src/test/java/com/github/dockerjava/cmd/ContainerDiffCmdIT.java b/docker-java/src/test/java/com/github/dockerjava/cmd/ContainerDiffCmdIT.java index ec3c01237..7ff39f3fa 100644 --- a/docker-java/src/test/java/com/github/dockerjava/cmd/ContainerDiffCmdIT.java +++ b/docker-java/src/test/java/com/github/dockerjava/cmd/ContainerDiffCmdIT.java @@ -14,8 +14,8 @@ import static com.github.dockerjava.core.DockerRule.DEFAULT_IMAGE; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.emptyString; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.emptyString; import static org.hamcrest.Matchers.not; import static org.testinfected.hamcrest.jpa.HasFieldWithValue.hasField; diff --git a/docker-java/src/test/java/com/github/dockerjava/cmd/InspectContainerCmdIT.java b/docker-java/src/test/java/com/github/dockerjava/cmd/InspectContainerCmdIT.java index 2c9fd3217..9e47cfd9f 100644 --- a/docker-java/src/test/java/com/github/dockerjava/cmd/InspectContainerCmdIT.java +++ b/docker-java/src/test/java/com/github/dockerjava/cmd/InspectContainerCmdIT.java @@ -18,10 +18,10 @@ import static com.github.dockerjava.utils.TestUtils.isNotSwarm; import static com.github.dockerjava.utils.TestUtils.isSwarm; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.emptyString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.emptyString; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; diff --git a/docker-java/src/test/java/com/github/dockerjava/cmd/UpdateContainerCmdIT.java b/docker-java/src/test/java/com/github/dockerjava/cmd/UpdateContainerCmdIT.java index 92e612203..b21bbb533 100644 --- a/docker-java/src/test/java/com/github/dockerjava/cmd/UpdateContainerCmdIT.java +++ b/docker-java/src/test/java/com/github/dockerjava/cmd/UpdateContainerCmdIT.java @@ -14,9 +14,9 @@ import java.io.IOException; -import static com.github.dockerjava.core.DockerRule.DEFAULT_IMAGE; import static com.github.dockerjava.core.RemoteApiVersion.VERSION_1_22; import static com.github.dockerjava.junit.DockerMatchers.isGreaterOrEqual; +import static com.github.dockerjava.core.DockerRule.DEFAULT_IMAGE; import static com.github.dockerjava.test.serdes.JSONSamples.testRoundTrip; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat;