Skip to content

Commit

Permalink
Provide an API to force access to the host (#4584)
Browse files Browse the repository at this point in the history
  • Loading branch information
bsideup committed Oct 18, 2021
1 parent 2174928 commit 81317a6
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ public class GenericContainer<SELF extends GenericContainer<SELF>>
@Setter(AccessLevel.NONE)
private boolean shouldBeReused = false;

private boolean hostAccessible = false;

public GenericContainer(@NonNull final DockerImageName dockerImageName) {
this.image = new RemoteDockerImage(dockerImageName);
Expand Down Expand Up @@ -834,6 +835,9 @@ private void applyConfiguration(CreateContainerCmd createCommand) {
createCommand.withNetworkMode(networkForLinks.get());
}

if (hostAccessible) {
PortForwardingContainer.INSTANCE.start();
}
PortForwardingContainer.INSTANCE.getNetwork().ifPresent(it -> {
withExtraHost(INTERNAL_HOST_HOSTNAME, it.getIpAddress());
});
Expand Down Expand Up @@ -1424,6 +1428,18 @@ public SELF withReuse(boolean reusable) {
return self();
}

/**
* Forces access to the tests host machine.
* Use this method if you need to call {@link org.testcontainers.Testcontainers#exposeHostPorts(int...)}
* after you start this container.
*
* @return this
*/
public SELF withAccessToHost(boolean value) {
this.hostAccessible = value;
return self();
}

@Override
public boolean equals(Object o) {
return this == o;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;

public enum PortForwardingContainer {
INSTANCE;
Expand Down Expand Up @@ -68,9 +69,24 @@ public void exposeHostPort(int hostPort, int containerPort) {
}
}

void start() {
getSshConnection();
}

Optional<ContainerNetwork> getNetwork() {
return Optional.ofNullable(container)
.map(GenericContainer::getContainerInfo)
.flatMap(it -> it.getNetworkSettings().getNetworks().values().stream().findFirst());
}

void reset() {
if (container != null) {
container.stop();
}
container = null;

((AtomicReference<?>) (Object) sshConnection).set(null);

exposedPorts.clear();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.google.common.collect.ImmutableMap;
import com.sun.net.httpserver.HttpServer;
import lombok.SneakyThrows;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
Expand Down Expand Up @@ -31,42 +32,52 @@ public static void setUpClass() throws Exception {
});

server.start();
Testcontainers.exposeHostPorts(server.getAddress().getPort());

Testcontainers.exposeHostPorts(ImmutableMap.of(server.getAddress().getPort(), 80));
Testcontainers.exposeHostPorts(ImmutableMap.of(server.getAddress().getPort(), 81));
}

@AfterClass
public static void tearDownClass() throws Exception {
public static void tearDownClass() {
server.stop(0);
}

@After
public void tearDown() {
PortForwardingContainer.INSTANCE.reset();
}

@Test
public void testExposedHostAfterContainerIsStarted() {
try (
GenericContainer<?> container = new GenericContainer<>(TINY_IMAGE)
.withCommand("top")
.withAccessToHost(true)
) {
container.start();
Testcontainers.exposeHostPorts(server.getAddress().getPort());
assertResponse(container, server.getAddress().getPort());
}
}

@Test
public void testExposedHost() throws Exception {
assertResponse(new GenericContainer<>(TINY_IMAGE)
.withCommand("top"),
server.getAddress().getPort());
Testcontainers.exposeHostPorts(server.getAddress().getPort());
assertResponse(new GenericContainer<>(TINY_IMAGE).withCommand("top"), server.getAddress().getPort());
}

@Test
public void testExposedHostWithNetwork() throws Exception {
Testcontainers.exposeHostPorts(server.getAddress().getPort());
try (Network network = Network.newNetwork()) {
assertResponse(new GenericContainer<>(TINY_IMAGE)
.withNetwork(network)
.withCommand("top"),
server.getAddress().getPort());
assertResponse(new GenericContainer<>(TINY_IMAGE).withNetwork(network).withCommand("top"), server.getAddress().getPort());
}
}

@Test
public void testExposedHostPortOnFixedInternalPorts() throws Exception {
assertResponse(new GenericContainer<>(TINY_IMAGE)
.withCommand("top"),
80);
assertResponse(new GenericContainer<>(TINY_IMAGE)
.withCommand("top"),
81);
Testcontainers.exposeHostPorts(ImmutableMap.of(server.getAddress().getPort(), 80));
Testcontainers.exposeHostPorts(ImmutableMap.of(server.getAddress().getPort(), 81));

assertResponse(new GenericContainer<>(TINY_IMAGE).withCommand("top"), 80);
assertResponse(new GenericContainer<>(TINY_IMAGE).withCommand("top"), 81);
}

@SneakyThrows
Expand Down
3 changes: 2 additions & 1 deletion docs/features/networking.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ We need to tell Testcontainers to prepare to expose this port to containers:
<!--/codeinclude-->

!!! warning
Note that the above command should be invoked _before_ containers are started, but _after_ the server on the host was started.
Note that the above command should be invoked _before_ containers are started, but _after_ the server on the host was started.
Alternatively, use `container.withAccessToHost(true)` to force the host access mechanism (you still need to call `exposeHostPorts` to make the port available).

Having done so, we can now access this port from any containers that are launched.
From a container's perspective, the hostname will be `host.testcontainers.internal` and the port will be the same value as `localServerPort`.
Expand Down

0 comments on commit 81317a6

Please sign in to comment.