diff --git a/core/src/main/java/org/testcontainers/containers/Container.java b/core/src/main/java/org/testcontainers/containers/Container.java index abea7fef576..5d0ce15cfd0 100644 --- a/core/src/main/java/org/testcontainers/containers/Container.java +++ b/core/src/main/java/org/testcontainers/containers/Container.java @@ -123,6 +123,14 @@ default void addFileSystemBind(final String hostPath, final String containerPath */ void addExposedPorts(int... ports); + /** + * Add an exposed port with the specified protocol. + * + * @param port the port to expose + * @param protocol the protocol (TCP or UDP) + */ + void addExposedPort(int port, InternetProtocol protocol); + /** * Specify the {@link WaitStrategy} to use to determine if the container is ready. * diff --git a/core/src/main/java/org/testcontainers/containers/ContainerState.java b/core/src/main/java/org/testcontainers/containers/ContainerState.java index e19f7a85310..beb30a3f99f 100644 --- a/core/src/main/java/org/testcontainers/containers/ContainerState.java +++ b/core/src/main/java/org/testcontainers/containers/ContainerState.java @@ -145,7 +145,7 @@ default Integer getFirstMappedPort() { } /** - * Get the actual mapped port for a given port exposed by the container. + * Get the actual mapped port for a given TCP port exposed by the container. * It should be used in conjunction with {@link #getHost()}. *
* Note: The returned port number might be outdated (for instance, after disconnecting from a network and reconnecting @@ -156,8 +156,29 @@ default Integer getFirstMappedPort() { * @return the port that the exposed port is mapped to, or null if it is not exposed * @see #getContainerInfo() * @see #getCurrentContainerInfo() + * @see #getMappedPort(int, InternetProtocol) */ default Integer getMappedPort(int originalPort) { + return getMappedPort(originalPort, InternetProtocol.TCP); + } + + /** + * Get the actual mapped port for a given port and protocol exposed by the container. + * It should be used in conjunction with {@link #getHost()}. + *
+ * Note: The returned port number might be outdated (for instance, after disconnecting from a network and reconnecting
+ * again). If you always need up-to-date value, override the {@link #getContainerInfo()} to return the
+ * {@link #getCurrentContainerInfo()}.
+ *
+ * @param originalPort the original port that is exposed
+ * @param protocol the protocol (TCP or UDP) of the exposed port
+ * @return the port that the exposed port is mapped to
+ * @throws IllegalStateException if the container is not started
+ * @throws IllegalArgumentException if the requested port is not mapped
+ * @see #getContainerInfo()
+ * @see #getCurrentContainerInfo()
+ */
+ default Integer getMappedPort(int originalPort, InternetProtocol protocol) {
Preconditions.checkState(
this.getContainerId() != null,
"Mapped port can only be obtained after the container is started"
@@ -166,13 +187,19 @@ default Integer getMappedPort(int originalPort) {
Ports.Binding[] binding = new Ports.Binding[0];
final InspectContainerResponse containerInfo = this.getContainerInfo();
if (containerInfo != null) {
- binding = containerInfo.getNetworkSettings().getPorts().getBindings().get(new ExposedPort(originalPort));
+ ExposedPort exposedPort = new ExposedPort(
+ originalPort,
+ com.github.dockerjava.api.model.InternetProtocol.parse(protocol.name())
+ );
+ binding = containerInfo.getNetworkSettings().getPorts().getBindings().get(exposedPort);
}
if (binding != null && binding.length > 0 && binding[0] != null) {
return Integer.valueOf(binding[0].getHostPortSpec());
} else {
- throw new IllegalArgumentException("Requested port (" + originalPort + ") is not mapped");
+ throw new IllegalArgumentException(
+ "Requested port (" + originalPort + "/" + protocol.toDockerNotation() + ") is not mapped"
+ );
}
}
diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java
index 0fe944433ae..d5c1e86cbe7 100644
--- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java
+++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java
@@ -1048,6 +1048,14 @@ public void addExposedPorts(int... ports) {
this.containerDef.addExposedTcpPorts(ports);
}
+ @Override
+ public void addExposedPort(int port, InternetProtocol protocol) {
+ this.containerDef.addExposedPort(
+ port,
+ com.github.dockerjava.api.model.InternetProtocol.parse(protocol.name())
+ );
+ }
+
/**
* {@inheritDoc}
*/
diff --git a/core/src/test/java/org/testcontainers/containers/ContainerStateTest.java b/core/src/test/java/org/testcontainers/containers/ContainerStateTest.java
index 7b37bc1926f..8a74dfcec35 100644
--- a/core/src/test/java/org/testcontainers/containers/ContainerStateTest.java
+++ b/core/src/test/java/org/testcontainers/containers/ContainerStateTest.java
@@ -1,12 +1,20 @@
package org.testcontainers.containers;
+import com.github.dockerjava.api.command.InspectContainerResponse;
+import com.github.dockerjava.api.model.ExposedPort;
+import com.github.dockerjava.api.model.NetworkSettings;
+import com.github.dockerjava.api.model.Ports;
+import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -35,4 +43,112 @@ void test(String name, String testSet, List