Skip to content

Commit 81317a6

Browse files
authored
Provide an API to force access to the host (#4584)
1 parent 2174928 commit 81317a6

File tree

4 files changed

+63
-19
lines changed

4 files changed

+63
-19
lines changed

core/src/main/java/org/testcontainers/containers/GenericContainer.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ public class GenericContainer<SELF extends GenericContainer<SELF>>
231231
@Setter(AccessLevel.NONE)
232232
private boolean shouldBeReused = false;
233233

234+
private boolean hostAccessible = false;
234235

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

838+
if (hostAccessible) {
839+
PortForwardingContainer.INSTANCE.start();
840+
}
837841
PortForwardingContainer.INSTANCE.getNetwork().ifPresent(it -> {
838842
withExtraHost(INTERNAL_HOST_HOSTNAME, it.getIpAddress());
839843
});
@@ -1424,6 +1428,18 @@ public SELF withReuse(boolean reusable) {
14241428
return self();
14251429
}
14261430

1431+
/**
1432+
* Forces access to the tests host machine.
1433+
* Use this method if you need to call {@link org.testcontainers.Testcontainers#exposeHostPorts(int...)}
1434+
* after you start this container.
1435+
*
1436+
* @return this
1437+
*/
1438+
public SELF withAccessToHost(boolean value) {
1439+
this.hostAccessible = value;
1440+
return self();
1441+
}
1442+
14271443
@Override
14281444
public boolean equals(Object o) {
14291445
return this == o;

core/src/main/java/org/testcontainers/containers/PortForwardingContainer.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import java.util.Set;
1616
import java.util.UUID;
1717
import java.util.concurrent.ConcurrentHashMap;
18+
import java.util.concurrent.atomic.AtomicReference;
1819

1920
public enum PortForwardingContainer {
2021
INSTANCE;
@@ -68,9 +69,24 @@ public void exposeHostPort(int hostPort, int containerPort) {
6869
}
6970
}
7071

72+
void start() {
73+
getSshConnection();
74+
}
75+
7176
Optional<ContainerNetwork> getNetwork() {
7277
return Optional.ofNullable(container)
7378
.map(GenericContainer::getContainerInfo)
7479
.flatMap(it -> it.getNetworkSettings().getNetworks().values().stream().findFirst());
7580
}
81+
82+
void reset() {
83+
if (container != null) {
84+
container.stop();
85+
}
86+
container = null;
87+
88+
((AtomicReference<?>) (Object) sshConnection).set(null);
89+
90+
exposedPorts.clear();
91+
}
7692
}

core/src/test/java/org/testcontainers/containers/ExposedHostTest.java

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.google.common.collect.ImmutableMap;
44
import com.sun.net.httpserver.HttpServer;
55
import lombok.SneakyThrows;
6+
import org.junit.After;
67
import org.junit.AfterClass;
78
import org.junit.BeforeClass;
89
import org.junit.Test;
@@ -31,42 +32,52 @@ public static void setUpClass() throws Exception {
3132
});
3233

3334
server.start();
34-
Testcontainers.exposeHostPorts(server.getAddress().getPort());
35-
36-
Testcontainers.exposeHostPorts(ImmutableMap.of(server.getAddress().getPort(), 80));
37-
Testcontainers.exposeHostPorts(ImmutableMap.of(server.getAddress().getPort(), 81));
3835
}
3936

4037
@AfterClass
41-
public static void tearDownClass() throws Exception {
38+
public static void tearDownClass() {
4239
server.stop(0);
4340
}
4441

42+
@After
43+
public void tearDown() {
44+
PortForwardingContainer.INSTANCE.reset();
45+
}
46+
47+
@Test
48+
public void testExposedHostAfterContainerIsStarted() {
49+
try (
50+
GenericContainer<?> container = new GenericContainer<>(TINY_IMAGE)
51+
.withCommand("top")
52+
.withAccessToHost(true)
53+
) {
54+
container.start();
55+
Testcontainers.exposeHostPorts(server.getAddress().getPort());
56+
assertResponse(container, server.getAddress().getPort());
57+
}
58+
}
59+
4560
@Test
4661
public void testExposedHost() throws Exception {
47-
assertResponse(new GenericContainer<>(TINY_IMAGE)
48-
.withCommand("top"),
49-
server.getAddress().getPort());
62+
Testcontainers.exposeHostPorts(server.getAddress().getPort());
63+
assertResponse(new GenericContainer<>(TINY_IMAGE).withCommand("top"), server.getAddress().getPort());
5064
}
5165

5266
@Test
5367
public void testExposedHostWithNetwork() throws Exception {
68+
Testcontainers.exposeHostPorts(server.getAddress().getPort());
5469
try (Network network = Network.newNetwork()) {
55-
assertResponse(new GenericContainer<>(TINY_IMAGE)
56-
.withNetwork(network)
57-
.withCommand("top"),
58-
server.getAddress().getPort());
70+
assertResponse(new GenericContainer<>(TINY_IMAGE).withNetwork(network).withCommand("top"), server.getAddress().getPort());
5971
}
6072
}
6173

6274
@Test
6375
public void testExposedHostPortOnFixedInternalPorts() throws Exception {
64-
assertResponse(new GenericContainer<>(TINY_IMAGE)
65-
.withCommand("top"),
66-
80);
67-
assertResponse(new GenericContainer<>(TINY_IMAGE)
68-
.withCommand("top"),
69-
81);
76+
Testcontainers.exposeHostPorts(ImmutableMap.of(server.getAddress().getPort(), 80));
77+
Testcontainers.exposeHostPorts(ImmutableMap.of(server.getAddress().getPort(), 81));
78+
79+
assertResponse(new GenericContainer<>(TINY_IMAGE).withCommand("top"), 80);
80+
assertResponse(new GenericContainer<>(TINY_IMAGE).withCommand("top"), 81);
7081
}
7182

7283
@SneakyThrows

docs/features/networking.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@ We need to tell Testcontainers to prepare to expose this port to containers:
6969
<!--/codeinclude-->
7070

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

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

0 commit comments

Comments
 (0)