Skip to content

Commit

Permalink
Merge pull request #19764 from geoand/#19741
Browse files Browse the repository at this point in the history
Use testcontainers (if applicable) to determine if Docker is working
  • Loading branch information
gastaldi authored Aug 30, 2021
2 parents 459f1bd + 7d1a76a commit 0e58c45
Showing 1 changed file with 131 additions and 54 deletions.
185 changes: 131 additions & 54 deletions core/deployment/src/main/java/io/quarkus/deployment/IsDockerWorking.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.net.Socket;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import java.util.function.BooleanSupplier;
import java.util.function.Function;
import java.util.function.Supplier;

import org.jboss.logging.Logger;

Expand All @@ -20,90 +23,164 @@ public class IsDockerWorking implements BooleanSupplier {

private static final Logger LOGGER = Logger.getLogger(IsDockerWorking.class.getName());

private final boolean silent;
private final List<Strategy> strategies;

public IsDockerWorking() {
this(false);
}

public IsDockerWorking(boolean silent) {
this.silent = silent;
this.strategies = List.of(new TestContainersStrategy(silent), new DockerHostStrategy(),
new DockerBinaryStrategy(silent));
}

@Override
public boolean getAsBoolean() {
//remote docker detection
//we don't want to pull in the docker API here
//so we just see if the DOCKER_HOST is set and we can connect to it
//we can't actually verify it is docker listening on the other end
String dockerHost = System.getenv("DOCKER_HOST");
if (dockerHost != null && !dockerHost.startsWith("unix:")) {
try {
URI url = new URI(dockerHost);
try (Socket s = new Socket(url.getHost(), url.getPort())) {
return true;
} catch (IOException e) {
LOGGER.warnf("Unable to connect to DOCKER_HOST URI %s, make sure docker is running on the specified host",
dockerHost);
}
} catch (URISyntaxException e) {
LOGGER.warnf("Unable to parse DOCKER_HOST URI %s, it will be ignored for working docker detection", dockerHost);
for (Strategy strategy : strategies) {
Result result = strategy.get();
if (result == Result.AVAILABLE) {
return true;
}
}
return false;
}

try {
if (!ExecUtil.execSilent("docker", "-v")) {
LOGGER.warn("'docker -v' returned an error code. Make sure your Docker binary is correct");
return false;
}
} catch (Exception e) {
LOGGER.warnf("No Docker binary found or general error: %s", e);
return false;
public static class IsDockerRunningSilent extends IsDockerWorking {
public IsDockerRunningSilent() {
super(true);
}
}

try {
OutputFilter filter = new OutputFilter();
if (ExecUtil.exec(new File("."), filter, "docker", "version", "--format", "'{{.Server.Version}}'")) {
LOGGER.debugf("Docker daemon found. Version: %s", filter.getOutput());
return true;
} else {
private interface Strategy extends Supplier<Result> {

}

/**
* Delegates the check to testcontainers (if the latter is on the classpath)
*/
private static class TestContainersStrategy implements Strategy {

private final boolean silent;

private TestContainersStrategy(boolean silent) {
this.silent = silent;
}

@Override
public Result get() {
try {
Class<?> dockerClientFactoryClass = Thread.currentThread().getContextClassLoader()
.loadClass("org.testcontainers.DockerClientFactory");
Object dockerClientFactoryInstance = dockerClientFactoryClass.getMethod("instance").invoke(null);
boolean isAvailable = (boolean) dockerClientFactoryClass.getMethod("isDockerAvailable")
.invoke(dockerClientFactoryInstance);
return isAvailable ? Result.AVAILABLE : Result.UNAVAILABLE;
} catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
if (!silent) {
LOGGER.warn("Could not determine version of Docker daemon");
LOGGER.debug("Unable to use testcontainers to determine if Docker is working", e);
}
return Result.UNKNOWN;
}
}
}

/**
* Detection using a remote host socket
* We don't want to pull in the docker API here, so we just see if the DOCKER_HOST is set
* and if we can connect to it.
* We can't actually verify it is docker listening on the other end.
* Furthermore, this does not support Unix Sockets
*/
private static class DockerHostStrategy implements Strategy {

@Override
public Result get() {

String dockerHost = System.getenv("DOCKER_HOST");
if (dockerHost != null && !dockerHost.startsWith("unix:")) {
try {
URI url = new URI(dockerHost);
try (Socket s = new Socket(url.getHost(), url.getPort())) {
return Result.AVAILABLE;
} catch (IOException e) {
LOGGER.warnf(
"Unable to connect to DOCKER_HOST URI %s, make sure docker is running on the specified host",
dockerHost);
}
} catch (URISyntaxException e) {
LOGGER.warnf("Unable to parse DOCKER_HOST URI %s, it will be ignored for working docker detection",
dockerHost);
}
return false;
}
} catch (Exception e) {
LOGGER.warn("Unexpected error occurred while determining Docker daemon version", e);
return false;
return Result.UNKNOWN;
}
}

public static class OutputFilter implements Function<InputStream, Runnable> {
private final StringBuilder builder = new StringBuilder();
private static class DockerBinaryStrategy implements Strategy {

private final boolean silent;

private DockerBinaryStrategy(boolean silent) {
this.silent = silent;
}

@Override
public Runnable apply(InputStream is) {
return () -> {
try (InputStreamReader isr = new InputStreamReader(is);
BufferedReader reader = new BufferedReader(isr)) {
public Result get() {
try {
if (!ExecUtil.execSilent("docker", "-v")) {
LOGGER.warn("'docker -v' returned an error code. Make sure your Docker binary is correct");
return Result.UNKNOWN;
}
} catch (Exception e) {
LOGGER.warnf("No Docker binary found or general error: %s", e);
return Result.UNKNOWN;
}

for (String line = reader.readLine(); line != null; line = reader.readLine()) {
builder.append(line);
try {
OutputFilter filter = new OutputFilter();
if (ExecUtil.exec(new File("."), filter, "docker", "version", "--format", "'{{.Server.Version}}'")) {
LOGGER.debugf("Docker daemon found. Version: %s", filter.getOutput());
return Result.AVAILABLE;
} else {
if (!silent) {
LOGGER.warn("Could not determine version of Docker daemon");
}
} catch (IOException e) {
throw new RuntimeException("Error reading stream.", e);
return Result.UNAVAILABLE;
}
};
} catch (Exception e) {
LOGGER.warn("Unexpected error occurred while determining Docker daemon version", e);
return Result.UNKNOWN;
}
}

public String getOutput() {
return builder.toString();
public static class OutputFilter implements Function<InputStream, Runnable> {
private final StringBuilder builder = new StringBuilder();

@Override
public Runnable apply(InputStream is) {
return () -> {

try (InputStreamReader isr = new InputStreamReader(is);
BufferedReader reader = new BufferedReader(isr)) {

for (String line = reader.readLine(); line != null; line = reader.readLine()) {
builder.append(line);
}
} catch (IOException e) {
throw new RuntimeException("Error reading stream.", e);
}
};
}

public String getOutput() {
return builder.toString();
}
}
}

public static class IsDockerRunningSilent extends IsDockerWorking {
public IsDockerRunningSilent() {
super(true);
}
private enum Result {
AVAILABLE,
UNAVAILABLE,
UNKNOWN
}
}

0 comments on commit 0e58c45

Please sign in to comment.