Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve support for alternative container runtimes #9140

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 33 additions & 1 deletion core/src/main/java/org/testcontainers/DockerClientFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@
import org.testcontainers.utility.ResourceReaper;
import org.testcontainers.utility.TestcontainersConfiguration;

import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
Expand Down Expand Up @@ -69,6 +71,8 @@ public class DockerClientFactory {

public static final Map<String, String> DEFAULT_LABELS = markerLabels();

private static final String DEFAULT_DOCKER_DOCKER_PATH = "/var/run/docker.sock";

static Map<String, String> markerLabels() {
String testcontainersVersion = TESTCONTAINERS_VERSION == null ? "unspecified" : TESTCONTAINERS_VERSION;

Expand Down Expand Up @@ -174,10 +178,38 @@ public String getRemoteDockerUnixSocketPath() {
}

URI dockerHost = getTransportConfig().getDockerHost();
String path = "unix".equals(dockerHost.getScheme()) ? dockerHost.getRawPath() : "/var/run/docker.sock";

// When the docker host is not local, assume that the remote one is running using the default privileged mode
// and used the standard /var/run/docker.sock socket
String path = this.isDockerDaemonSocketLocal(dockerHost) ? dockerHost.getRawPath() : DEFAULT_DOCKER_DOCKER_PATH;

return SystemUtils.IS_OS_WINDOWS ? "/" + path : path;
}

private Boolean isDockerDaemonSocketLocal(URI dockerHost) {
if (!"unix".equals(dockerHost.getScheme())) {
log.debug("docker host is not a unix socket. Docker socket is not considered local");
return false;
}
// Several Desktop providers runs docker within a virtual machime running on the host
// (including Docker Desktop and Rancher Desktop) exposes the Docker socket within the user's home directory.
//
// Docker ce linux also has a rootless capability that creates a user-owned socket in the $XDG_RUNTIME_DIR folder (usually /run/user/$UID)
// see https://docs.docker.com/engine/security/rootless/
try {
return !Paths
.get(dockerHost.getRawPath())
.toRealPath()
.normalize()
.startsWith(System.getProperty("user.home"));
} catch (IOException e) {
log.debug(
"error checking whether the final docker socket path is in user home. Docker socket is considered local"
);
return true;
}
}

/**
* @return a new initialized Docker client
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,26 @@

/**
* Use environment variables and system properties (as supported by the underlying DockerClient DefaultConfigBuilder)
* to try and locate a docker environment.
* to try and locate a docker environment based on the currently active configuration.
* <p>
* Resolution order is:
* Docker Host resolution order is:
* <ol>
* <li>DOCKER_HOST env var</li>
* <li>docker.host in ~/.testcontainers.properties</li>
* <li>docker host pointed by the Docker context</li>
* </ol>
* <p>
* Docker context resolution order is
* <ol>
* <li>DOCKER_CONTEXT env var</li>
* <li>docker.context in ~/.testcontainers.properties</li>
* <li>current docker context pointed by the Docker config</li>
* </ol>
* <p>
* Docker config resolution order is
* <ol>
* <li>DOCKER_CONFIG env var</li>
* <li>$HOME/.docker</li>
* </ol>
*
* @deprecated this class is used by the SPI and should not be used directly
Expand Down Expand Up @@ -43,9 +57,14 @@ public EnvironmentAndSystemPropertyClientProviderStrategy() {
case "auto":
Optional<String> dockerHost = getSetting("docker.host");
dockerHost.ifPresent(configBuilder::withDockerHost);
applicable = dockerHost.isPresent();
getSetting("docker.config").ifPresent(configBuilder::withDockerConfig);
getSetting("docker.context").ifPresent(configBuilder::withDockerContext);
getSetting("docker.tls.verify").ifPresent(configBuilder::withDockerTlsVerify);
getSetting("docker.cert.path").ifPresent(configBuilder::withDockerCertPath);
// We don't have access to the docker-java internals to understand whether a docker-context is defined or not.
// Therefore, we assume there is always a docker context defined and try it to start with.
// In case there is an error initializing the docker client with this library, it will fallback to the other resolvers.
applicable = true;
break;
case "autoIgnoringUserProperties":
applicable = configBuilder.isDockerHostSetExplicitly();
Expand Down
77 changes: 25 additions & 52 deletions docs/supported_docker_environment/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,40 @@ These Docker environments are automatically detected and used by Testcontainers

It is possible to configure Testcontainers to work with alternative container runtimes.
Making use of the free [Testcontainers Desktop](https://testcontainers.com/desktop/) app will take care of most of the manual configuration.
When using those alternatives without Testcontainers Desktop,
Although Testcontainers has a detection for most of the cases, when using those alternatives without Testcontainers Desktop,
sometimes some manual configuration might be necessary
(see further down for specific runtimes, or [Customizing Docker host detection](/features/configuration/#customizing-docker-host-detection) for general configuration mechanisms).
Alternative container runtimes are not actively tested in the main development workflow,
so not all Testcontainers features might be available.
If you have further questions about configuration details for your setup or whether it supports running Testcontainers-based tests,
please contact the Testcontainers team and other users from the Testcontainers community on [Slack](https://slack.testcontainers.org/).

## Colima
## Docker environment discovery

In order to run testcontainers against [colima](https://github.com/abiosoft/colima) the env vars below should be set
Testcontainers will try to connect to a Docker daemon using the following strategies in order:

* Environment variables:
* `DOCKER_HOST`
* `DOCKER_TLS_VERIFY`
* `DOCKER_CERT_PATH`
* `DOCKER_CONTEXT`
* `DOCKER_CONFIG`
* Defaults:
* `DOCKER_HOST=https://localhost:2376`
* `DOCKER_TLS_VERIFY=1`
* `DOCKER_CERT_PATH=~/.docker`
* `DOCKER_CONTEXT=$(docker context show)`
* `DOCKER_CONFIG=~/.docker`
* If the current docker context provides a working Docker environment, it will be preferred to further installation detection.
* If Docker Machine is installed, the docker machine environment for the *first* machine found. Docker Machine needs to be on the PATH for this to succeed.
* If you're going to run your tests inside a container, please read [Patterns for running tests inside a docker container](continuous_integration/dind_patterns.md) first.

Although this detection works in the majority of docker installations, for more complex installations, you may need to [customize the docker host detection](/features/configuration/#customizing-docker-host-detection).

When the Docker socket either differs from a unix socket or is running inside the user home directory, Testcontainers will consider the Docker host as remote
and the docker host mounted by containers like Ryuk will default to `/var/run/docker.sock`.
If you have a different configuration, you will need to use the `TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE` environment variable.

```bash
colima start --network-address
export TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE=/var/run/docker.sock
export TESTCONTAINERS_HOST_OVERRIDE=$(colima ls -j | jq -r '.address')
export DOCKER_HOST="unix://${HOME}/.colima/default/docker.sock"
```

## Podman

Expand Down Expand Up @@ -59,49 +75,6 @@ export TESTCONTAINERS_RYUK_DISABLED=true
Previous to version 1.19.0, `export TESTCONTAINERS_RYUK_PRIVILEGED=true`
was required for rootful mode. Starting with 1.19.0, this is no longer required.

## Rancher Desktop

In order to run testcontainers against [Rancher Desktop](https://rancherdesktop.io/) the env vars below should be set.

If you're running Rancher Desktop as an administrator in a MacOS (M1) machine:

Using QEMU emulation

```bash
export TESTCONTAINERS_HOST_OVERRIDE=$(rdctl shell ip a show rd0 | awk '/inet / {sub("/.*",""); print $2}')
```

Using VZ emulation

```bash
export TESTCONTAINERS_HOST_OVERRIDE=$(rdctl shell ip a show vznat | awk '/inet / {sub("/.*",""); print $2}')
```

If you're not running Rancher Desktop as an administrator in a MacOS (M1) machine:

Using VZ emulation

```bash
export DOCKER_HOST=unix://$HOME/.rd/docker.sock
export TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE=/var/run/docker.sock
export TESTCONTAINERS_HOST_OVERRIDE=$(rdctl shell ip a show vznat | awk '/inet / {sub("/.*",""); print $2}')
```

## Docker environment discovery

Testcontainers will try to connect to a Docker daemon using the following strategies in order:

* Environment variables:
* `DOCKER_HOST`
* `DOCKER_TLS_VERIFY`
* `DOCKER_CERT_PATH`
* Defaults:
* `DOCKER_HOST=https://localhost:2376`
* `DOCKER_TLS_VERIFY=1`
* `DOCKER_CERT_PATH=~/.docker`
* If Docker Machine is installed, the docker machine environment for the *first* machine found. Docker Machine needs to be on the PATH for this to succeed.
* If you're going to run your tests inside a container, please read [Patterns for running tests inside a docker container](continuous_integration/dind_patterns.md) first.

## Docker registry authentication

Testcontainers will try to authenticate to registries with supplied config using the following strategies in order:
Expand Down