Skip to content

Commit

Permalink
speed up port detection by running the checks as a single command (#1782
Browse files Browse the repository at this point in the history
)
  • Loading branch information
bsideup committed Aug 26, 2019
1 parent 827c4c5 commit 137c510
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 30 deletions.
Expand Up @@ -20,13 +20,13 @@ public class ExternalPortListeningCheck implements Callable<Boolean> {
public Boolean call() {
String address = containerState.getContainerIpAddress();

for (Integer externalPort : externalLivenessCheckPorts) {
externalLivenessCheckPorts.parallelStream().forEach(externalPort -> {
try {
new Socket(address, externalPort).close();
} catch (IOException e) {
throw new IllegalStateException("Socket not listening yet: " + externalPort);
}
}
});
return true;
}
}
@@ -1,9 +1,13 @@
package org.testcontainers.containers.wait.internal;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.testcontainers.containers.Container.ExecResult;
import org.testcontainers.containers.ExecInContainerPattern;
import org.testcontainers.containers.wait.strategy.WaitStrategyTarget;

import java.time.Duration;
import java.time.Instant;
import java.util.Set;

import static java.lang.String.format;
Expand All @@ -12,37 +16,34 @@
* Mechanism for testing that a socket is listening when run from the container being checked.
*/
@RequiredArgsConstructor
@Slf4j
public class InternalCommandPortListeningCheck implements java.util.concurrent.Callable<Boolean> {

private final WaitStrategyTarget waitStrategyTarget;
private final Set<Integer> internalPorts;

@Override
public Boolean call() {
for (Integer internalPort : internalPorts) {
tryPort(internalPort);
String command = "true";

for (int internalPort : internalPorts) {
command += " && ";
command += " (";
command += format("cat /proc/net/tcp{,6} | awk '{print $2}' | grep -i :%x", internalPort);
command += " || ";
command += format("nc -vz -w 1 localhost %d", internalPort);
command += " || ";
command += format("/bin/bash -c '</dev/tcp/localhost/%d'", internalPort);
command += ")";
}

return true;
}

private void tryPort(Integer internalPort) {
String[][] commands = {
{"/bin/sh", "-c", format("cat /proc/net/tcp{,6} | awk '{print $2}' | grep -i :%x", internalPort)},
{"/bin/sh", "-c", format("nc -vz -w 1 localhost %d", internalPort)},
{"/bin/bash", "-c", format("</dev/tcp/localhost/%d", internalPort)}
};

for (String[] command : commands) {
try {
if (ExecInContainerPattern.execInContainer(waitStrategyTarget.getContainerInfo(), command).getExitCode() == 0) {
return;
}
} catch (Exception e) {
throw new IllegalStateException(e);
}
Instant before = Instant.now();
try {
ExecResult result = ExecInContainerPattern.execInContainer(waitStrategyTarget.getContainerInfo(), "/bin/sh", "-c", command);
log.trace("Check for {} took {}", internalPorts, Duration.between(before, Instant.now()));
return result.getExitCode() == 0;
} catch (Exception e) {
throw new IllegalStateException(e);
}

throw new IllegalStateException("Socket not listening yet: " + internalPort);
}
}
Expand Up @@ -25,7 +25,9 @@ public class HostPortWaitStrategy extends AbstractWaitStrategy {
protected void waitUntilReady() {
final Set<Integer> externalLivenessCheckPorts = getLivenessCheckPorts();
if (externalLivenessCheckPorts.isEmpty()) {
log.debug("Liveness check ports of {} is empty. Not waiting.", waitStrategyTarget.getContainerInfo().getName());
if (log.isDebugEnabled()) {
log.debug("Liveness check ports of {} is empty. Not waiting.", waitStrategyTarget.getContainerInfo().getName());
}
return;
}

Expand Down
Expand Up @@ -6,7 +6,7 @@
import org.testcontainers.containers.BindMode;
import org.testcontainers.containers.GenericContainer;

import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows;
import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse;
import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue;

public class InternalCommandPortListeningCheckTest {
Expand All @@ -30,8 +30,8 @@ public void singleListening() {
public void nonListening() {
final InternalCommandPortListeningCheck check = new InternalCommandPortListeningCheck(nginx, ImmutableSet.of(8080, 1234));

assertThrows("InternalCommandPortListeningCheck detects a non-listening port among many",
IllegalStateException.class,
(Runnable) check::call);
final Boolean result = check.call();

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

0 comments on commit 137c510

Please sign in to comment.