Skip to content

Commit 442d2e5

Browse files
committed
Merge branch 'master' of https://github.com/testcontainers/testcontainers-java into issues-2876
2 parents bb0e081 + 706a669 commit 442d2e5

File tree

194 files changed

+1964
-630
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

194 files changed

+1964
-630
lines changed

.github/workflows/ci-rootless.yml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
name: CI-Docker-Rootless
2+
3+
on:
4+
pull_request: {}
5+
push: { branches: [ master ] }
6+
7+
jobs:
8+
test:
9+
runs-on: ubuntu-18.04
10+
strategy:
11+
fail-fast: false
12+
matrix:
13+
include:
14+
- { XDG_RUNTIME_DIR: "" }
15+
- { XDG_RUNTIME_DIR: "/tmp/docker-testcontainers/" }
16+
env:
17+
XDG_RUNTIME_DIR: ${{ matrix.XDG_RUNTIME_DIR }}
18+
steps:
19+
- uses: actions/checkout@v2
20+
- name: debug
21+
run: id -u; whoami
22+
- name: uninstall rootful Docker
23+
run: sudo apt-get -q -y --purge remove moby-engine moby-buildx && sudo rm -rf /var/run/docker.sock
24+
- name: install rootless Docker
25+
run: |
26+
mkdir -p $XDG_RUNTIME_DIR || true
27+
curl -fsSL https://get.docker.com/rootless | sh > init.sh
28+
cat init.sh
29+
source <(grep '^export' init.sh)
30+
PATH=$HOME/bin:$PATH dockerd-rootless.sh --experimental --storage-driver vfs &
31+
sleep 1
32+
DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock docker info || ls -la $XDG_RUNTIME_DIR
33+
- name: Build with Gradle
34+
run: ./gradlew --no-daemon --scan testcontainers:test
35+
- name: aggregate test reports with ciMate
36+
if: always()
37+
continue-on-error: true
38+
env:
39+
CIMATE_PROJECT_ID: 2348n4vl
40+
CIMATE_CI_KEY: "CI / rootless Docker (${{ matrix.XDG_RUNTIME_DIR }})"
41+
run: |
42+
wget -q https://get.cimate.io/release/linux/cimate
43+
chmod +x cimate
44+
./cimate "**/TEST-*.xml"

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ jobs:
5656
run: docker image prune -af
5757
- name: Build and test with Gradle (${{matrix.gradle_args}})
5858
run: |
59-
./gradlew --no-daemon --continue --scan --info ${{matrix.gradle_args}}
59+
./gradlew --no-daemon --continue --scan ${{matrix.gradle_args}}
6060
- name: Aggregate test reports with ciMate
6161
if: always()
6262
continue-on-error: true

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

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@
1818
import lombok.SneakyThrows;
1919
import lombok.Synchronized;
2020
import lombok.extern.slf4j.Slf4j;
21+
import org.apache.commons.lang.StringUtils;
2122
import org.testcontainers.dockerclient.DockerClientProviderStrategy;
2223
import org.testcontainers.dockerclient.DockerMachineClientProviderStrategy;
24+
import org.testcontainers.dockerclient.TransportConfig;
2325
import org.testcontainers.images.TimeLimitedLoggedPullImageResultCallback;
2426
import org.testcontainers.utility.ComparableVersion;
2527
import org.testcontainers.utility.MountableFile;
@@ -29,6 +31,7 @@
2931
import java.io.ByteArrayOutputStream;
3032
import java.io.IOException;
3133
import java.io.InputStream;
34+
import java.net.URI;
3235
import java.util.ArrayList;
3336
import java.util.List;
3437
import java.util.Map;
@@ -57,7 +60,7 @@ public class DockerClientFactory {
5760
TESTCONTAINERS_SESSION_ID_LABEL, SESSION_ID
5861
);
5962

60-
private static final String TINY_IMAGE = TestcontainersConfiguration.getInstance().getTinyImage();
63+
private static final String TINY_IMAGE = TestcontainersConfiguration.getInstance().getTinyDockerImageName().asCanonicalNameString();
6164
private static DockerClientFactory instance;
6265

6366
// Cached client configuration
@@ -129,6 +132,24 @@ private DockerClientProviderStrategy getOrInitializeStrategy() {
129132
return strategy;
130133
}
131134

