From be7251b5414363c968916d101d6411b556d5b283 Mon Sep 17 00:00:00 2001 From: Joplek99 Date: Thu, 7 Nov 2024 03:14:41 +0100 Subject: [PATCH] Check if container belongs to a network when using exposeHostPorts (#8298) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #7335 --------- Co-authored-by: EddĂș MelĂ©ndez Gonzales --- .../containers/GenericContainer.java | 11 +- .../containers/ExposedHostTest.java | 100 +++++++++++++++++- 2 files changed, 108 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index b2436b77286..acd70f69cab 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -623,7 +623,14 @@ private void connectToPortForwardingNetwork(String networkMode) { .map(ContainerNetwork::getNetworkID) .ifPresent(networkId -> { if (!Arrays.asList(networkId, "none", "host").contains(networkMode)) { - dockerClient.connectToNetworkCmd().withContainerId(containerId).withNetworkId(networkId).exec(); + com.github.dockerjava.api.model.Network network = + this.dockerClient.inspectNetworkCmd().withNetworkId(networkId).exec(); + if (!network.getContainers().containsKey(this.containerId)) { + this.dockerClient.connectToNetworkCmd() + .withContainerId(this.containerId) + .withNetworkId(networkId) + .exec(); + } } }); } @@ -827,7 +834,7 @@ private void applyConfiguration(CreateContainerCmd createCommand) { withExtraHost(INTERNAL_HOST_HOSTNAME, it.getIpAddress()); }); - String[] extraHostsArray = extraHosts.stream().toArray(String[]::new); + String[] extraHostsArray = extraHosts.stream().distinct().toArray(String[]::new); createCommand.withExtraHosts(extraHostsArray); if (workingDirectory != null) { diff --git a/core/src/test/java/org/testcontainers/containers/ExposedHostTest.java b/core/src/test/java/org/testcontainers/containers/ExposedHostTest.java index 4a3142d9461..4ce316fcf80 100644 --- a/core/src/test/java/org/testcontainers/containers/ExposedHostTest.java +++ b/core/src/test/java/org/testcontainers/containers/ExposedHostTest.java @@ -7,13 +7,21 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; +import org.testcontainers.DockerClientFactory; import org.testcontainers.TestImages; import org.testcontainers.Testcontainers; +import org.testcontainers.utility.TestcontainersConfiguration; +import java.io.IOException; import java.io.OutputStream; import java.net.InetSocketAddress; +import java.util.List; +import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assumptions.assumeThat; public class ExposedHostTest { @@ -33,7 +41,6 @@ public static void setUpClass() throws Exception { } } ); - server.start(); } @@ -82,6 +89,61 @@ public void testExposedHostPortOnFixedInternalPorts() { assertResponse(new GenericContainer<>(tinyContainerDef()), 81); } + @Test + public void testExposedHostWithReusableContainerAndFixedNetworkName() throws IOException, InterruptedException { + assumeThat(TestcontainersConfiguration.getInstance().environmentSupportsReuse()).isTrue(); + Network network = createReusableNetwork(UUID.randomUUID()); + Testcontainers.exposeHostPorts(server.getAddress().getPort()); + + GenericContainer container = new GenericContainer<>(tinyContainerDef()).withReuse(true).withNetwork(network); + container.start(); + + assertHttpResponseFromHost(container, server.getAddress().getPort()); + + PortForwardingContainer.INSTANCE.reset(); + Testcontainers.exposeHostPorts(server.getAddress().getPort()); + + GenericContainer reusedContainer = new GenericContainer<>(tinyContainerDef()) + .withReuse(true) + .withNetwork(network); + reusedContainer.start(); + + assertThat(reusedContainer.getContainerId()).isEqualTo(container.getContainerId()); + assertHttpResponseFromHost(reusedContainer, server.getAddress().getPort()); + + container.stop(); + reusedContainer.stop(); + DockerClientFactory.lazyClient().removeNetworkCmd(network.getId()).exec(); + } + + @Test + public void testExposedHostOnFixedInternalPortsWithReusableContainerAndFixedNetworkName() + throws IOException, InterruptedException { + assumeThat(TestcontainersConfiguration.getInstance().environmentSupportsReuse()).isTrue(); + Network network = createReusableNetwork(UUID.randomUUID()); + Testcontainers.exposeHostPorts(ImmutableMap.of(server.getAddress().getPort(), 1234)); + + GenericContainer container = new GenericContainer<>(tinyContainerDef()).withReuse(true).withNetwork(network); + container.start(); + + assertHttpResponseFromHost(container, 1234); + + PortForwardingContainer.INSTANCE.reset(); + Testcontainers.exposeHostPorts(ImmutableMap.of(server.getAddress().getPort(), 1234)); + + GenericContainer reusedContainer = new GenericContainer<>(tinyContainerDef()) + .withReuse(true) + .withNetwork(network); + reusedContainer.start(); + + assertThat(reusedContainer.getContainerId()).isEqualTo(container.getContainerId()); + assertHttpResponseFromHost(reusedContainer, 1234); + + container.stop(); + reusedContainer.stop(); + DockerClientFactory.lazyClient().removeNetworkCmd(network.getId()).exec(); + } + @SneakyThrows protected void assertResponse(GenericContainer container, int port) { try { @@ -108,4 +170,40 @@ private static class TinyContainerDef extends ContainerDef { setCommand("top"); } } + + private void assertHttpResponseFromHost(GenericContainer container, int port) + throws IOException, InterruptedException { + String httpResponseFromHost = container + .execInContainer("wget", "-O", "-", "http://host.testcontainers.internal:" + port) + .getStdout(); + assertThat(httpResponseFromHost).isEqualTo("Hello World!"); + } + + private static Network createReusableNetwork(UUID name) { + String networkName = name.toString(); + Network network = new Network() { + @Override + public String getId() { + return networkName; + } + + @Override + public void close() {} + + @Override + public Statement apply(Statement base, Description description) { + return null; + } + }; + + List networks = DockerClientFactory + .lazyClient() + .listNetworksCmd() + .withNameFilter(networkName) + .exec(); + if (networks.isEmpty()) { + Network.builder().createNetworkCmdModifier(cmd -> cmd.withName(networkName)).build().getId(); + } + return network; + } }