Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix internal port check when other ports are opened as well on the target container #2363

Merged
merged 9 commits into from Mar 10, 2020
Expand Up @@ -66,10 +66,6 @@ public Container.ExecResult execInContainer(InspectContainerResponse containerIn

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

dockerClient
.execCreateCmd(containerId)
.withCmd(command);

log.debug("{}: Running \"exec\" command: {}", containerName, String.join(" ", command));
final ExecCreateCmdResponse execCreateCmdResponse = dockerClient.execCreateCmd(containerId)
.withAttachStdout(true).withAttachStderr(true).withCmd(command).exec();
Expand Down
Expand Up @@ -29,7 +29,7 @@ public Boolean call() {
for (int internalPort : internalPorts) {
command += " && ";
command += " (";
command += format("cat /proc/net/tcp* | awk '{print $2}' | grep -i :%x", internalPort);
command += format("cat /proc/net/tcp* | awk '{print $2}' | grep -i ':0*%x'", internalPort);
command += " || ";
command += format("nc -vz -w 1 localhost %d", internalPort);
command += " || ";
Expand Down
Expand Up @@ -3,23 +3,47 @@
import com.google.common.collect.ImmutableSet;
import org.junit.Rule;
import org.junit.Test;
import org.testcontainers.containers.BindMode;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.rnorth.ducttape.TimeoutException;
import org.rnorth.ducttape.unreliables.Unreliables;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.images.builder.ImageFromDockerfile;

import java.util.concurrent.TimeUnit;

import static java.util.Arrays.asList;
import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse;
import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue;

@RunWith(Parameterized.class)
public class InternalCommandPortListeningCheckTest {

// Linking a custom configuration into the container so that nginx is listening on port 8080. This is necessary to prove
// that the command formatting uses the correct casing for hexadecimal numbers (i.e. 1F90 and not 1f90).
@Parameterized.Parameters(name = "{index} - {0}")
public static Iterable<Object[]> data() {
return asList(
new Object[][]{
{"internal-port-check-dockerfile/Dockerfile-tcp"},
{"internal-port-check-dockerfile/Dockerfile-nc"},
{"internal-port-check-dockerfile/Dockerfile-bash"},
});
}

@Rule
public GenericContainer nginx = new GenericContainer<>("nginx:1.9.4")
.withClasspathResourceMapping("nginx_on_8080.conf", "/etc/nginx/conf.d/default.conf", BindMode.READ_ONLY);
public GenericContainer container;

public InternalCommandPortListeningCheckTest(String dockerfile) {
container = new GenericContainer(new ImageFromDockerfile()
bsideup marked this conversation as resolved.
Show resolved Hide resolved
.withFileFromClasspath("Dockerfile", dockerfile)
.withFileFromClasspath("nginx.conf", "internal-port-check-dockerfile/nginx.conf")
);
}

@Test
public void singleListening() {
final InternalCommandPortListeningCheck check = new InternalCommandPortListeningCheck(nginx, ImmutableSet.of(8080));
final InternalCommandPortListeningCheck check = new InternalCommandPortListeningCheck(container, ImmutableSet.of(8080));

Unreliables.retryUntilTrue(5, TimeUnit.SECONDS, check);

final Boolean result = check.call();

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

@Test
public void nonListening() {
final InternalCommandPortListeningCheck check = new InternalCommandPortListeningCheck(nginx, ImmutableSet.of(8080, 1234));
final InternalCommandPortListeningCheck check = new InternalCommandPortListeningCheck(container, ImmutableSet.of(8080, 1234));

try {
Unreliables.retryUntilTrue(5, TimeUnit.SECONDS, check);
} catch (TimeoutException e) {
// we expect it to timeout
}

final Boolean result = check.call();

assertFalse("InternalCommandPortListeningCheck detects a non-listening port among many", result);
}

@Test
public void lowAndHighPortListening() {
final InternalCommandPortListeningCheck check = new InternalCommandPortListeningCheck(container, ImmutableSet.of(100, 8080));

Unreliables.retryUntilTrue(5, TimeUnit.SECONDS, check);

final Boolean result = check.call();

assertTrue("InternalCommandPortListeningCheck identifies a low and a high port", result);
}
}
@@ -0,0 +1,11 @@
FROM nginx:1.17-alpine

RUN apk add bash

# Make sure the /proc/net/tcp* check fails in this container
RUN rm /usr/bin/awk

# Make sure the nc check fails in this container
RUN rm /usr/bin/nc

ADD nginx.conf /etc/nginx/conf.d/default.conf
@@ -0,0 +1,9 @@
FROM nginx:1.17-alpine

# If this fails, you ended up using a base image with bash installed. Consider removing /bin/bash in this case
RUN if bash -c true &> /dev/null; then exit 1; fi

# Make sure the /proc/net/tcp* check fails in this container
RUN rm /usr/bin/awk

ADD nginx.conf /etc/nginx/conf.d/default.conf
@@ -0,0 +1,9 @@
FROM nginx:1.17-alpine

# If this fails, you ended up using a base image with bash installed. Consider removing /bin/bash in this case
RUN if bash -c true &> /dev/null; then exit 1; fi

# Make sure the nc check fails in this container
RUN rm /usr/bin/nc

ADD nginx.conf /etc/nginx/conf.d/default.conf
@@ -0,0 +1,8 @@
# This configuration makes Nginx listen on port port 8080

server {
# 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)
listen 8080;
# Port 100 is necessary to ensure that the /proc/net/tcp* check also succeeds with trailing zeros in the hexadecimal port
listen 100;
}
5 changes: 0 additions & 5 deletions core/src/test/resources/nginx_on_8080.conf

This file was deleted.