Skip to content

Commit

Permalink
Take into account "fallback to container" when determining where nati…
Browse files Browse the repository at this point in the history
…ve-image gets executed

Sometimes we need to do things differently based on whether the native
build happens within a container or not.

Before this patch, we used to determine that based exclusively on explicit
configuration.

After this patch, we correctly take into account that we sometimes need
to "fall back" to containers even though the configuration didn't mention
anything about containers, simply because native-image isn't installed.

The previous behavior used to lead to at least one bug: when debugging
GraalVM's JVM, we determined the address to bind to based on whether
we're running in a container or not, and
[here we used to make the wrong choice](https://github.com/quarkusio/quarkus/blob/635a848bb8022e4ca6e945af7e62edc32a288588/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java#L780-L784),
resulting in the JVM debug agent being inaccessible from the Docker host.

See also https://quarkusio.zulipchat.com/#narrow/stream/187038-dev/topic/quarkus.2Enative.2Edebug-build-process/near/345399379
  • Loading branch information
yrodiere committed Mar 31, 2023
1 parent 6048280 commit f81b7fe
Show file tree
Hide file tree
Showing 13 changed files with 192 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import static io.quarkus.dev.console.QuarkusConsole.IS_WINDOWS;

import io.quarkus.builder.item.MultiBuildItem;
import io.quarkus.deployment.pkg.NativeConfig;

/**
* Native-image might not be supported for a particular
Expand All @@ -32,14 +31,14 @@ public UnsupportedOSBuildItem(Os os, String error) {
this.error = error;
}

public boolean triggerError(NativeConfig nativeConfig) {
public boolean triggerError(boolean isContainerBuild) {
return
// When the host OS is unsupported, it could have helped to
// run in a Linux builder image (e.g. an extension unsupported on Windows).
(os.active && !nativeConfig.isContainerBuild()) ||
(os.active && !isContainerBuild) ||
// If Linux is the OS the extension does not support,
// it fails in a container build regardless the host OS,
// because we have only Linux based builder images.
(nativeConfig.isContainerBuild() && os == Os.LINUX);
(isContainerBuild && os == Os.LINUX);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ public class NativeConfig {
@ConfigItem
public boolean remoteContainerBuild;

public boolean isContainerBuild() {
public boolean isExplicitContainerBuild() {
return containerBuild.orElse(containerRuntime.isPresent() || remoteContainerBuild);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package io.quarkus.deployment.pkg.builditem;

import io.quarkus.builder.item.SimpleBuildItem;
import io.quarkus.deployment.pkg.steps.NativeImageBuildRunner;

/**
* The resolved factory for the native image runner.
*/
public final class NativeImageRunnerFactoryBuildItem extends SimpleBuildItem {
private final NativeImageBuildRunner.Factory buildRunnerFactory;

public NativeImageRunnerFactoryBuildItem(NativeImageBuildRunner.Factory buildRunnerFactory) {
this.buildRunnerFactory = buildRunnerFactory;
}

public NativeImageBuildRunner.Factory getBuildRunnerFactory() {
return buildRunnerFactory;
}

public boolean isContainerBuild() {
return buildRunnerFactory.isContainerBuild();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,30 @@ public abstract class NativeImageBuildContainerRunner extends NativeImageBuildRu

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

public static abstract class Factory implements NativeImageBuildRunner.Factory {
protected final NativeConfig nativeConfig;

public Factory(NativeConfig nativeConfig) {
this.nativeConfig = nativeConfig;
}

@Override
public abstract NativeImageBuildContainerRunner create(Path outputDir, String nativeImageName);

@Override
public boolean isContainerBuild() {
return true;
}
}

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

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,19 @@

public class NativeImageBuildLocalContainerRunner extends NativeImageBuildContainerRunner {

public NativeImageBuildLocalContainerRunner(NativeConfig nativeConfig, Path outputDir) {
super(nativeConfig, outputDir);
public static class Factory extends NativeImageBuildContainerRunner.Factory {
public Factory(NativeConfig nativeConfig) {
super(nativeConfig);
}

@Override
public NativeImageBuildLocalContainerRunner create(Path outputDir, String nativeImageName) {
return new NativeImageBuildLocalContainerRunner(this, outputDir);
}
}

private NativeImageBuildLocalContainerRunner(Factory factory, Path outputDir) {
super(factory, outputDir);
if (SystemUtils.IS_OS_LINUX) {
final ArrayList<String> containerRuntimeArgs = new ArrayList<>(Arrays.asList(baseContainerRuntimeArgs));
if (containerRuntime == DOCKER && containerRuntime.isRootless()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,37 @@
package io.quarkus.deployment.pkg.steps;

import java.io.File;
import java.nio.file.Path;
import java.util.List;
import java.util.stream.Stream;

import org.apache.commons.lang3.SystemUtils;

public class NativeImageBuildLocalRunner extends NativeImageBuildRunner {

public static class Factory implements NativeImageBuildRunner.Factory {
private final String nativeImageExecutable;

public Factory(String nativeImageExecutable) {
this.nativeImageExecutable = nativeImageExecutable;
}

@Override
public NativeImageBuildRunner create(Path outputDir, String nativeImageName) {
return new NativeImageBuildLocalRunner(this, outputDir.toFile());
}

@Override
public boolean isContainerBuild() {
return false;
}
}

private final String nativeImageExecutable;
private final File workingDirectory;

public NativeImageBuildLocalRunner(String nativeImageExecutable, File workingDirectory) {
this.nativeImageExecutable = nativeImageExecutable;
private NativeImageBuildLocalRunner(Factory factory, File workingDirectory) {
this.nativeImageExecutable = factory.nativeImageExecutable;
this.workingDirectory = workingDirectory;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,25 @@ public class NativeImageBuildRemoteContainerRunner extends NativeImageBuildConta
// issue https://github.com/containers/podman/issues/9608
private static final String CONTAINER_BUILD_VOLUME_NAME = "quarkus-native-builder-image-project-volume";

public static class Factory extends NativeImageBuildContainerRunner.Factory {
public Factory(NativeConfig nativeConfig) {
super(nativeConfig);
}

@Override
public NativeImageBuildRemoteContainerRunner create(Path outputDir, String nativeImageName) {
return new NativeImageBuildRemoteContainerRunner(this, outputDir, nativeImageName,
getResultingExecutableName(nativeImageName));
}
}

private final String nativeImageName;
private final String resultingExecutableName;
private String containerId;

public NativeImageBuildRemoteContainerRunner(NativeConfig nativeConfig, Path outputDir,
String nativeImageName, String resultingExecutableName) {
super(nativeConfig, outputDir);
private NativeImageBuildRemoteContainerRunner(Factory factory, Path outputDir, String nativeImageName,
String resultingExecutableName) {
super(factory, outputDir);
this.nativeImageName = nativeImageName;
this.resultingExecutableName = resultingExecutableName;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,28 @@ public abstract class NativeImageBuildRunner {

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

/**
* The point of this factory is to represent the resolved runner type and parameters,
* so that it can be resolved early and the runner type taken into account in build steps
* that are needed for the eventual build.
* In particular, we sometimes need to determine whether the build is in-container or not
* to produce build items that will be consumed during the build.
*/
public interface Factory {
NativeImageBuildRunner create(Path outputDir, String nativeImageName);

boolean isContainerBuild();

default String getResultingExecutableName(String nativeImageName) {
String resultingExecutableName = nativeImageName;
if (SystemUtils.IS_OS_WINDOWS && !isContainerBuild()) {
//once image is generated it gets added .exe on Windows
resultingExecutableName = resultingExecutableName + ".exe";
}
return resultingExecutableName;
}
}

public GraalVM.Version getGraalVMVersion() {
final GraalVM.Version graalVMVersion;
try {
Expand Down

0 comments on commit f81b7fe

Please sign in to comment.