Skip to content

Commit 1c97b97

Browse files
authored
Un-shade docker-java-api (#2882)
This PR makes Testcontainers depend on `docker-java-api` instead of including it into our JAR unshaded. Note that `docker-java-core` and `docker-java-transport-okhttp` remain shaded due to their transitive dependencies like Guava or now-in-Kotlin OkHttp. The transition requires a breaking change in the `DockerClientProviderStrategy` API and includes a rework. The rework also includes an improvement that removes one Docker API call (`/_ping`) and instead reuses the `/_info` one.
1 parent ddbaf94 commit 1c97b97

27 files changed

+268
-339
lines changed

build.gradle

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -122,11 +122,6 @@ subprojects {
122122
for (pkg in packages) {
123123
pkg = pkg.replaceAll('/', '.')
124124

125-
if (pkg.startsWith("com.github.dockerjava.")) {
126-
// Keep docker-java's package inside the final jar
127-
continue;
128-
}
129-
130125
tasks.shadowJar.relocate(pkg, "org.testcontainers.shaded.${pkg}")
131126
}
132127
}

core/build.gradle

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -100,17 +100,33 @@ task japicmp(type: me.champeau.gradle.japicmp.JapicmpTask) {
100100
"org.testcontainers.containers.wait.WaitAllStrategy",
101101
"org.testcontainers.containers.wait.WaitStrategy",
102102
"org.testcontainers.dockerclient.AuditLoggingDockerClient",
103+
"org.testcontainers.dockerclient.LogToStringContainerCallback",
104+
"org.testcontainers.dockerclient.DockerMachineClientProviderStrategy",
105+
"org.testcontainers.dockerclient.EnvironmentAndSystemPropertyClientProviderStrategy",
106+
"org.testcontainers.dockerclient.NpipeSocketClientProviderStrategy",
107+
"org.testcontainers.dockerclient.ProxiedUnixSocketClientProviderStrategy",
108+
"org.testcontainers.dockerclient.WindowsClientProviderStrategy",
109+
"org.testcontainers.dockerclient.UnixSocketClientProviderStrategy",
103110
"org.testcontainers.dockerclient.auth.AuthDelegatingDockerClientConfig",
104111
"org.testcontainers.containers.GenericContainer\$AbstractWaitStrategy",
105112
"org.testcontainers.dockerclient.transport.okhttp.NamedPipeSocketFactory",
106113
"org.testcontainers.dockerclient.transport.okhttp.OkHttpDockerCmdExecFactory",
107114
"org.testcontainers.dockerclient.transport.okhttp.UnixSocketFactory",
115+
"org.testcontainers.containers.output.FrameConsumerResultCallback",
116+
"org.testcontainers.images.TimeLimitedLoggedPullImageResultCallback",
108117
]
109118

110119
methodExcludes = [
111120
"org.testcontainers.dockerclient.DockerClientConfigUtils#getDetectedDockerHostIp()",
112121
"org.testcontainers.dockerclient.DockerClientConfigUtils#getDockerHostIpAddress(com.github.dockerjava.core.DockerClientConfig)",
113122

123+
"org.testcontainers.dockerclient.DockerClientProviderStrategy#checkOSType()",
124+
"org.testcontainers.dockerclient.DockerClientProviderStrategy#getClientForConfig(com.github.dockerjava.core.DockerClientConfig)",
125+
"org.testcontainers.dockerclient.DockerClientProviderStrategy#ping(com.github.dockerjava.api.DockerClient, int)",
126+
"org.testcontainers.dockerclient.DockerClientProviderStrategy#test()",
127+
128+
"org.testcontainers.dockerclient.UnixSocketClientProviderStrategy#tryConfiguration(java.lang.String)",
129+
114130
"org.testcontainers.utility.ResourceReaper#start(java.lang.String, com.github.dockerjava.api.DockerClient, boolean)",
115131

116132
"org.testcontainers.containers.Container#fetchDockerDaemonInfo()",
@@ -126,6 +142,10 @@ task japicmp(type: me.champeau.gradle.japicmp.JapicmpTask) {
126142
"org.testcontainers.containers.GenericContainer#containerId",
127143
"org.testcontainers.containers.GenericContainer#containerName",
128144
"org.testcontainers.containers.GenericContainer#dockerDaemonInfo",
145+
146+
"org.testcontainers.dockerclient.DockerClientProviderStrategy#LOGGER",
147+
"org.testcontainers.dockerclient.DockerClientProviderStrategy#client",
148+
"org.testcontainers.dockerclient.DockerClientProviderStrategy#config",
129149
]
130150

131151
onlyBinaryIncompatibleModified = true
@@ -150,25 +170,27 @@ dependencies {
150170
exclude(group: 'org.jetbrains', module: 'annotations')
151171
}
152172

153-
compile ('org.rnorth.visible-assertions:visible-assertions:2.1.2') {
154-
// Excluded in favor of jna-platform below
155-
exclude(group: "net.java.dev.jna", module: "jna")
156-
}
173+
compile 'org.rnorth.visible-assertions:visible-assertions:2.1.2'
157174

158-
compile "net.java.dev.jna:jna-platform:5.5.0"
175+
compile "com.github.docker-java:docker-java-api:3.2.5"
159176

160-
shaded ('com.github.docker-java:docker-java-transport-okhttp:3.2.5') {
161-
exclude(group: 'net.java.dev.jna')
177+
shaded ('com.github.docker-java:docker-java-core:3.2.5') {
178+
exclude(group: 'com.github.docker-java', module: 'docker-java-api')
179+
exclude(group: 'com.github.docker-java', module: 'docker-java-transport')
180+
exclude(group: 'com.fasterxml.jackson.core', module: 'jackson-annotations')
162181
exclude(group: 'com.google.code.findbug')
163182
exclude(group: 'org.slf4j')
164183
exclude(group: 'org.apache.commons', module: 'commons-compress')
165184
}
166185

167-
shaded ('com.github.docker-java:docker-java-transport-zerodep:3.2.5') {
186+
shaded ('com.github.docker-java:docker-java-transport-okhttp:3.2.5') {
187+
exclude(group: 'com.github.docker-java', module: 'docker-java-core')
168188
exclude(group: 'net.java.dev.jna')
169189
exclude(group: 'org.slf4j')
170190
}
171191

192+
compile 'com.github.docker-java:docker-java-transport-zerodep:3.2.5'
193+
172194
shaded "org.yaml:snakeyaml:1.25"
173195

174196
shaded 'org.glassfish.main.external:trilead-ssh2-repackaged:4.1.2'

core/src/jarFileTest/java/org/testcontainers/JarFileShadingTest.java

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,12 @@ public class JarFileShadingTest extends AbstractJarFileTest {
1515
public void testPackages() throws Exception {
1616
assertThatFileList(root).containsOnly(
1717
"org",
18-
"META-INF",
19-
"com"
18+
"META-INF"
2019
);
2120

2221
assertThatFileList(root.resolve("org")).containsOnly(
2322
"testcontainers"
2423
);
25-
26-
assertThatFileList(root.resolve("com")).containsOnly(
27-
"github"
28-
);
29-
30-
assertThatFileList(root.resolve("com").resolve("github")).containsOnly(
31-
"dockerjava"
32-
);
3324
}
3425

3526
@Test

core/src/jarFileTest/java/org/testcontainers/PublicBinaryAPITest.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,11 @@ public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IO
8888

8989
@Before
9090
public void setUp() {
91-
Assume.assumeFalse(classNode.name.startsWith("com.github.dockerjava."));
9291
switch (classNode.name) {
93-
// TODO should go to docker-java project
94-
case "org/testcontainers/dockerclient/auth/AuthDelegatingDockerClientConfig":
92+
// Necessary evil
93+
case "org/testcontainers/dockerclient/UnixSocketClientProviderStrategy":
94+
case "org/testcontainers/dockerclient/DockerClientProviderStrategy":
95+
case "org/testcontainers/dockerclient/WindowsClientProviderStrategy":
9596
Assume.assumeTrue(false);
9697
}
9798
}

core/src/main/java/org/testcontainers/DockerClientFactory.java

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
package org.testcontainers;
22

33
import com.github.dockerjava.api.DockerClient;
4+
import com.github.dockerjava.api.async.ResultCallback;
45
import com.github.dockerjava.api.command.CreateContainerCmd;
56
import com.github.dockerjava.api.exception.InternalServerErrorException;
67
import com.github.dockerjava.api.exception.NotFoundException;
78
import com.github.dockerjava.api.model.AccessMode;
89
import com.github.dockerjava.api.model.Bind;
10+
import com.github.dockerjava.api.model.Frame;
911
import com.github.dockerjava.api.model.Image;
1012
import com.github.dockerjava.api.model.Info;
1113
import com.github.dockerjava.api.model.Version;
1214
import com.github.dockerjava.api.model.Volume;
13-
import com.github.dockerjava.core.command.ExecStartResultCallback;
1415
import com.google.common.annotations.VisibleForTesting;
1516
import com.google.common.collect.ImmutableMap;
1617
import lombok.Getter;
@@ -26,6 +27,7 @@
2627
import org.testcontainers.utility.TestcontainersConfiguration;
2728

2829
import java.io.ByteArrayOutputStream;
30+
import java.io.IOException;
2931
import java.io.InputStream;
3032
import java.util.ArrayList;
3133
import java.util.List;
@@ -148,7 +150,7 @@ public DockerClient client() {
148150

149151
String hostIpAddress = strategy.getDockerHostIpAddress();
150152
log.info("Docker host IP address is {}", hostIpAddress);
151-
final DockerClient client = new DelegatingDockerClient(strategy.getClient()) {
153+
final DockerClient client = new DelegatingDockerClient(strategy.getDockerClient()) {
152154
@Override
153155
public void close() {
154156
throw new IllegalStateException("You should never close the global DockerClient!");
@@ -228,7 +230,24 @@ private void checkDiskSpace(DockerClient dockerClient, String id) {
228230
try {
229231
dockerClient
230232
.execStartCmd(dockerClient.execCreateCmd(id).withAttachStdout(true).withCmd("df", "-P").exec().getId())
231-
.exec(new ExecStartResultCallback(outputStream, null))
233+
.exec(new ResultCallback.Adapter<Frame>() {
234+
@Override
235+
public void onNext(Frame frame) {
236+
if (frame == null) {
237+
return;
238+
}
239+
switch (frame.getStreamType()) {
240+
case RAW:
241+
case STDOUT:
242+
try {
243+
outputStream.write(frame.getPayload());
244+
outputStream.flush();
245+
} catch (IOException e) {
246+
onError(e);
247+
}
248+
}
249+
}
250+
})
232251
.awaitCompletion();
233252
} catch (Exception e) {
234253
log.debug("Can't exec disk checking command", e);
@@ -295,7 +314,7 @@ public String dockerHostIpAddress() {
295314

296315
public <T> T runInsideDocker(Consumer<CreateContainerCmd> createContainerCmdConsumer, BiFunction<DockerClient, String, T> block) {
297316
// We can't use client() here because it might create an infinite loop
298-
return runInsideDocker(getOrInitializeStrategy().getClient(), createContainerCmdConsumer, block);
317+
return runInsideDocker(getOrInitializeStrategy().getDockerClient(), createContainerCmdConsumer, block);
299318
}
300319

301320
private <T> T runInsideDocker(DockerClient client, Consumer<CreateContainerCmd> createContainerCmdConsumer, BiFunction<DockerClient, String, T> block) {

core/src/main/java/org/testcontainers/containers/GenericContainer.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import com.github.dockerjava.api.model.PortBinding;
1818
import com.github.dockerjava.api.model.Volume;
1919
import com.github.dockerjava.api.model.VolumesFrom;
20+
import com.github.dockerjava.core.DefaultDockerClientConfig;
2021
import com.google.common.annotations.VisibleForTesting;
2122
import com.google.common.base.Strings;
2223
import com.google.common.collect.ImmutableMap;
@@ -514,12 +515,15 @@ void checksumFile(File file, Checksum checksum) {
514515
@UnstableAPI
515516
@SneakyThrows(JsonProcessingException.class)
516517
final String hash(CreateContainerCmd createCommand) {
517-
// TODO add Testcontainers' version to the hash
518-
byte[] commandJson = new ObjectMapper()
518+
DefaultDockerClientConfig dockerClientConfig = DefaultDockerClientConfig.createDefaultConfigBuilder().build();
519+
520+
byte[] commandJson = dockerClientConfig.getObjectMapper()
521+
.copy()
519522
.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY)
520523
.enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)
521524
.writeValueAsBytes(createCommand);
522525

526+
// TODO add Testcontainers' version to the hash
523527
return Hashing.sha1().hashBytes(commandJson).toString();
524528
}
525529

core/src/main/java/org/testcontainers/containers/output/FrameConsumerResultCallback.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package org.testcontainers.containers.output;
22

33

4+
import com.github.dockerjava.api.async.ResultCallbackTemplate;
45
import com.github.dockerjava.api.model.Frame;
56
import com.github.dockerjava.api.model.StreamType;
6-
import com.github.dockerjava.core.async.ResultCallbackTemplate;
77
import org.slf4j.Logger;
88
import org.slf4j.LoggerFactory;
99

0 commit comments

Comments
 (0)