Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable Podman and Docker Windows quarkus-container-image-docker testing #31490

Merged
merged 6 commits into from
Mar 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@

package io.quarkus.deployment;

import java.io.File;
import static io.quarkus.runtime.util.ContainerRuntimeUtil.ContainerRuntime.UNAVAILABLE;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
Expand All @@ -11,23 +12,20 @@
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.util.List;
import java.util.Optional;
import java.util.function.BooleanSupplier;
import java.util.function.Supplier;

import org.eclipse.microprofile.config.ConfigProvider;
import org.jboss.logging.Logger;

import io.quarkus.deployment.console.StartupLogCompressor;
import io.quarkus.deployment.util.ExecUtil;
import io.quarkus.runtime.util.ContainerRuntimeUtil;

public class IsDockerWorking implements BooleanSupplier {

private static final Logger LOGGER = Logger.getLogger(IsDockerWorking.class.getName());
public static final int DOCKER_HOST_CHECK_TIMEOUT = 1000;
public static final int DOCKER_CMD_CHECK_TIMEOUT = 3000;

private final List<Strategy> strategies;

Expand All @@ -36,8 +34,7 @@ public IsDockerWorking() {
}

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

@Override
Expand Down Expand Up @@ -170,41 +167,11 @@ public Result get() {

private static class DockerBinaryStrategy implements Strategy {

private final boolean silent;
private final String binary;

private DockerBinaryStrategy(boolean silent) {
this.silent = silent;
this.binary = ConfigProvider.getConfig().getOptionalValue("quarkus.docker.executable-name", String.class)
.orElse("docker");
}

@Override
public Result get() {
try {
if (!ExecUtil.execSilentWithTimeout(Duration.ofMillis(DOCKER_CMD_CHECK_TIMEOUT), binary, "-v")) {
LOGGER.warnf("'%s -v' returned an error code. Make sure your Docker binary is correct", binary);
return Result.UNKNOWN;
}
} catch (Exception e) {
LOGGER.warnf("No %s binary found or general error: %s", binary, e);
return Result.UNKNOWN;
}

try {
OutputFilter filter = new OutputFilter();
if (ExecUtil.execWithTimeout(new File("."), filter, Duration.ofMillis(DOCKER_CMD_CHECK_TIMEOUT),
"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");
}
return Result.UNAVAILABLE;
}
} catch (Exception e) {
LOGGER.warn("Unexpected error occurred while determining Docker daemon version", e);
if (ContainerRuntimeUtil.detectContainerRuntime(false) != UNAVAILABLE) {
return Result.AVAILABLE;
} else {
return Result.UNKNOWN;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.quarkus.deployment.pkg.steps;

import static io.quarkus.deployment.pkg.steps.LinuxIDUtil.getLinuxID;
import static io.quarkus.runtime.util.ContainerRuntimeUtil.detectContainerRuntime;

import java.io.File;
import java.io.IOException;
Expand Down Expand Up @@ -28,6 +29,7 @@
import io.quarkus.deployment.pkg.builditem.OutputTargetBuildItem;
import io.quarkus.deployment.steps.MainClassBuildStep;
import io.quarkus.runtime.LaunchMode;
import io.quarkus.runtime.util.ContainerRuntimeUtil;
import io.quarkus.utilities.JavaBinFinder;

public class AppCDSBuildStep {
Expand All @@ -37,6 +39,7 @@ public class AppCDSBuildStep {
public static final String CLASSES_LIST_FILE_NAME = "classes.lst";
private static final String CONTAINER_IMAGE_BASE_BUILD_DIR = "/tmp/quarkus";
private static final String CONTAINER_IMAGE_APPCDS_DIR = CONTAINER_IMAGE_BASE_BUILD_DIR + "/appcds";
public static final ContainerRuntimeUtil.ContainerRuntime CONTAINER_RUNTIME = detectContainerRuntime(false);

@BuildStep(onlyIf = AppCDSRequired.class)
public void requested(OutputTargetBuildItem outputTarget, BuildProducer<AppCDSRequestedBuildItem> producer)
Expand Down Expand Up @@ -201,8 +204,12 @@ private Path createClassesList(JarBuildItem jarResult,
// generate the classes file on the host
private List<String> dockerRunCommands(OutputTargetBuildItem outputTarget, String containerImage,
String containerWorkingDir) {
if (CONTAINER_RUNTIME == ContainerRuntimeUtil.ContainerRuntime.UNAVAILABLE) {
throw new IllegalStateException("No container runtime was found. "
+ "Make sure you have either Docker or Podman installed in your environment.");
}
List<String> command = new ArrayList<>(10);
command.add("docker");
command.add(CONTAINER_RUNTIME.getExecutableName());
command.add("run");
command.add("-v");
command.add(outputTarget.getOutputDirectory().toAbsolutePath().toString() + ":" + CONTAINER_IMAGE_BASE_BUILD_DIR
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
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 @@ -9,30 +11,24 @@
import java.util.List;

import org.apache.commons.lang3.SystemUtils;
import org.jboss.logging.Logger;

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

public class NativeImageBuildLocalContainerRunner extends NativeImageBuildContainerRunner {

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

public NativeImageBuildLocalContainerRunner(NativeConfig nativeConfig, Path outputDir) {
super(nativeConfig, outputDir);
if (SystemUtils.IS_OS_LINUX) {
ArrayList<String> containerRuntimeArgs = new ArrayList<>(Arrays.asList(baseContainerRuntimeArgs));
if (containerRuntime == ContainerRuntimeUtil.ContainerRuntime.DOCKER
&& containerRuntime.isRootless()) {
final ArrayList<String> containerRuntimeArgs = new ArrayList<>(Arrays.asList(baseContainerRuntimeArgs));
if (containerRuntime == DOCKER && 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 == ContainerRuntimeUtil.ContainerRuntime.PODMAN
&& containerRuntime.isRootless()) {
if (containerRuntime == PODMAN && containerRuntime.isRootless()) {
// Needed to avoid AccessDeniedExceptions
containerRuntimeArgs.add("--userns=keep-id");
}
Expand All @@ -44,16 +40,19 @@ public NativeImageBuildLocalContainerRunner(NativeConfig nativeConfig, Path outp

@Override
protected List<String> getContainerRuntimeBuildArgs() {
List<String> containerRuntimeArgs = super.getContainerRuntimeBuildArgs();
String volumeOutputPath = outputPath;
final List<String> containerRuntimeArgs = super.getContainerRuntimeBuildArgs();
final String volumeOutputPath;
if (SystemUtils.IS_OS_WINDOWS) {
volumeOutputPath = FileUtil.translateToVolumePath(volumeOutputPath);
volumeOutputPath = FileUtil.translateToVolumePath(outputPath);
} else {
volumeOutputPath = outputPath;
}

String selinuxBindOption = ":z";
if (SystemUtils.IS_OS_MAC
&& ContainerRuntimeUtil.detectContainerRuntime() == ContainerRuntimeUtil.ContainerRuntime.PODMAN) {
final String selinuxBindOption;
if (SystemUtils.IS_OS_MAC && containerRuntime == PODMAN) {
selinuxBindOption = "";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we know the reason why podman on mac needs different arguments here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No idea. I don't have access to any mac hw, so I tested this PR only on Windows and Linux. This bit hasn't been changed though.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mac qemu doesnt support selinux labels over 9pfs. Although not sure if this leads to an actual error, if it does it could be changed to ignore

} else {
selinuxBindOption = ":z";
}

Collections.addAll(containerRuntimeArgs, "-v",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.quarkus.deployment.pkg.steps;

import static io.quarkus.deployment.pkg.steps.AppCDSBuildStep.CONTAINER_RUNTIME;
import static org.assertj.core.api.Assertions.assertThat;

import java.nio.file.Path;
Expand All @@ -10,13 +11,18 @@
import org.junit.jupiter.api.condition.DisabledIfSystemProperty;

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

class NativeImageBuildContainerRunnerTest {

// This will default to false in the maven build and true in the IDE, so this will still run if invoked explicitly
@DisabledIfSystemProperty(named = "avoid-containers", matches = "true")
@Test
void testBuilderImageBeingPickedUp() {
if (CONTAINER_RUNTIME == ContainerRuntimeUtil.ContainerRuntime.UNAVAILABLE) {
throw new IllegalStateException("No container runtime was found. "
+ "Make sure you have either Docker or Podman installed in your environment.");
}
NativeConfig nativeConfig = new NativeConfig();
nativeConfig.containerRuntime = Optional.empty();
boolean found;
Expand All @@ -25,33 +31,39 @@ void testBuilderImageBeingPickedUp() {

nativeConfig.builderImage = "graalvm";
localRunner = new NativeImageBuildLocalContainerRunner(nativeConfig, Path.of("/tmp"));
command = localRunner.buildCommand("docker", Collections.emptyList(), Collections.emptyList());
command = localRunner.buildCommand(CONTAINER_RUNTIME.getExecutableName(), Collections.emptyList(),
Collections.emptyList());
found = false;
for (String part : command) {
if (part.contains("ubi-quarkus-graalvmce-builder-image")) {
found = true;
break;
}
}
assertThat(found).isTrue();

nativeConfig.builderImage = "mandrel";
localRunner = new NativeImageBuildLocalContainerRunner(nativeConfig, Path.of("/tmp"));
command = localRunner.buildCommand("docker", Collections.emptyList(), Collections.emptyList());
command = localRunner.buildCommand(CONTAINER_RUNTIME.getExecutableName(), Collections.emptyList(),
Collections.emptyList());
found = false;
for (String part : command) {
if (part.contains("ubi-quarkus-mandrel-builder-image")) {
found = true;
break;
}
}
assertThat(found).isTrue();

nativeConfig.builderImage = "RandomString";
localRunner = new NativeImageBuildLocalContainerRunner(nativeConfig, Path.of("/tmp"));
command = localRunner.buildCommand("docker", Collections.emptyList(), Collections.emptyList());
command = localRunner.buildCommand(CONTAINER_RUNTIME.getExecutableName(), Collections.emptyList(),
Collections.emptyList());
found = false;
for (String part : command) {
if (part.equals("RandomString")) {
found = true;
break;
}
}
assertThat(found).isTrue();
Expand Down
Loading