|
7 | 7 | import com.github.dockerjava.core.DockerClientImpl; |
8 | 8 | import com.github.dockerjava.core.RemoteApiVersion; |
9 | 9 | import com.github.dockerjava.transport.DockerHttpClient; |
| 10 | +import com.github.dockerjava.transport.NamedPipeSocket; |
| 11 | +import com.github.dockerjava.transport.SSLConfig; |
| 12 | +import com.github.dockerjava.transport.UnixSocket; |
10 | 13 | import com.github.dockerjava.zerodep.ZerodepDockerHttpClient; |
11 | 14 | import com.google.common.annotations.VisibleForTesting; |
12 | 15 | import com.google.common.base.Throwables; |
13 | 16 | import lombok.Getter; |
14 | 17 | import lombok.extern.slf4j.Slf4j; |
15 | 18 | import org.apache.commons.io.IOUtils; |
16 | 19 | import org.apache.commons.lang3.StringUtils; |
| 20 | +import org.awaitility.Awaitility; |
17 | 21 | import org.jetbrains.annotations.Nullable; |
18 | 22 | import org.rnorth.ducttape.TimeoutException; |
19 | 23 | import org.rnorth.ducttape.ratelimits.RateLimiter; |
20 | 24 | import org.rnorth.ducttape.ratelimits.RateLimiterBuilder; |
21 | 25 | import org.rnorth.ducttape.unreliables.Unreliables; |
22 | 26 | import org.testcontainers.DockerClientFactory; |
| 27 | +import org.testcontainers.UnstableAPI; |
23 | 28 | import org.testcontainers.utility.TestcontainersConfiguration; |
24 | 29 |
|
| 30 | +import javax.net.SocketFactory; |
| 31 | +import java.io.File; |
| 32 | +import java.net.InetSocketAddress; |
| 33 | +import java.net.Socket; |
| 34 | +import java.net.SocketAddress; |
| 35 | +import java.net.SocketTimeoutException; |
25 | 36 | import java.net.URI; |
| 37 | +import java.security.KeyManagementException; |
| 38 | +import java.security.KeyStoreException; |
| 39 | +import java.security.NoSuchAlgorithmException; |
| 40 | +import java.security.UnrecoverableKeyException; |
| 41 | +import java.time.Duration; |
26 | 42 | import java.util.ArrayList; |
27 | 43 | import java.util.Collections; |
28 | 44 | import java.util.Comparator; |
|
31 | 47 | import java.util.Objects; |
32 | 48 | import java.util.Optional; |
33 | 49 | import java.util.Set; |
| 50 | +import java.util.concurrent.Callable; |
34 | 51 | import java.util.concurrent.TimeUnit; |
35 | 52 | import java.util.concurrent.atomic.AtomicBoolean; |
36 | 53 | import java.util.function.Predicate; |
@@ -104,6 +121,71 @@ public DockerClient getClient() { |
104 | 121 | return dockerClient; |
105 | 122 | } |
106 | 123 |
|
| 124 | + /** |
| 125 | + * TODO we should consider moving this to docker-java at some point |
| 126 | + */ |
| 127 | + @UnstableAPI |
| 128 | + protected boolean test() { |
| 129 | + TransportConfig transportConfig = getTransportConfig(); |
| 130 | + URI dockerHost = transportConfig.getDockerHost(); |
| 131 | + |
| 132 | + Callable<Socket> socketProvider; |
| 133 | + SocketAddress socketAddress; |
| 134 | + switch (dockerHost.getScheme()) { |
| 135 | + case "tcp": |
| 136 | + case "http": |
| 137 | + case "https": |
| 138 | + SocketFactory socketFactory = SocketFactory.getDefault(); |
| 139 | + SSLConfig sslConfig = transportConfig.getSslConfig(); |
| 140 | + if (sslConfig != null) { |
| 141 | + try { |
| 142 | + socketFactory = sslConfig.getSSLContext().getSocketFactory(); |
| 143 | + } catch (KeyManagementException | UnrecoverableKeyException | NoSuchAlgorithmException | KeyStoreException e) { |
| 144 | + log.warn("Exception while creating SSLSocketFactory", e); |
| 145 | + return false; |
| 146 | + } |
| 147 | + } |
| 148 | + socketProvider = socketFactory::createSocket; |
| 149 | + socketAddress = new InetSocketAddress(dockerHost.getHost(), dockerHost.getPort()); |
| 150 | + break; |
| 151 | + case "unix": |
| 152 | + case "npipe": |
| 153 | + if (!new File(dockerHost.getPath()).exists()) { |
| 154 | + log.debug("DOCKER_HOST socket file '{}' does not exist", dockerHost.getPath()); |
| 155 | + return false; |
| 156 | + } |
| 157 | + socketProvider = () -> { |
| 158 | + switch (dockerHost.getScheme()) { |
| 159 | + case "unix": |
| 160 | + return UnixSocket.get(dockerHost.getPath()); |
| 161 | + case "npipe": |
| 162 | + return new NamedPipeSocket(dockerHost.getPath()); |
| 163 | + default: |
| 164 | + throw new IllegalStateException("Unexpected scheme " + dockerHost.getScheme()); |
| 165 | + } |
| 166 | + }; |
| 167 | + socketAddress = new InetSocketAddress("localhost", 2375); |
| 168 | + break; |
| 169 | + default: |
| 170 | + log.warn("Unknown DOCKER_HOST scheme {}, skipping the strategy test...", dockerHost.getScheme()); |
| 171 | + return true; |
| 172 | + } |
| 173 | + |
| 174 | + try (Socket socket = socketProvider.call()) { |
| 175 | + Duration timeout = Duration.ofMillis(200); |
| 176 | + Awaitility.await() |
| 177 | + .atMost(TestcontainersConfiguration.getInstance().getClientPingTimeout(), TimeUnit.SECONDS) |
| 178 | + .pollInterval(timeout) |
| 179 | + .pollDelay(Duration.ofSeconds(0)) // start checking immediately |
| 180 | + .ignoreExceptionsInstanceOf(SocketTimeoutException.class) |
| 181 | + .untilAsserted(() -> socket.connect(socketAddress, (int) timeout.toMillis())); |
| 182 | + return true; |
| 183 | + } catch (Exception e) { |
| 184 | + log.warn("DOCKER_HOST {} is not listening", dockerHost); |
| 185 | + return false; |
| 186 | + } |
| 187 | + } |
| 188 | + |
107 | 189 | /** |
108 | 190 | * Determine the right DockerClientConfig to use for building clients by trial-and-error. |
109 | 191 | * |
@@ -161,20 +243,13 @@ public boolean test(DockerClientProviderStrategy dockerClientProviderStrategy) { |
161 | 243 | private static boolean tryOutStrategy(List<String> configurationFailures, DockerClientProviderStrategy strategy) { |
162 | 244 | try { |
163 | 245 | log.debug("Trying out strategy: {}", strategy.getClass().getSimpleName()); |
164 | | - DockerClient dockerClient = strategy.getDockerClient(); |
165 | | - |
166 | | - Info info; |
167 | | - try { |
168 | | - info = Unreliables.retryUntilSuccess(TestcontainersConfiguration.getInstance().getClientPingTimeout(), TimeUnit.SECONDS, () -> { |
169 | | - return strategy.PING_RATE_LIMITER.getWhenReady(() -> { |
170 | | - log.debug("Pinging docker daemon..."); |
171 | | - return dockerClient.infoCmd().exec(); |
172 | | - }); |
173 | | - }); |
174 | | - } catch (TimeoutException e) { |
175 | | - IOUtils.closeQuietly(dockerClient); |
176 | | - throw e; |
| 246 | + |
| 247 | + if (!strategy.test()) { |
| 248 | + log.debug("strategy {} did not pass the test", strategy.getClass().getSimpleName()); |
| 249 | + return false; |
177 | 250 | } |
| 251 | + |
| 252 | + Info info = strategy.getDockerClient().infoCmd().exec(); |
178 | 253 | log.info("Found Docker environment with {}", strategy.getDescription()); |
179 | 254 | log.debug( |
180 | 255 | "Transport type: '{}', Docker host: '{}'", |
|
0 commit comments