Skip to content

Commit

Permalink
Merge pull request #32309 from gsmet/rootless-detection
Browse files Browse the repository at this point in the history
Improve container runtime detection
  • Loading branch information
gastaldi authored Apr 13, 2023
2 parents 8d5c770 + f2f24be commit bbe0ade
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 bbe0ade

Please sign in to comment.