135+
@UnstableAPI
136+
public TransportConfig getTransportConfig() {
137+
return getOrInitializeStrategy().getTransportConfig();
138+
}
139+
140+
@UnstableAPI
141+
public String getRemoteDockerUnixSocketPath() {
142+
String dockerSocketOverride = System.getenv("TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE");
143+
if (!StringUtils.isBlank(dockerSocketOverride)) {
144+
return dockerSocketOverride;
145+
}
146+
147+
URI dockerHost = getTransportConfig().getDockerHost();
148+
return "unix".equals(dockerHost.getScheme())
149+
? dockerHost.getRawPath()
150+
: "/var/run/docker.sock";
151+
}
152+
132153
/**
133154
*
134155
* @return a new initialized Docker client

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

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import com.github.dockerjava.api.DockerClient;
44
import com.github.dockerjava.api.model.Container;
5+
import com.github.dockerjava.core.LocalDirectorySSLConfig;
6+
import com.github.dockerjava.transport.SSLConfig;
57
import com.google.common.annotations.VisibleForTesting;
68
import com.google.common.base.Joiner;
79
import com.google.common.base.Splitter;
@@ -22,6 +24,7 @@
2224
import org.testcontainers.containers.wait.strategy.Wait;
2325
import org.testcontainers.containers.wait.strategy.WaitAllStrategy;
2426
import org.testcontainers.containers.wait.strategy.WaitStrategy;
27+
import org.testcontainers.dockerclient.TransportConfig;
2528
import org.testcontainers.lifecycle.Startable;
2629
import org.testcontainers.utility.AuditLogger;
2730
import org.testcontainers.utility.Base58;
@@ -59,7 +62,6 @@
5962
import static com.google.common.base.Strings.isNullOrEmpty;
6063
import static java.util.stream.Collectors.joining;
6164
import static java.util.stream.Collectors.toList;
62-
import static org.testcontainers.containers.BindMode.READ_ONLY;
6365
import static org.testcontainers.containers.BindMode.READ_WRITE;
6466

6567
/**
@@ -580,12 +582,11 @@ interface DockerCompose {
580582
*/
581583
class ContainerisedDockerCompose extends GenericContainer<ContainerisedDockerCompose> implements DockerCompose {
582584

583-
private static final String DOCKER_SOCKET_PATH = "/var/run/docker.sock";
584585
public static final char UNIX_PATH_SEPERATOR = ':';
585586

586587
public ContainerisedDockerCompose(List<File> composeFiles, String identifier) {
587588

588-
super(TestcontainersConfiguration.getInstance().getDockerComposeContainerImage());
589+
super(TestcontainersConfiguration.getInstance().getDockerComposeDockerImageName());
589590
addEnv(ENV_PROJECT_NAME, identifier);
590591

591592
// Map the docker compose file into the container
@@ -601,24 +602,18 @@ public ContainerisedDockerCompose(List<File> composeFiles, String identifier) {
601602
final String composeFileEnvVariableValue = Joiner.on(UNIX_PATH_SEPERATOR).join(absoluteDockerComposeFiles); // we always need the UNIX path separator
602603
logger().debug("Set env COMPOSE_FILE={}", composeFileEnvVariableValue);
603604
addEnv(ENV_COMPOSE_FILE, composeFileEnvVariableValue);
604-
addFileSystemBind(pwd, containerPwd, READ_ONLY);
605+
addFileSystemBind(pwd, containerPwd, READ_WRITE);
605606

606607
// Ensure that compose can access docker. Since the container is assumed to be running on the same machine
607608
// as the docker daemon, just mapping the docker control socket is OK.
608609
// As there seems to be a problem with mapping to the /var/run directory in certain environments (e.g. CircleCI)
609610
// we map the socket file outside of /var/run, as just /docker.sock
610-
addFileSystemBind(getDockerSocketHostPath(), "/docker.sock", READ_WRITE);
611+
addFileSystemBind("/" + DockerClientFactory.instance().getRemoteDockerUnixSocketPath(), "/docker.sock", READ_WRITE);
611612
addEnv("DOCKER_HOST", "unix:///docker.sock");
612613
setStartupCheckStrategy(new IndefiniteWaitOneShotStartupCheckStrategy());
613614
setWorkingDirectory(containerPwd);
614615
}
615616

616-
private String getDockerSocketHostPath() {
617-
return SystemUtils.IS_OS_WINDOWS
618-
? "/" + DOCKER_SOCKET_PATH
619-
: DOCKER_SOCKET_PATH;
620-
}
621-
622617
@Override
623618
public void invoke() {
624619
super.start();
@@ -681,16 +676,36 @@ public DockerCompose withEnv(Map<String, String> env) {
681676
return this;
682677
}
683678

679+
@VisibleForTesting
680+
static boolean executableExists() {
681+
return CommandLine.executableExists(COMPOSE_EXECUTABLE);
682+
}
683+
684684
@Override
685685
public void invoke() {
686686
// bail out early
687-
if (!CommandLine.executableExists(COMPOSE_EXECUTABLE)) {
687+
if (!executableExists()) {
688688
throw new ContainerLaunchException("Local Docker Compose not found. Is " + COMPOSE_EXECUTABLE + " on the PATH?");
689689
}
690690

691691
final Map<String, String> environment = Maps.newHashMap(env);
692692
environment.put(ENV_PROJECT_NAME, identifier);
693693

694+
String dockerHost = System.getenv("DOCKER_HOST");
695+
if (dockerHost == null) {
696+
TransportConfig transportConfig = DockerClientFactory.instance().getTransportConfig();
697+
SSLConfig sslConfig = transportConfig.getSslConfig();
698+
if (sslConfig != null) {
699+
if (sslConfig instanceof LocalDirectorySSLConfig) {
700+
environment.put("DOCKER_CERT_PATH", ((LocalDirectorySSLConfig) sslConfig).getDockerCertPath());
701+
environment.put("DOCKER_TLS_VERIFY", "true");
702+
} else {
703+
logger().warn("Couldn't set DOCKER_CERT_PATH. `sslConfig` is present but it's not LocalDirectorySSLConfig.");
704+
}
705+
}
706+
dockerHost = transportConfig.getDockerHost().toString();
707+
}
708+
environment.put("DOCKER_HOST", dockerHost);
694709

695710
final Stream<String> absoluteDockerComposeFilePaths = composeFiles.stream()
696711
.map(File::getAbsolutePath)

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@
1212
* not available - which could manifest as flaky or unstable tests.</p>
1313
*/
1414
public class FixedHostPortGenericContainer<SELF extends FixedHostPortGenericContainer<SELF>> extends GenericContainer<SELF> {
15+
16+
/**
17+
* @deprecated it is highly recommended that {@link FixedHostPortGenericContainer} not be used, as it risks port conflicts.
18+
*/
19+
@Deprecated
1520
public FixedHostPortGenericContainer(@NotNull String dockerImageName) {
1621
super(dockerImageName);
1722
}

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

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
import com.github.dockerjava.api.model.ContainerNetwork;
1313
import com.github.dockerjava.api.model.ExposedPort;
1414
import com.github.dockerjava.api.model.HostConfig;
15-
import com.github.dockerjava.api.model.Info;
1615
import com.github.dockerjava.api.model.Link;
1716
import com.github.dockerjava.api.model.PortBinding;
1817
import com.github.dockerjava.api.model.Volume;
@@ -55,6 +54,7 @@
5554
import org.testcontainers.lifecycle.TestDescription;
5655
import org.testcontainers.lifecycle.TestLifecycleAware;
5756
import org.testcontainers.utility.Base58;
57+
import org.testcontainers.utility.DockerImageName;
5858
import org.testcontainers.utility.DockerLoggerFactory;
5959
import org.testcontainers.utility.DockerMachineClient;
6060
import org.testcontainers.utility.MountableFile;
@@ -79,6 +79,7 @@
7979
import java.util.HashMap;
8080
import java.util.HashSet;
8181
import java.util.Iterator;
82+
import java.util.LinkedHashMap;
8283
import java.util.LinkedHashSet;
8384
import java.util.List;
8485
import java.util.Map;
@@ -180,7 +181,8 @@ public class GenericContainer<SELF extends GenericContainer<SELF>>
180181
@Nullable
181182
private Long shmSize;
182183

183-
private Map<MountableFile, String> copyToFileContainerPathMap = new HashMap<>();
184+
// Maintain order in which entries are added, as earlier target location may be a prefix of a later location.
185+
private Map<MountableFile, String> copyToFileContainerPathMap = new LinkedHashMap<>();
184186

185187
protected final Set<Startable> dependencies = new HashSet<>();
186188

@@ -225,10 +227,27 @@ public class GenericContainer<SELF extends GenericContainer<SELF>>
225227
@Setter(AccessLevel.NONE)
226228
private boolean shouldBeReused = false;
227229

230+
231+
public GenericContainer(@NonNull final DockerImageName dockerImageName) {
232+
this.image = new RemoteDockerImage(dockerImageName);
233+
}
234+
235+
public GenericContainer(@NonNull final RemoteDockerImage image) {
236+
this.image = image;
237+
}
238+
239+
/**
240+
* @deprecated use {@link GenericContainer(DockerImageName)} instead
241+
*/
242+
@Deprecated
228243
public GenericContainer() {
229244
this(TestcontainersConfiguration.getInstance().getTinyImage());
230245
}
231246

247+
/**
248+
* @deprecated use {@link GenericContainer(DockerImageName)} instead
249+
*/
250+
@Deprecated
232251
public GenericContainer(@NonNull final String dockerImageName) {
233252
this.setDockerImageName(dockerImageName);
234253
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
public enum PortForwardingContainer {
2020
INSTANCE;
2121

22-
private GenericContainer container;
22+
private GenericContainer<?> container;
2323

2424
private final Set<Entry<Integer, Integer>> exposedPorts = Collections.newSetFromMap(new ConcurrentHashMap<>());
2525

@@ -29,7 +29,7 @@ public enum PortForwardingContainer {
2929
@SneakyThrows
3030
private Connection createSSHSession() {
3131
String password = UUID.randomUUID().toString();
32-
container = new GenericContainer<>(TestcontainersConfiguration.getInstance().getSSHdImage())
32+
container = new GenericContainer<>(TestcontainersConfiguration.getInstance().getSSHdDockerImageName())
3333
.withExposedPorts(22)
3434
.withEnv("PASSWORD", password)
3535
.withCommand(

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.testcontainers.containers;
22

33
import org.testcontainers.utility.Base58;
4+
import org.testcontainers.utility.DockerImageName;
45
import org.testcontainers.utility.TestcontainersConfiguration;
56

67
import java.util.HashMap;
@@ -16,7 +17,11 @@ public class SocatContainer extends GenericContainer<SocatContainer> {
1617
private final Map<Integer, String> targets = new HashMap<>();
1718

1819
public SocatContainer() {
19-
super(TestcontainersConfiguration.getInstance().getSocatContainerImage());
20+
this(TestcontainersConfiguration.getInstance().getSocatDockerImageName());
21+
}
22+
23+
public SocatContainer(final DockerImageName dockerImageName) {
24+
super(dockerImageName);
2025
withCreateContainerCmdModifier(it -> it.withEntrypoint("/bin/sh"));
2126
withCreateContainerCmdModifier(it -> it.withName("testcontainers-socat-" + Base58.randomString(8)));
2227
}
@@ -39,4 +44,4 @@ protected void configure() {
3944
.collect(Collectors.joining(" & "))
4045
);
4146
}
42-
}
47+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public VncRecordingContainer(@NonNull GenericContainer<?> targetContainer) {
5252
* Create a sidekick container and attach it to another container. The VNC output of that container will be recorded.
5353
*/
5454
public VncRecordingContainer(@NonNull Network network, @NonNull String targetNetworkAlias) throws IllegalStateException {
55-
super(TestcontainersConfiguration.getInstance().getVncRecordedContainerImage());
55+
super(TestcontainersConfiguration.getInstance().getVncDockerImageName());
5656

5757
this.targetNetworkAlias = targetNetworkAlias;
5858
withNetwork(network);

core/src/main/java/org/testcontainers/dockerclient/AuthDelegatingDockerClientConfig.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public AuthConfig effectiveAuthConfig(String imageName) {
4040
}
4141

4242
// try and obtain more accurate auth config using our resolution
43-
final DockerImageName parsed = new DockerImageName(imageName);
43+
final DockerImageName parsed = DockerImageName.parse(imageName);
4444
final AuthConfig effectiveAuthConfig = RegistryAuthLocator.instance()
4545
.lookupAuthConfig(parsed, fallbackAuthConfig);
4646

0 commit comments

Comments
 (0)