From 35d11ed64621c9eb01eba284bff5558adf72a937 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Thu, 10 Mar 2022 15:41:51 +0200 Subject: [PATCH] Detect container runtime in when using Jib The detection uses the same method Quarkus already used when building the native binary using a container build Fixes: #24231 (cherry picked from commit a6745e07b8f4399f5a0c8c10989e993074871a02) --- .../quarkus/deployment/pkg/NativeConfig.java | 8 +- .../NativeImageBuildContainerRunner.java | 3 + .../deployment/util/ContainerRuntimeUtil.java | 73 +++++++++++++++++++ .../image/jib/deployment/JibProcessor.java | 6 ++ 4 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 core/deployment/src/main/java/io/quarkus/deployment/util/ContainerRuntimeUtil.java diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/NativeConfig.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/NativeConfig.java index 5f6f09d5a5509..6b1bf2dc69b9c 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/NativeConfig.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/NativeConfig.java @@ -6,6 +6,7 @@ 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; @@ -69,7 +70,7 @@ public class NativeConfig { * It also serves as the default Locale language for the native executable application runtime. * e.g. en or cs as defined by IETF BCP 47 language tags. *

- * + * * @deprecated Use the global quarkus.default-locale. */ @ConfigItem @@ -82,7 +83,7 @@ public class NativeConfig { * It also serves as the default Locale country for the native executable application runtime. * e.g. US or FR as defined by ISO 3166-1 alpha-2 codes. *

- * + * * @deprecated Use the global quarkus.default-locale. */ @ConfigItem @@ -429,7 +430,10 @@ public static class Compression { /** * Supported Container runtimes + * + * @deprecated Use {@link ContainerRuntimeUtil.ContainerRuntime} instead. */ + @Deprecated(since = "2.7", forRemoval = true) public static enum ContainerRuntime { DOCKER, PODMAN; diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildContainerRunner.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildContainerRunner.java index d3f1e486f49cf..a2bcefe8f9b6c 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildContainerRunner.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildContainerRunner.java @@ -15,6 +15,7 @@ import org.jboss.logging.Logger; import io.quarkus.deployment.pkg.NativeConfig; +import io.quarkus.deployment.util.ContainerRuntimeUtil; import io.quarkus.deployment.util.FileUtil; import io.quarkus.deployment.util.ProcessUtil; @@ -130,7 +131,9 @@ protected String[] buildCommand(String dockerCmd, List containerRuntimeA * 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 + * @deprecated Use {@link ContainerRuntimeUtil#detectContainerRuntime()} instead */ + @Deprecated(since = "2.7", forRemoval = true) public static NativeConfig.ContainerRuntime detectContainerRuntime() { // Docker version 19.03.14, build 5eb3275d40 String dockerVersionOutput = getVersionOutputFor(NativeConfig.ContainerRuntime.DOCKER); diff --git a/core/deployment/src/main/java/io/quarkus/deployment/util/ContainerRuntimeUtil.java b/core/deployment/src/main/java/io/quarkus/deployment/util/ContainerRuntimeUtil.java new file mode 100644 index 0000000000000..d1e7fe6988462 --- /dev/null +++ b/core/deployment/src/main/java/io/quarkus/deployment/util/ContainerRuntimeUtil.java @@ -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(); + } + } +} diff --git a/extensions/container-image/container-image-jib/deployment/src/main/java/io/quarkus/container/image/jib/deployment/JibProcessor.java b/extensions/container-image/container-image-jib/deployment/src/main/java/io/quarkus/container/image/jib/deployment/JibProcessor.java index c33c455410c35..3c6e0edbb04f4 100644 --- a/extensions/container-image/container-image-jib/deployment/src/main/java/io/quarkus/container/image/jib/deployment/JibProcessor.java +++ b/extensions/container-image/container-image-jib/deployment/src/main/java/io/quarkus/container/image/jib/deployment/JibProcessor.java @@ -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; @@ -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); }