diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index 989aaf36be3..db741271ac7 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -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; @@ -69,6 +71,8 @@ public class DockerClientFactory { public static final Map DEFAULT_LABELS = markerLabels(); + private static final String DEFAULT_DOCKER_DOCKER_PATH = "/var/run/docker.sock"; + static Map markerLabels() { String testcontainersVersion = TESTCONTAINERS_VERSION == null ? "unspecified" : TESTCONTAINERS_VERSION; @@ -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 */ diff --git a/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java index 969610373d1..d2dcd4d03ee 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java @@ -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. *

- * Resolution order is: + * Docker Host resolution order is: *

    *
  1. DOCKER_HOST env var
  2. *
  3. docker.host in ~/.testcontainers.properties
  4. + *
  5. docker host pointed by the Docker context
  6. + *
+ *

+ * Docker context resolution order is + *

    + *
  1. DOCKER_CONTEXT env var
  2. + *
  3. docker.context in ~/.testcontainers.properties
  4. + *
  5. current docker context pointed by the Docker config
  6. + *
+ *

+ * Docker config resolution order is + *

    + *
  1. DOCKER_CONFIG env var
  2. + *
  3. $HOME/.docker
  4. *
* * @deprecated this class is used by the SPI and should not be used directly @@ -43,9 +57,14 @@ public EnvironmentAndSystemPropertyClientProviderStrategy() { case "auto": Optional 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(); diff --git a/docs/supported_docker_environment/index.md b/docs/supported_docker_environment/index.md index 0bdac109245..dc1a86fb00b 100644 --- a/docs/supported_docker_environment/index.md +++ b/docs/supported_docker_environment/index.md @@ -11,7 +11,7 @@ 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, @@ -19,16 +19,32 @@ 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 @@ -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: