Skip to content

Commit b437227

Browse files
authored
Fix internal port check when other ports are opened as well on the target container (#2363)
* Remove redundant execCreateCmd call This one is never used and probably causes some garbage on the docker side. * Allow leading zeros in /proc/net/tcp* when doing internal port listen check When at least one process is listening for a port that is significantly higher then the port we're checking, this check will fail as the checked port will contain leading zeros in the output of "cat /proc/net/tcp". This commit fixes this by changing the grep to ":0*XXX". * Surround grep parameter with ' Otherwise unnecessary shell expansion is tried * Refactor/Rewrite InternalCommandPortListeningCheckTest to test all possible checks This commit refactors InternalCommandPortListeningCheckTest to work with 3 different nginx based containers, each being able to only succeed one of the 3 tests performed in InternalCommandPortListeningCheck. Each test is now executed agains all 3 containers to ensure that all tests work as expected. * Add test for low+high ports This test should fail without the leading zeros fix found a few commits before this one. * Check for bash existence instead of only mentioning it in a comment * Rename nginx_on_8080.conf to nginx.conf It's not only port 8080 anymore, so better not have it in the name. * Make container a @rule again by moving initialization into contructor * Wait a few seconds for InternalCommandPortListeningCheck to pass
1 parent bf187bb commit b437227

File tree

8 files changed

+86
-17
lines changed

8 files changed

+86
-17
lines changed

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

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,6 @@ public Container.ExecResult execInContainer(InspectContainerResponse containerIn
6666

6767
DockerClient dockerClient = DockerClientFactory.instance().client();
6868

69-
dockerClient
70-
.execCreateCmd(containerId)
71-
.withCmd(command);
72-
7369
log.debug("{}: Running \"exec\" command: {}", containerName, String.join(" ", command));
7470
final ExecCreateCmdResponse execCreateCmdResponse = dockerClient.execCreateCmd(containerId)
7571
.withAttachStdout(true).withAttachStderr(true).withCmd(command).exec();

core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public Boolean call() {
2929
for (int internalPort : internalPorts) {
3030
command += " && ";
3131
command += " (";
32-
command += format("cat /proc/net/tcp* | awk '{print $2}' | grep -i :%x", internalPort);
32+
command += format("cat /proc/net/tcp* | awk '{print $2}' | grep -i ':0*%x'", internalPort);
3333
command += " || ";
3434
command += format("nc -vz -w 1 localhost %d", internalPort);
3535
command += " || ";

core/src/test/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheckTest.java

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,47 @@
33
import com.google.common.collect.ImmutableSet;
44
import org.junit.Rule;
55
import org.junit.Test;
6-
import org.testcontainers.containers.BindMode;
6+
import org.junit.runner.RunWith;
7+
import org.junit.runners.Parameterized;
8+
import org.rnorth.ducttape.TimeoutException;
9+
import org.rnorth.ducttape.unreliables.Unreliables;
710
import org.testcontainers.containers.GenericContainer;
11+
import org.testcontainers.images.builder.ImageFromDockerfile;
812

13+
import java.util.concurrent.TimeUnit;
14+
15+
import static java.util.Arrays.asList;
916
import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse;
1017
import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue;
1118

19+
@RunWith(Parameterized.class)
1220
public class InternalCommandPortListeningCheckTest {
1321

14-
// Linking a custom configuration into the container so that nginx is listening on port 8080. This is necessary to prove
15-
// that the command formatting uses the correct casing for hexadecimal numbers (i.e. 1F90 and not 1f90).
22+
@Parameterized.Parameters(name = "{index} - {0}")
23+
public static Iterable<Object[]> data() {
24+
return asList(
25+
new Object[][]{
26+
{"internal-port-check-dockerfile/Dockerfile-tcp"},
27+
{"internal-port-check-dockerfile/Dockerfile-nc"},
28+
{"internal-port-check-dockerfile/Dockerfile-bash"},
29+
});
30+
}
31+
1632
@Rule
17-
public GenericContainer nginx = new GenericContainer<>("nginx:1.9.4")
18-
.withClasspathResourceMapping("nginx_on_8080.conf", "/etc/nginx/conf.d/default.conf", BindMode.READ_ONLY);
33+
public GenericContainer container;
34+
35+
public InternalCommandPortListeningCheckTest(String dockerfile) {
36+
container = new GenericContainer(new ImageFromDockerfile()
37+
.withFileFromClasspath("Dockerfile", dockerfile)
38+
.withFileFromClasspath("nginx.conf", "internal-port-check-dockerfile/nginx.conf")
39+
);
40+
}
1941

2042
@Test
2143
public void singleListening() {
22-
final InternalCommandPortListeningCheck check = new InternalCommandPortListeningCheck(nginx, ImmutableSet.of(8080));
44+
final InternalCommandPortListeningCheck check = new InternalCommandPortListeningCheck(container, ImmutableSet.of(8080));
45+
46+
Unreliables.retryUntilTrue(5, TimeUnit.SECONDS, check);
2347

2448
final Boolean result = check.call();
2549

@@ -28,10 +52,27 @@ public void singleListening() {
2852

2953
@Test
3054
public void nonListening() {
31-
final InternalCommandPortListeningCheck check = new InternalCommandPortListeningCheck(nginx, ImmutableSet.of(8080, 1234));
55+
final InternalCommandPortListeningCheck check = new InternalCommandPortListeningCheck(container, ImmutableSet.of(8080, 1234));
56+
57+
try {
58+
Unreliables.retryUntilTrue(5, TimeUnit.SECONDS, check);
59+
} catch (TimeoutException e) {
60+
// we expect it to timeout
61+
}
3262

3363
final Boolean result = check.call();
3464

3565
assertFalse("InternalCommandPortListeningCheck detects a non-listening port among many", result);
3666
}
67+
68+
@Test
69+
public void lowAndHighPortListening() {
70+
final InternalCommandPortListeningCheck check = new InternalCommandPortListeningCheck(container, ImmutableSet.of(100, 8080));
71+
72+
Unreliables.retryUntilTrue(5, TimeUnit.SECONDS, check);
73+
74+
final Boolean result = check.call();
75+
76+
assertTrue("InternalCommandPortListeningCheck identifies a low and a high port", result);
77+
}
3778
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
FROM nginx:1.17-alpine
2+
3+
RUN apk add bash
4+
5+
# Make sure the /proc/net/tcp* check fails in this container
6+
RUN rm /usr/bin/awk
7+
8+
# Make sure the nc check fails in this container
9+
RUN rm /usr/bin/nc
10+
11+
ADD nginx.conf /etc/nginx/conf.d/default.conf
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
FROM nginx:1.17-alpine
2+
3+
# If this fails, you ended up using a base image with bash installed. Consider removing /bin/bash in this case
4+
RUN if bash -c true &> /dev/null; then exit 1; fi
5+
6+
# Make sure the /proc/net/tcp* check fails in this container
7+
RUN rm /usr/bin/awk
8+
9+
ADD nginx.conf /etc/nginx/conf.d/default.conf
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
FROM nginx:1.17-alpine
2+
3+
# If this fails, you ended up using a base image with bash installed. Consider removing /bin/bash in this case
4+
RUN if bash -c true &> /dev/null; then exit 1; fi
5+
6+
# Make sure the nc check fails in this container
7+
RUN rm /usr/bin/nc
8+
9+
ADD nginx.conf /etc/nginx/conf.d/default.conf
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# This configuration makes Nginx listen on port port 8080
2+
3+
server {
4+
# Port 8080 is necessary to prove that the command formatting in the /proc/net/tcp* check uses the correct casing for hexadecimal numbers (i.e. 1F90 and not 1f90)
5+
listen 8080;
6+
# Port 100 is necessary to ensure that the /proc/net/tcp* check also succeeds with trailing zeros in the hexadecimal port
7+
listen 100;
8+
}

core/src/test/resources/nginx_on_8080.conf

Lines changed: 0 additions & 5 deletions
This file was deleted.

0 commit comments

Comments
 (0)