diff --git a/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java index e0afba9af93..e9bdbd90bea 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java @@ -2,10 +2,19 @@ import com.github.dockerjava.core.DefaultDockerClientConfig; import com.github.dockerjava.core.DockerClientConfig; +import org.testcontainers.utility.TestcontainersConfiguration; + +import java.util.Optional; /** * Use environment variables and system properties (as supported by the underlying DockerClient DefaultConfigBuilder) * to try and locate a docker environment. + *
+ * Resolution order is: + *
.testcontainers.properties
file in the user's home directory or
* a testcontainers.properties
found on the classpath.
+ *
+ * Note that when searching environment variables, the prefix `TESTCONTAINERS_` will usually be applied to the
+ * property name, which will be converted to upper-case with underscore separators. This prefix will not be added
+ * if the property name begins `docker.`.
*
* @param propertyName name of configuration file property (dot-separated lower case)
* @return the found value, or null if not set
@@ -220,6 +228,10 @@ public String getEnvVarOrProperty(@NotNull final String propertyName, @Nullable
/**
* Gets a configured setting from an environment variable (if present) or a configuration file property otherwise.
* The configuration file will be the .testcontainers.properties
file in the user's home directory.
+ *
+ * Note that when searching environment variables, the prefix `TESTCONTAINERS_` will usually be applied to the
+ * property name, which will be converted to upper-case with underscore separators. This prefix will not be added
+ * if the property name begins `docker.`.
*
* @param propertyName name of configuration file property (dot-separated lower case)
* @return the found value, or null if not set
@@ -230,8 +242,11 @@ public String getEnvVarOrUserProperty(@NotNull final String propertyName, @Nulla
}
/**
- * Gets a configured setting from a the user's configuration file.
- * The configuration file will be the .testcontainers.properties
file in the user's home directory.
+ * Gets a configured setting from an environment variable.
+ *
+ * Note that when searching environment variables, the prefix `TESTCONTAINERS_` will usually be applied to the + * property name, which will be converted to upper-case with underscore separators. This prefix will not be added + * if the property name begins `docker.`. * * @param propertyName name of configuration file property (dot-separated lower case) * @return the found value, or null if not set diff --git a/core/src/test/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategyTest.java b/core/src/test/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategyTest.java new file mode 100644 index 00000000000..91f61848d69 --- /dev/null +++ b/core/src/test/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategyTest.java @@ -0,0 +1,91 @@ +package org.testcontainers.dockerclient; + +import com.github.dockerjava.core.DefaultDockerClientConfig; +import com.github.dockerjava.core.LocalDirectorySSLConfig; +import com.github.dockerjava.transport.SSLConfig; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.testcontainers.utility.MockTestcontainersConfigurationRule; +import org.testcontainers.utility.TestcontainersConfiguration; + +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; + +/** + * Test that we can use Testcontainers configuration file to override settings. We assume that docker-java has test + * coverage for detection of environment variables (e.g. DOCKER_HOST) and its own properties config file. + */ +@RunWith(MockitoJUnitRunner.class) +public class EnvironmentAndSystemPropertyClientProviderStrategyTest { + + @Rule + public MockTestcontainersConfigurationRule mockConfig = new MockTestcontainersConfigurationRule(); + private URI defaultDockerHost; + private com.github.dockerjava.core.SSLConfig defaultSSLConfig; + + @Before + public void checkEnvironmentClear() { + // If docker-java picks up non-default settings from the environment, our test needs to know to expect those + DefaultDockerClientConfig defaultConfig = DefaultDockerClientConfig.createDefaultConfigBuilder().build(); + defaultDockerHost = defaultConfig.getDockerHost(); + defaultSSLConfig = defaultConfig.getSSLConfig(); + } + + @Test + public void testWhenConfigAbsent() { + Mockito.doReturn(null).when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.host"), isNull()); + Mockito.doReturn(null).when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.tls.verify"), isNull()); + Mockito.doReturn(null).when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.cert.path"), isNull()); + + EnvironmentAndSystemPropertyClientProviderStrategy strategy = new EnvironmentAndSystemPropertyClientProviderStrategy(); + + TransportConfig transportConfig = strategy.getTransportConfig(); + assertEquals(defaultDockerHost, transportConfig.getDockerHost()); + assertEquals(defaultSSLConfig, transportConfig.getSslConfig()); + } + + @Test + public void testWhenDockerHostPresent() { + Mockito.doReturn("tcp://1.2.3.4:2375").when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.host"), isNull()); + Mockito.doReturn(null).when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.tls.verify"), isNull()); + Mockito.doReturn(null).when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.cert.path"), isNull()); + + EnvironmentAndSystemPropertyClientProviderStrategy strategy = new EnvironmentAndSystemPropertyClientProviderStrategy(); + + TransportConfig transportConfig = strategy.getTransportConfig(); + assertEquals("tcp://1.2.3.4:2375", transportConfig.getDockerHost().toString()); + assertEquals(defaultSSLConfig, transportConfig.getSslConfig()); + } + + @Test + public void testWhenDockerHostAndSSLConfigPresent() throws IOException { + Path tempDir = Files.createTempDirectory("testcontainers-test"); + String tempDirPath = tempDir.toAbsolutePath().toString(); + + Mockito.doReturn("tcp://1.2.3.4:2375").when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.host"), isNull()); + Mockito.doReturn("1").when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.tls.verify"), isNull()); + Mockito.doReturn(tempDirPath).when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.cert.path"), isNull()); + + EnvironmentAndSystemPropertyClientProviderStrategy strategy = new EnvironmentAndSystemPropertyClientProviderStrategy(); + + TransportConfig transportConfig = strategy.getTransportConfig(); + assertEquals("tcp://1.2.3.4:2375", transportConfig.getDockerHost().toString()); + + SSLConfig sslConfig = transportConfig.getSslConfig(); + assertNotNull(sslConfig); + assertTrue(sslConfig instanceof LocalDirectorySSLConfig); + assertEquals(tempDirPath, ((LocalDirectorySSLConfig) sslConfig).getDockerCertPath()); + } +} diff --git a/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java b/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java index e5300bce58d..0fca36f0f2f 100644 --- a/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java +++ b/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java @@ -113,6 +113,27 @@ public void shouldReadChecksFromEnvironment() { assertTrue("checks disabled via env var", newConfig().isDisableChecks()); } + @Test + public void shouldReadDockerSettingsFromEnvironmentWithoutTestcontainersPrefix() { + userProperties.remove("docker.foo"); + environment.put("DOCKER_FOO", "some value"); + assertEquals("reads unprefixed env vars for docker. settings", "some value", newConfig().getEnvVarOrUserProperty("docker.foo", "default")); + } + + @Test + public void shouldNotReadDockerSettingsFromEnvironmentWithTestcontainersPrefix() { + userProperties.remove("docker.foo"); + environment.put("TESTCONTAINERS_DOCKER_FOO", "some value"); + assertEquals("reads unprefixed env vars for docker. settings", "default", newConfig().getEnvVarOrUserProperty("docker.foo", "default")); + } + + @Test + public void shouldReadDockerSettingsFromUserProperties() { + environment.remove("DOCKER_FOO"); + userProperties.put("docker.foo", "some value"); + assertEquals("reads unprefixed user properties for docker. settings", "some value", newConfig().getEnvVarOrUserProperty("docker.foo", "default")); + } + @Test public void shouldNotReadDockerClientStrategyFromClasspathProperties() { String currentValue = newConfig().getDockerClientStrategyClassName(); @@ -129,7 +150,7 @@ public void shouldReadDockerClientStrategyFromUserProperties() { @Test public void shouldReadDockerClientStrategyFromEnvironment() { - userProperties.remove("docker.client.strategy"); + userProperties.remove("docker.client.strategy"); environment.put("TESTCONTAINERS_DOCKER_CLIENT_STRATEGY", "foo"); assertEquals("Docker client strategy is changed by env var", "foo", newConfig().getDockerClientStrategyClassName()); } diff --git a/docs/features/configuration.md b/docs/features/configuration.md index da7bbd82d60..2336ec840c5 100644 --- a/docs/features/configuration.md +++ b/docs/features/configuration.md @@ -96,9 +96,9 @@ but does not allow starting privileged containers, you can turn off the Ryuk con ## Customizing Docker host detection -Testcontainers will attempt to detect the Docker environment and configure everything. +Testcontainers will attempt to detect the Docker environment and configure everything to work automatically. -However, sometimes a customization is required. For that, you can provide the following environment variables: +However, sometimes customization is required. Testcontainers will respect the following **environment variables**: > **DOCKER_HOST** = unix:///var/run/docker.sock > See [Docker environment variables](https://docs.docker.com/engine/reference/commandline/cli/#environment-variables) @@ -110,3 +110,14 @@ However, sometimes a customization is required. For that, you can provide the fo > **TESTCONTAINERS_HOST_OVERRIDE** > Docker's host on which ports are exposed. > Example: `docker.svc.local` + +For advanced users, the Docker host connection can be configured **via configuration** in `~/.testcontainers.properties`. +Note that these settings require use of the `EnvironmentAndSystemPropertyClientProviderStrategy`. The example below +illustrates usage: + +```properties +docker.client.strategy=org.testcontainers.dockerclient.EnvironmentAndSystemPropertyClientProviderStrategy +docker.host=tcp\://my.docker.host\:1234 # Equivalent to the DOCKER_HOST environment variable. Colons should be escaped. +docker.tls.verify=1 # Equivalent to the DOCKER_TLS_VERIFY environment variable +docker.cert.path=/some/path # Equivalent to the DOCKER_CERT_PATH environment variable +```