Skip to content

Commit

Permalink
Improve container runtime detection
Browse files Browse the repository at this point in the history
Fully resolve the container runtime once and for all. It costs us one
more call when the rootless info is not needed but I prefer things to be
resolved entirely once, rather than using patterns that are a bit
brittle.

Fixes #32246
  • Loading branch information
gsmet authored and Karm committed Apr 13, 2023
1 parent 0505c1c commit f2f24be
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 77 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@ public boolean isContainer() {

@Override
public void setup(boolean processInheritIODisabled) {
if (containerRuntime == ContainerRuntimeUtil.ContainerRuntime.DOCKER
|| containerRuntime == ContainerRuntimeUtil.ContainerRuntime.PODMAN) {
if (containerRuntime != ContainerRuntimeUtil.ContainerRuntime.UNAVAILABLE) {
log.infof("Using %s to run the native image builder", containerRuntime.getExecutableName());
// 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
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package io.quarkus.deployment.pkg.steps;

import static io.quarkus.deployment.pkg.steps.LinuxIDUtil.getLinuxID;
import static io.quarkus.runtime.util.ContainerRuntimeUtil.ContainerRuntime.DOCKER;
import static io.quarkus.runtime.util.ContainerRuntimeUtil.ContainerRuntime.PODMAN;

import java.nio.file.Path;
import java.util.ArrayList;
Expand All @@ -21,14 +19,14 @@ public NativeImageBuildLocalContainerRunner(NativeConfig nativeConfig) {
super(nativeConfig);
if (SystemUtils.IS_OS_LINUX) {
final ArrayList<String> containerRuntimeArgs = new ArrayList<>(Arrays.asList(baseContainerRuntimeArgs));
if (containerRuntime == DOCKER && containerRuntime.isRootless()) {
if (containerRuntime.isDocker() && containerRuntime.isRootless()) {
Collections.addAll(containerRuntimeArgs, "--user", String.valueOf(0));
} else {
String uid = getLinuxID("-ur");
String gid = getLinuxID("-gr");
if (uid != null && gid != null && !uid.isEmpty() && !gid.isEmpty()) {
Collections.addAll(containerRuntimeArgs, "--user", uid + ":" + gid);
if (containerRuntime == PODMAN && containerRuntime.isRootless()) {
if (containerRuntime.isPodman() && containerRuntime.isRootless()) {
// Needed to avoid AccessDeniedExceptions
containerRuntimeArgs.add("--userns=keep-id");
}
Expand All @@ -47,7 +45,7 @@ protected List<String> getContainerRuntimeBuildArgs(Path outputDir) {
}

final String selinuxBindOption;
if (SystemUtils.IS_OS_MAC && containerRuntime == PODMAN) {
if (SystemUtils.IS_OS_MAC && containerRuntime.isPodman()) {
selinuxBindOption = "";
} else {
selinuxBindOption = ":z";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,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 == ContainerRuntimeUtil.ContainerRuntime.PODMAN
&& containerRuntime.isRootless()) {
if (containerRuntime.isPodman() && containerRuntime.isRootless()) {
// Needed to avoid AccessDeniedExceptions
commandLine.add("--userns=keep-id");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,77 +31,96 @@ 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,
* or {@link ContainerRuntime#UNAVAILABLE} if no container runtime is available and the required arg is false.
* @return a fully resolved {@link ContainerRuntime} indicating if Docker or Podman is available and in rootless mode or not
* @throws IllegalStateException if no container runtime was found to build the image
*/
public static ContainerRuntime detectContainerRuntime() {
return detectContainerRuntime(true);
}

public static ContainerRuntime detectContainerRuntime(boolean required) {
final ContainerRuntime containerRuntime = loadContainerRuntimeFromSystemProperty();
ContainerRuntime containerRuntime = loadContainerRuntimeFromSystemProperty();
if (containerRuntime != null) {
return containerRuntime;
} else {
// Docker version 19.03.14, build 5eb3275d40
String dockerVersionOutput;
boolean dockerAvailable;
// Check if Podman is installed
// podman version 2.1.1
String podmanVersionOutput;
boolean podmanAvailable;
if (CONTAINER_EXECUTABLE != null) {
if (CONTAINER_EXECUTABLE.trim().equalsIgnoreCase("docker")) {
dockerVersionOutput = getVersionOutputFor(ContainerRuntime.DOCKER);
dockerAvailable = dockerVersionOutput.contains("Docker version");
if (dockerAvailable) {
storeContainerRuntimeInSystemProperty(ContainerRuntime.DOCKER);
return ContainerRuntime.DOCKER;
}
}
if (CONTAINER_EXECUTABLE.trim().equalsIgnoreCase("podman")) {
podmanVersionOutput = getVersionOutputFor(ContainerRuntime.PODMAN);
podmanAvailable = podmanVersionOutput.startsWith("podman version");
if (podmanAvailable) {
storeContainerRuntimeInSystemProperty(ContainerRuntime.PODMAN);
return ContainerRuntime.PODMAN;
}
}

ContainerRuntime containerRuntimeEnvironment = getContainerRuntimeEnvironment();
if (containerRuntimeEnvironment == ContainerRuntime.UNAVAILABLE) {
storeContainerRuntimeInSystemProperty(ContainerRuntime.UNAVAILABLE);

if (required) {
throw new IllegalStateException("No container runtime was found. "
+ "Make sure you have either Docker or Podman installed in your environment.");
}

return ContainerRuntime.UNAVAILABLE;
}

// we have a working container environment, let's resolve it fully
containerRuntime = fullyResolveContainerRuntime(containerRuntimeEnvironment);

storeContainerRuntimeInSystemProperty(containerRuntime);

return containerRuntime;
}

private static ContainerRuntime getContainerRuntimeEnvironment() {
// Docker version 19.03.14, build 5eb3275d40
String dockerVersionOutput;
boolean dockerAvailable;
// Check if Podman is installed
// podman version 2.1.1
String podmanVersionOutput;
boolean podmanAvailable;

if (CONTAINER_EXECUTABLE != null) {
if (CONTAINER_EXECUTABLE.trim().equalsIgnoreCase("docker")) {
dockerVersionOutput = getVersionOutputFor(ContainerRuntime.DOCKER);
dockerAvailable = dockerVersionOutput.contains("Docker version");
if (dockerAvailable) {
return ContainerRuntime.DOCKER;
}
log.warn("quarkus.native.container-runtime config property must be set to either podman or docker " +
"and the executable must be available. Ignoring it.");
}
dockerVersionOutput = getVersionOutputFor(ContainerRuntime.DOCKER);
dockerAvailable = dockerVersionOutput.contains("Docker version");
if (dockerAvailable) {
// Check if "docker" is an alias to "podman"
if (dockerVersionOutput.startsWith("podman version") ||
dockerVersionOutput.startsWith("podman.exe version")) {
storeContainerRuntimeInSystemProperty(ContainerRuntime.PODMAN);
if (CONTAINER_EXECUTABLE.trim().equalsIgnoreCase("podman")) {
podmanVersionOutput = getVersionOutputFor(ContainerRuntime.PODMAN);
podmanAvailable = podmanVersionOutput.startsWith("podman version");
if (podmanAvailable) {
return ContainerRuntime.PODMAN;
}
storeContainerRuntimeInSystemProperty(ContainerRuntime.DOCKER);
return ContainerRuntime.DOCKER;
}
podmanVersionOutput = getVersionOutputFor(ContainerRuntime.PODMAN);
podmanAvailable = podmanVersionOutput.startsWith("podman version") ||
podmanVersionOutput.startsWith("podman.exe version");
if (podmanAvailable) {
storeContainerRuntimeInSystemProperty(ContainerRuntime.PODMAN);
log.warn("quarkus.native.container-runtime config property must be set to either podman or docker " +
"and the executable must be available. Ignoring it.");
}

dockerVersionOutput = getVersionOutputFor(ContainerRuntime.DOCKER);
dockerAvailable = dockerVersionOutput.contains("Docker version");
if (dockerAvailable) {
// Check if "docker" is an alias to "podman"
if (dockerVersionOutput.startsWith("podman version") ||
dockerVersionOutput.startsWith("podman.exe version")) {
return ContainerRuntime.PODMAN;
}
return ContainerRuntime.DOCKER;
}
podmanVersionOutput = getVersionOutputFor(ContainerRuntime.PODMAN);
podmanAvailable = podmanVersionOutput.startsWith("podman version") ||
podmanVersionOutput.startsWith("podman.exe version");
if (podmanAvailable) {
return ContainerRuntime.PODMAN;
}

storeContainerRuntimeInSystemProperty(ContainerRuntime.UNAVAILABLE);
return ContainerRuntime.UNAVAILABLE;
}

if (required) {
throw new IllegalStateException("No container runtime was found. "
+ "Make sure you have either Docker or Podman installed in your environment.");
}
private static ContainerRuntime fullyResolveContainerRuntime(ContainerRuntime containerRuntimeEnvironment) {
boolean rootless = getRootlessStateFor(containerRuntimeEnvironment);

return ContainerRuntime.UNAVAILABLE;
if (!rootless) {
return containerRuntimeEnvironment;
}

return containerRuntimeEnvironment == ContainerRuntime.DOCKER ? ContainerRuntime.DOCKER_ROOTLESS
: ContainerRuntime.PODMAN_ROOTLESS;
}

private static ContainerRuntime loadContainerRuntimeFromSystemProperty() {
Expand Down Expand Up @@ -195,16 +214,19 @@ private static boolean getRootlessStateFor(ContainerRuntime containerRuntime) {
* Supported Container runtimes
*/
public enum ContainerRuntime {
DOCKER("docker" + (OS.current() == OS.WINDOWS ? ".exe" : "")),
PODMAN("podman" + (OS.current() == OS.WINDOWS ? ".exe" : "")),
UNAVAILABLE(null);
DOCKER("docker", false),
DOCKER_ROOTLESS("docker", true),
PODMAN("podman", false),
PODMAN_ROOTLESS("podman", true),
UNAVAILABLE(null, false);

private Boolean rootless;
private final String executableName;

private String executableName;
private final boolean rootless;

ContainerRuntime(String executableName) {
this.executableName = executableName;
ContainerRuntime(String executableName, boolean rootless) {
this.executableName = executableName + (OS.current() == OS.WINDOWS ? ".exe" : "");
this.rootless = rootless;
}

public String getExecutableName() {
Expand All @@ -215,17 +237,15 @@ public String getExecutableName() {
return executableName;
}

public boolean isDocker() {
return this == DOCKER || this == DOCKER_ROOTLESS;
}

public boolean isPodman() {
return this == PODMAN || this == PODMAN_ROOTLESS;
}

public boolean isRootless() {
if (rootless != null) {
return rootless;
} else {
if (this != ContainerRuntime.UNAVAILABLE) {
rootless = getRootlessStateFor(this);
} else {
throw new IllegalStateException("No container runtime was found. "
+ "Make sure you have either Docker or Podman installed in your environment.");
}
}
return rootless;
}

Expand Down

0 comments on commit f2f24be

Please sign in to comment.