Skip to content

Commit

Permalink
Add TestcontainersHostPropertyClientStrategy (#7053)
Browse files Browse the repository at this point in the history
New strategy to use `tc.host` in `~/.testcontainers.properties` to try and connect
to docker.

This strategy ensure that user-defined variables such as `DOCKER_HOST`, 
`TESTCONTAINERS_HOST_OVERRIDE` and `TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE` 
do not have an effect.

This strategy takes precedence over other strategies.
  • Loading branch information
eddumelendez authored May 26, 2023
1 parent fc770df commit eb5eb8a
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -158,9 +158,11 @@ public TransportConfig getTransportConfig() {

@UnstableAPI
public String getRemoteDockerUnixSocketPath() {
String dockerSocketOverride = System.getenv("TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE");
if (!StringUtils.isBlank(dockerSocketOverride)) {
return dockerSocketOverride;
if (this.strategy != null && this.strategy.allowUserOverrides()) {
String dockerSocketOverride = System.getenv("TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE");
if (!StringUtils.isBlank(dockerSocketOverride)) {
return dockerSocketOverride;
}
}

URI dockerHost = getTransportConfig().getDockerHost();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@

/**
* Mechanism to find a viable Docker client configuration according to the host system environment.
* <p>
* The order is:
* <ul>
* <li>{@code TestcontainersHostPropertyClientProviderStrategy}</li>
* <li>{@code EnvironmentAndSystemPropertyClientProviderStrategy}</li>
* <li>Persistable {@code DockerClientProviderStrategy} in <code>~/.testcontainers.properties</code></li>
* <li>Other strategies order by priority</li>
* </ul>
*/
@Slf4j
public abstract class DockerClientProviderStrategy {
Expand Down Expand Up @@ -90,6 +98,10 @@ protected boolean isPersistable() {
return true;
}

public boolean allowUserOverrides() {
return true;
}

/**
* @return highest to lowest priority value
*/
Expand Down Expand Up @@ -217,7 +229,8 @@ public static DockerClientProviderStrategy getFirstValidStrategy(List<DockerClie
List<String> configurationFailures = new ArrayList<>();
List<DockerClientProviderStrategy> allStrategies = new ArrayList<>();

// The environment has the highest priority
// Manually enforce priority independent of priority property of strategy
allStrategies.add(new TestcontainersHostPropertyClientProviderStrategy());
allStrategies.add(new EnvironmentAndSystemPropertyClientProviderStrategy());

// Next strategy to try out is the one configured using the Testcontainers configuration mechanism
Expand Down Expand Up @@ -401,16 +414,23 @@ public static DockerClient getClientForConfig(TransportConfig transportConfig) {

public synchronized String getDockerHostIpAddress() {
if (dockerHostIpAddress == null) {
dockerHostIpAddress = resolveDockerHostIpAddress(getDockerClient(), getTransportConfig().getDockerHost());
dockerHostIpAddress =
resolveDockerHostIpAddress(
getDockerClient(),
getTransportConfig().getDockerHost(),
allowUserOverrides()
);
}
return dockerHostIpAddress;
}

@VisibleForTesting
static String resolveDockerHostIpAddress(DockerClient client, URI dockerHost) {
String hostOverride = System.getenv("TESTCONTAINERS_HOST_OVERRIDE");
if (!StringUtils.isBlank(hostOverride)) {
return hostOverride;
static String resolveDockerHostIpAddress(DockerClient client, URI dockerHost, boolean allowUserOverrides) {
if (allowUserOverrides) {
String hostOverride = System.getenv("TESTCONTAINERS_HOST_OVERRIDE");
if (!StringUtils.isBlank(hostOverride)) {
return hostOverride;
}
}

switch (dockerHost.getScheme()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package org.testcontainers.dockerclient;

import com.github.dockerjava.core.DefaultDockerClientConfig;
import com.github.dockerjava.core.DockerClientConfig;
import org.testcontainers.utility.TestcontainersConfiguration;

import java.util.Optional;

/**
* Use <code>tc.host</code> in <code>~/.testcontainers.properties</code>
* to try and locate a docker environment.
*
* @deprecated this class is used by the SPI and should not be used directly
*/
@Deprecated
public final class TestcontainersHostPropertyClientProviderStrategy extends DockerClientProviderStrategy {

public static final int PRIORITY = EnvironmentAndSystemPropertyClientProviderStrategy.PRIORITY - 10;

private final DockerClientConfig dockerClientConfig;

public TestcontainersHostPropertyClientProviderStrategy() {
this(DefaultDockerClientConfig.createDefaultConfigBuilder());
}

public TestcontainersHostPropertyClientProviderStrategy(DefaultDockerClientConfig.Builder configBuilder) {
Optional<String> tcHost = Optional.ofNullable(
TestcontainersConfiguration.getInstance().getUserProperty("tc.host", null)
);

tcHost.ifPresent(configBuilder::withDockerHost);
this.dockerClientConfig = configBuilder.build();
}

@Override
public String getDescription() {
return "Testcontainers Host with tc.host=" + this.dockerClientConfig.getDockerHost();
}

@Override
public TransportConfig getTransportConfig() throws InvalidConfigurationException {
return TransportConfig
.builder()
.dockerHost(dockerClientConfig.getDockerHost())
.sslConfig(dockerClientConfig.getSSLConfig())
.build();
}

@Override
protected int getPriority() {
return PRIORITY;
}

@Override
protected boolean isPersistable() {
return false;
}

@Override
public boolean allowUserOverrides() {
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -279,18 +279,16 @@ public String getEnvVarOrUserProperty(@NotNull final String propertyName, @Nulla
}

/**
* Gets a configured setting from an environment variable.
* <p>
* 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.`.
* Gets a configured setting from <code>~/.testcontainers.properties</code>.
*
* @param propertyName name of configuration file property (dot-separated lower case)
* @return the found value, or null if not set
*/
@Contract("_, !null -> !null")
public String getUserProperty(@NotNull final String propertyName, @Nullable final String defaultValue) {
return getConfigurable(propertyName, defaultValue);
return this.userProperties.get(propertyName) != null
? (String) this.userProperties.get(propertyName)
: defaultValue;
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
org.testcontainers.dockerclient.TestcontainersHostPropertyClientProviderStrategy
org.testcontainers.dockerclient.EnvironmentAndSystemPropertyClientProviderStrategy
org.testcontainers.dockerclient.UnixSocketClientProviderStrategy
org.testcontainers.dockerclient.DockerMachineClientProviderStrategy
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ public void getDockerHostIpAddressShouldReturnLocalhostWhenUnixSocket() {

String actual = DockerClientProviderStrategy.resolveDockerHostIpAddress(
client,
URI.create("unix:///var/run/docker.sock")
URI.create("unix:///var/run/docker.sock"),
true
);
assertThat(actual).isEqualTo("localhost");
}
Expand All @@ -28,7 +29,8 @@ public void getDockerHostIpAddressShouldReturnLocalhostWhenUnixSocket() {
public void getDockerHostIpAddressShouldReturnDockerHostIpWhenHttpsUri() {
String actual = DockerClientProviderStrategy.resolveDockerHostIpAddress(
client,
URI.create("http://12.23.34.45")
URI.create("http://12.23.34.45"),
true
);
assertThat(actual).isEqualTo("12.23.34.45");
}
Expand All @@ -37,7 +39,8 @@ public void getDockerHostIpAddressShouldReturnDockerHostIpWhenHttpsUri() {
public void getDockerHostIpAddressShouldReturnDockerHostIpWhenTcpUri() {
String actual = DockerClientProviderStrategy.resolveDockerHostIpAddress(
client,
URI.create("tcp://12.23.34.45")
URI.create("tcp://12.23.34.45"),
true
);
assertThat(actual).isEqualTo("12.23.34.45");
}
Expand All @@ -46,7 +49,8 @@ public void getDockerHostIpAddressShouldReturnDockerHostIpWhenTcpUri() {
public void getDockerHostIpAddressShouldReturnNullWhenUnsupportedUriScheme() {
String actual = DockerClientProviderStrategy.resolveDockerHostIpAddress(
client,
URI.create("gopher://12.23.34.45")
URI.create("gopher://12.23.34.45"),
true
);
assertThat(actual).isNull();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package org.testcontainers.dockerclient;

import com.github.dockerjava.core.DefaultDockerClientConfig;
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.net.URI;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;

@RunWith(MockitoJUnitRunner.class)
public class TestcontainersHostPropertyClientProviderStrategyTest {

@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();
this.defaultDockerHost = defaultConfig.getDockerHost();
this.defaultSSLConfig = defaultConfig.getSSLConfig();
}

@Test
public void tcHostPropertyIsProvided() {
Mockito
.doReturn("tcp://127.0.0.1:9000")
.when(TestcontainersConfiguration.getInstance())
.getUserProperty(eq("tc.host"), isNull());

TestcontainersHostPropertyClientProviderStrategy strategy = new TestcontainersHostPropertyClientProviderStrategy();

TransportConfig transportConfig = strategy.getTransportConfig();
assertThat(transportConfig.getDockerHost().toString()).isEqualTo("tcp://127.0.0.1:9000");
assertThat(transportConfig.getSslConfig()).isEqualTo(this.defaultSSLConfig);
}

@Test
public void tcHostPropertyIsNotProvided() {
Mockito.doReturn(null).when(TestcontainersConfiguration.getInstance()).getUserProperty(eq("tc.host"), isNull());

TestcontainersHostPropertyClientProviderStrategy strategy = new TestcontainersHostPropertyClientProviderStrategy();

TransportConfig transportConfig = strategy.getTransportConfig();
assertThat(transportConfig.getDockerHost()).isEqualTo(this.defaultDockerHost);
assertThat(transportConfig.getSslConfig()).isEqualTo(this.defaultSSLConfig);
}
}

0 comments on commit eb5eb8a

Please sign in to comment.