Skip to content

Honour the active Docker CLI context for the default client#11826

Open
ebriney wants to merge 6 commits into
testcontainers:mainfrom
ebriney:docker-context-support
Open

Honour the active Docker CLI context for the default client#11826
ebriney wants to merge 6 commits into
testcontainers:mainfrom
ebriney:docker-context-support

Conversation

@ebriney
Copy link
Copy Markdown

@ebriney ebriney commented Jun 3, 2026

Summary

Testcontainers picks a Docker daemon by walking a chain of provider strategies, but until now the chain did not look at the user's active Docker CLI context. On a machine where Docker Desktop ships the daemon at ~/.docker/run/docker.sock and currentContext in ~/.docker/config.json points at desktop-linux, the existing strategies still happen to work because DockerDesktopClientProviderStrategy hard-codes that path. But anyone who switches context — to OrbStack, a remote tcp:// host, an SSH endpoint, etc. — ends up driving a different daemon from the rest of their toolchain.

This PR adds a new SPI-registered strategy that mirrors docker(1)'s own resolution:

  1. DOCKER_HOST env var (handled by the existing env-var strategy).
  2. DOCKER_CONTEXT env var.
  3. currentContext in $DOCKER_CONFIG/config.json (default ~/.docker/config.json).
  4. Built-in default context (no metadata file — strategy steps aside and lets the local-socket strategies handle it).

For named contexts, the endpoint host is read from $DOCKER_CONFIG/contexts/meta/<sha256(name)>/meta.json, and per-context TLS material under contexts/tls/<sha256(name)>/docker/ is wired into a LocalDirectorySSLConfig when present.

What's in the change

  • DockerContextResolver — pure utility that does the path math and JSON parsing. Public API is just two static methods plus a DockerContextEndpoint value object. Env-var lookup is parameterised so it can be tested without touching the process environment.
  • DockerContextClientProviderStrategy — the SPI hook. Priority is EnvironmentAndSystemPropertyClientProviderStrategy.PRIORITY - 10, i.e. between the env-var strategy (100) and the unix-socket strategy (80), so DOCKER_HOST still wins as it does in the CLI. isApplicable() returns false for the built-in default context and for unsupported schemes (e.g. ssh://), letting the chain fall through cleanly.
  • A second public constructor, DockerContextClientProviderStrategy(String contextName), lets callers pin a specific context by name, bypassing the auto-resolution.
  • SPI registration in META-INF/services/...DockerClientProviderStrategy between the env-var and unix-socket strategies.

Tests

  • DockerContextResolverTest — 11 unit tests over a temp config dir covering the resolution chain (env precedence, blank values, missing config), endpoint parsing, TLS discovery, malformed inputs, and a sanity check that the SHA-256 directory name matches what Docker Desktop actually writes for desktop-linux.
  • DockerContextClientProviderStrategyTest — 9 tests covering isApplicable() for default/ssh/named contexts, the explicit-name constructor, missing-socket diagnostics, TLS wiring, priority bounds, plus two live tests that exercise an actual Docker daemon end-to-end (one via the strategy, one through DockerClientFactory.isUsing(...)). Live tests use assumeThat to skip cleanly when no daemon is reachable.
  • Fixtures use forward-slash unix:// paths on POSIX and npipe:// on Windows so JSON parsing stays portable.

Docs

Updates docs/supported_docker_environment/index.md (discovery chain) and docs/features/configuration.md (DOCKER_CONTEXT and DOCKER_CONFIG env vars).

Test plan

  • ./gradlew :testcontainers:test --tests "org.testcontainers.dockerclient.DockerContext*" passes on macOS with Docker Desktop running (live tests execute).
  • Same on macOS with Docker Desktop stopped (live tests skip rather than fail).
  • ./gradlew :testcontainers:checkstyleMain :testcontainers:checkstyleTest :testcontainers:spotlessApply is clean.
  • CI runs on Linux / Windows.

ebriney and others added 6 commits June 3, 2026 11:59
Mirrors the docker(1) resolution chain (DOCKER_HOST > DOCKER_CONTEXT >
currentContext in ~/.docker/config.json) and parses the matching
meta.json under ~/.docker/contexts/meta/<sha256(name)>/, exposing the
endpoint host URI and any TLS material to callers.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds DockerContextClientProviderStrategy (registered via the SPI between
the env-var and unix-socket strategies) so a default-configured
Testcontainers picks the same daemon as `docker info`. This matters for
setups where Docker is only reachable through the configured context
(e.g. Docker Desktop on macOS without a /var/run/docker.sock symlink,
OrbStack, or any DOCKER_CONTEXT override).

A public DockerContextClientProviderStrategy(String contextName)
constructor mirrors EnvironmentAndSystemPropertyClientProviderStrategy's
package-private hook so callers can pin a specific context by name.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The two unix-socket fixtures embedded an absolute temp-file path into a
JSON meta document. On Windows that path contains backslashes, which are
invalid JSON escapes and broke Jackson parsing before the production code
ran. Use an OS-appropriate endpoint instead: a real unix socket on POSIX
(so the socket-existence check is still exercised) and an npipe address on
Windows (no backing file needed). Both forms use forward slashes and embed
safely into the JSON fixtures.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
connectsToLocalDockerThroughActiveContext only guarded the ping/info
calls; when Docker Desktop is stopped, getTransportConfig already throws
InvalidConfigurationException for the missing socket, which surfaced as
a hard failure instead of a skip. Move the transport-resolve and client
construction inside the existing try/catch so any failure on the path to
the daemon translates to a JUnit assumption skip.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mention the new context-aware lookup in the Docker environment
discovery list and call out DOCKER_CONTEXT / DOCKER_CONFIG in the
host-detection env-var reference.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Result of running ./gradlew :testcontainers:spotlessApply over the new
Docker context sources; no behavior change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ebriney ebriney requested a review from a team as a code owner June 3, 2026 13:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant