Skip to content

Commit

Permalink
Merge pull request #24238 from geoand/#24231
Browse files Browse the repository at this point in the history
Detect container runtime when using Jib
  • Loading branch information
geoand authored Mar 11, 2022
2 parents f07126d + a6745e0 commit 6d5abf7
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 71 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

import java.io.File;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.OptionalInt;

import io.quarkus.deployment.util.ContainerRuntimeUtil;
import io.quarkus.runtime.annotations.ConfigGroup;
import io.quarkus.runtime.annotations.ConfigItem;
import io.quarkus.runtime.annotations.ConfigPhase;
Expand Down Expand Up @@ -229,7 +229,7 @@ public String getEffectiveBuilderImage() {
* a container build is always done.
*/
@ConfigItem
public Optional<ContainerRuntime> containerRuntime;
public Optional<ContainerRuntimeUtil.ContainerRuntime> containerRuntime;

/**
* Options to pass to the container runtime
Expand Down Expand Up @@ -442,18 +442,6 @@ public static class Compression {
public Optional<List<String>> additionalArgs;
}

/**
* Supported Container runtimes
*/
public static enum ContainerRuntime {
DOCKER,
PODMAN;

public String getExecutableName() {
return this.name().toLowerCase();
}
}

/**
* Supported Builder Image providers/distributions
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package io.quarkus.deployment.pkg.steps;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
Expand All @@ -15,22 +14,22 @@
import org.jboss.logging.Logger;

import io.quarkus.deployment.pkg.NativeConfig;
import io.quarkus.deployment.util.FileUtil;
import io.quarkus.deployment.util.ContainerRuntimeUtil;
import io.quarkus.deployment.util.ProcessUtil;

public abstract class NativeImageBuildContainerRunner extends NativeImageBuildRunner {

private static final Logger log = Logger.getLogger(NativeImageBuildContainerRunner.class);

final NativeConfig nativeConfig;
protected final NativeConfig.ContainerRuntime containerRuntime;
protected final ContainerRuntimeUtil.ContainerRuntime containerRuntime;
String[] baseContainerRuntimeArgs;
protected final String outputPath;
private final String containerName;

public NativeImageBuildContainerRunner(NativeConfig nativeConfig, Path outputDir) {
this.nativeConfig = nativeConfig;
containerRuntime = nativeConfig.containerRuntime.orElseGet(NativeImageBuildContainerRunner::detectContainerRuntime);
containerRuntime = nativeConfig.containerRuntime.orElseGet(ContainerRuntimeUtil::detectContainerRuntime);
log.infof("Using %s to run the native image builder", containerRuntime.getExecutableName());

this.baseContainerRuntimeArgs = new String[] { "--env", "LANG=C", "--rm" };
Expand All @@ -41,8 +40,8 @@ public NativeImageBuildContainerRunner(NativeConfig nativeConfig, Path outputDir

@Override
public void setup(boolean processInheritIODisabled) {
if (containerRuntime == NativeConfig.ContainerRuntime.DOCKER
|| containerRuntime == NativeConfig.ContainerRuntime.PODMAN) {
if (containerRuntime == ContainerRuntimeUtil.ContainerRuntime.DOCKER
|| containerRuntime == ContainerRuntimeUtil.ContainerRuntime.PODMAN) {
// we pull the docker image in order to give users an indication of which step the process is at
// it's not strictly necessary we do this, however if we don't the subsequent version command
// will appear to block and no output will be shown
Expand Down Expand Up @@ -125,51 +124,4 @@ protected String[] buildCommand(String dockerCmd, List<String> containerRuntimeA
.flatMap(Function.identity()).toArray(String[]::new);
}

/**
* @return {@link NativeConfig.ContainerRuntime#DOCKER} if it's available, or {@link NativeConfig.ContainerRuntime#PODMAN}
* if the podman
* executable exists in the environment or if the docker executable is an alias to podman
* @throws IllegalStateException if no container runtime was found to build the image
*/
public static NativeConfig.ContainerRuntime detectContainerRuntime() {
// Docker version 19.03.14, build 5eb3275d40
String dockerVersionOutput = getVersionOutputFor(NativeConfig.ContainerRuntime.DOCKER);
boolean dockerAvailable = dockerVersionOutput.contains("Docker version");
// Check if Podman is installed
// podman version 2.1.1
String podmanVersionOutput = getVersionOutputFor(NativeConfig.ContainerRuntime.PODMAN);
boolean podmanAvailable = podmanVersionOutput.startsWith("podman version");
if (dockerAvailable) {
// Check if "docker" is an alias to "podman"
if (dockerVersionOutput.equals(podmanVersionOutput)) {
return NativeConfig.ContainerRuntime.PODMAN;
}
return NativeConfig.ContainerRuntime.DOCKER;
} else if (podmanAvailable) {
return NativeConfig.ContainerRuntime.PODMAN;
} else {
throw new IllegalStateException("No container runtime was found to run the native image builder. "
+ "Make sure you have Docker or Podman installed in your environment.");
}
}

private static String getVersionOutputFor(NativeConfig.ContainerRuntime containerRuntime) {
Process versionProcess = null;
try {
ProcessBuilder pb = new ProcessBuilder(containerRuntime.getExecutableName(), "--version")
.redirectErrorStream(true);
versionProcess = pb.start();
versionProcess.waitFor();
return new String(FileUtil.readFileContents(versionProcess.getInputStream()), StandardCharsets.UTF_8);
} catch (IOException | InterruptedException e) {
// If an exception is thrown in the process, just return an empty String
log.debugf(e, "Failure to read version output from %s", containerRuntime.getExecutableName());
return "";
} finally {
if (versionProcess != null) {
versionProcess.destroy();
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import org.apache.commons.lang3.SystemUtils;

import io.quarkus.deployment.pkg.NativeConfig;
import io.quarkus.deployment.util.ContainerRuntimeUtil;
import io.quarkus.deployment.util.FileUtil;

public class NativeImageBuildLocalContainerRunner extends NativeImageBuildContainerRunner {
Expand All @@ -23,7 +24,7 @@ public NativeImageBuildLocalContainerRunner(NativeConfig nativeConfig, Path outp
String gid = getLinuxID("-gr");
if (uid != null && gid != null && !uid.isEmpty() && !gid.isEmpty()) {
Collections.addAll(containerRuntimeArgs, "--user", uid + ":" + gid);
if (containerRuntime == NativeConfig.ContainerRuntime.PODMAN) {
if (containerRuntime == ContainerRuntimeUtil.ContainerRuntime.PODMAN) {
// Needed to avoid AccessDeniedExceptions
containerRuntimeArgs.add("--userns=keep-id");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import io.quarkus.deployment.pkg.builditem.ArtifactResultBuildItem;
import io.quarkus.deployment.pkg.builditem.NativeImageBuildItem;
import io.quarkus.deployment.pkg.builditem.UpxCompressedBuildItem;
import io.quarkus.deployment.util.ContainerRuntimeUtil;
import io.quarkus.deployment.util.FileUtil;
import io.quarkus.deployment.util.ProcessUtil;

Expand Down Expand Up @@ -99,8 +100,8 @@ private boolean runUpxInContainer(NativeImageBuildItem nativeImage, NativeConfig
List<String> extraArgs = nativeConfig.compression.additionalArgs.orElse(Collections.emptyList());

List<String> commandLine = new ArrayList<>();
NativeConfig.ContainerRuntime containerRuntime = nativeConfig.containerRuntime
.orElseGet(NativeImageBuildContainerRunner::detectContainerRuntime);
ContainerRuntimeUtil.ContainerRuntime containerRuntime = nativeConfig.containerRuntime
.orElseGet(ContainerRuntimeUtil::detectContainerRuntime);
commandLine.add(containerRuntime.getExecutableName());

commandLine.add("run");
Expand All @@ -121,7 +122,7 @@ private boolean runUpxInContainer(NativeImageBuildItem nativeImage, NativeConfig
String gid = getLinuxID("-gr");
if (uid != null && gid != null && !uid.isEmpty() && !gid.isEmpty()) {
Collections.addAll(commandLine, "--user", uid + ":" + gid);
if (containerRuntime == NativeConfig.ContainerRuntime.PODMAN) {
if (containerRuntime == ContainerRuntimeUtil.ContainerRuntime.PODMAN) {
// Needed to avoid AccessDeniedExceptions
commandLine.add("--userns=keep-id");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package io.quarkus.deployment.util;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

import org.jboss.logging.Logger;

public final class ContainerRuntimeUtil {

private static final Logger log = Logger.getLogger(ContainerRuntimeUtil.class);

private ContainerRuntimeUtil() {
}

/**
* @return {@link ContainerRuntime#DOCKER} if it's available, or {@link ContainerRuntime#PODMAN}
* if the podman
* executable exists in the environment or if the docker executable is an alias to podman
* @throws IllegalStateException if no container runtime was found to build the image
*/
public static ContainerRuntime detectContainerRuntime() {
// Docker version 19.03.14, build 5eb3275d40
String dockerVersionOutput = getVersionOutputFor(ContainerRuntime.DOCKER);
boolean dockerAvailable = dockerVersionOutput.contains("Docker version");
// Check if Podman is installed
// podman version 2.1.1
String podmanVersionOutput = getVersionOutputFor(ContainerRuntime.PODMAN);
boolean podmanAvailable = podmanVersionOutput.startsWith("podman version");
if (dockerAvailable) {
// Check if "docker" is an alias to "podman"
if (dockerVersionOutput.equals(podmanVersionOutput)) {
return ContainerRuntime.PODMAN;
}
return ContainerRuntime.DOCKER;
} else if (podmanAvailable) {
return ContainerRuntime.PODMAN;
} else {
throw new IllegalStateException("No container runtime was found to. "
+ "Make sure you have Docker or Podman installed in your environment.");
}
}

private static String getVersionOutputFor(ContainerRuntime containerRuntime) {
Process versionProcess = null;
try {
ProcessBuilder pb = new ProcessBuilder(containerRuntime.getExecutableName(), "--version")
.redirectErrorStream(true);
versionProcess = pb.start();
versionProcess.waitFor();
return new String(FileUtil.readFileContents(versionProcess.getInputStream()), StandardCharsets.UTF_8);
} catch (IOException | InterruptedException e) {
// If an exception is thrown in the process, just return an empty String
log.debugf(e, "Failure to read version output from %s", containerRuntime.getExecutableName());
return "";
} finally {
if (versionProcess != null) {
versionProcess.destroy();
}
}
}

/**
* Supported Container runtimes
*/
public enum ContainerRuntime {
DOCKER,
PODMAN;

public String getExecutableName() {
return this.name().toLowerCase();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
import io.quarkus.deployment.pkg.builditem.UpxCompressedBuildItem;
import io.quarkus.deployment.pkg.steps.JarResultBuildStep;
import io.quarkus.deployment.pkg.steps.NativeBuild;
import io.quarkus.deployment.util.ContainerRuntimeUtil;
import io.quarkus.fs.util.ZipUtils;
import io.quarkus.maven.dependency.ResolvedDependency;

Expand Down Expand Up @@ -266,6 +267,11 @@ private Containerizer createContainerizer(ContainerImageConfig containerImageCon
dockerDaemonImage.setDockerExecutable(Paths.get(jibConfigExecutableName.get()));
} else if (dockerConfigExecutableName.isPresent()) {
dockerDaemonImage.setDockerExecutable(Paths.get(dockerConfigExecutableName.get()));
} else {
// detect the container runtime instead of falling back to 'docker' as the default
ContainerRuntimeUtil.ContainerRuntime detectedContainerRuntime = ContainerRuntimeUtil.detectContainerRuntime();
log.infof("Using %s to run the native image builder", detectedContainerRuntime.getExecutableName());
dockerDaemonImage.setDockerExecutable(Paths.get(detectedContainerRuntime.getExecutableName()));
}
containerizer = Containerizer.to(dockerDaemonImage);
}
Expand Down

0 comments on commit 6d5abf7

Please sign in to comment.