Skip to content

Commit

Permalink
Merge pull request quarkusio#29401 from iocanel/cli-image-native
Browse files Browse the repository at this point in the history
Improvements in image cli and extensions
  • Loading branch information
geoand authored Nov 22, 2022
2 parents aa0efb0 + ea81275 commit e67b133
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 69 deletions.
14 changes: 13 additions & 1 deletion devtools/cli/src/main/java/io/quarkus/cli/image/Push.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public class Push extends BaseImageCommand {
private static final Map<BuildTool, String> ACTION_MAPPING = Map.of(BuildTool.MAVEN, "quarkus:image-push",
BuildTool.GRADLE, "imagePush");

private static final String QUARKUS_CONTAINER_IMAGE_BUILD = "quarkus.container-image.build";
private static final String QUARKUS_CONTAINER_IMAGE_USERNAME = "quarkus.container-image.username";
private static final String QUARKUS_CONTAINER_IMAGE_PASSWORD = "quarkus.container-image.password";

Expand All @@ -31,17 +32,28 @@ public class Push extends BaseImageCommand {
"--registry-password-stdin" }, description = "Read the image registry password from stdin.")
public boolean registryPasswordStdin;

@CommandLine.Option(order = 11, names = {
"--also-build" }, description = "(Re)build the image before pushing.")
public boolean alsoBuild;

@Override
public void populateImageConfiguration(Map<String, String> properties) {
super.populateImageConfiguration(properties);
registryUsername.ifPresent(u -> properties.put(QUARKUS_CONTAINER_IMAGE_USERNAME, u));

if (registryPasswordStdin) {
if (registryPasswordStdin && !runMode.isDryRun()) {
String password = new String(System.console().readPassword("Registry password:"));
properties.put(QUARKUS_CONTAINER_IMAGE_PASSWORD, password);
} else {
registryPassword.ifPresent(p -> properties.put(QUARKUS_CONTAINER_IMAGE_PASSWORD, p));
}

if (alsoBuild) {
properties.put(QUARKUS_CONTAINER_IMAGE_BUILD, "true");
} else {
properties.put(QUARKUS_CONTAINER_IMAGE_BUILD, "false");
}

properties.put(QUARKUS_FORCED_EXTENSIONS, QUARKUS_CONTAINER_IMAGE_EXTENSION);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,14 @@ public void testUsage() throws Exception {
assertTrue(result.getStdout().contains("-Dquarkus.container-image.tag=1.0"));

// 3 image push --dry-run
result = CliDriver.execute(project, "image", "push", "--help");
result = CliDriver.execute(project, "image", "push", "--dry-run");
assertEquals(CommandLine.ExitCode.OK, result.getExitCode(), "Expected OK return code." + result);
assertTrue(result.getStdout().contains("-Dquarkus.container-image.build=false"));

// 4 image push --also-build --dry-run
result = CliDriver.execute(project, "image", "push", "--also-build", "--dry-run");
assertEquals(CommandLine.ExitCode.OK, result.getExitCode(), "Expected OK return code." + result);
assertTrue(result.getStdout().contains("-Dquarkus.container-image.build=true"));

}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package io.quarkus.container.image.buildpack.deployment;

import static io.quarkus.container.image.deployment.util.EnablementUtil.buildContainerImageNeeded;
import static io.quarkus.container.image.deployment.util.EnablementUtil.pushContainerImageNeeded;
import static io.quarkus.container.util.PathsUtil.findMainSourcesRoot;

import java.nio.file.Path;
Expand All @@ -18,6 +20,7 @@

import dev.snowdrop.buildpack.Buildpack;
import dev.snowdrop.buildpack.BuildpackBuilder;
import dev.snowdrop.buildpack.docker.DockerClientUtils;
import io.quarkus.container.image.deployment.ContainerImageConfig;
import io.quarkus.container.image.deployment.util.NativeBinaryUtil;
import io.quarkus.container.spi.AvailableContainerImageExtensionBuildItem;
Expand Down Expand Up @@ -73,17 +76,20 @@ public void buildFromJar(ContainerImageConfig containerImageConfig, BuildpackCon
BuildProducer<ArtifactResultBuildItem> artifactResultProducer,
BuildProducer<ContainerImageBuilderBuildItem> containerImageBuilder) {

if (containerImageConfig.isBuildExplicitlyDisabled()) {
boolean buildContainerImage = buildContainerImageNeeded(containerImageConfig, buildRequest);
boolean pushContainerImage = pushContainerImageNeeded(containerImageConfig, pushRequest);

if (!buildContainerImage && !pushContainerImage) {
return;
}

if (!containerImageConfig.isBuildExplicitlyEnabled() && !containerImageConfig.isPushExplicitlyEnabled()
&& !buildRequest.isPresent() && !pushRequest.isPresent()) {
if (containerImageConfig.isBuildExplicitlyDisabled()) {
return;
}

log.info("Starting (local) container image build for jar using buildpack.");
String targetImageName = runBuildpackBuild(buildpackConfig, containerImage, containerImageConfig, pushRequest,
String targetImageName = runBuildpackBuild(buildpackConfig, containerImage, containerImageConfig, buildContainerImage,
pushContainerImage,
outputTarget, false /* isNative */);

artifactResultProducer.produce(new ArtifactResultBuildItem(null, "jar-container",
Expand All @@ -102,12 +108,14 @@ public void buildFromNative(ContainerImageConfig containerImageConfig, Buildpack
BuildProducer<ArtifactResultBuildItem> artifactResultProducer,
BuildProducer<ContainerImageBuilderBuildItem> containerImageBuilder) {

if (containerImageConfig.isBuildExplicitlyDisabled()) {
boolean buildContainerImage = buildContainerImageNeeded(containerImageConfig, buildRequest);
boolean pushContainerImage = pushContainerImageNeeded(containerImageConfig, pushRequest);

if (!buildContainerImage && !pushContainerImage) {
return;
}

if (!containerImageConfig.isBuildExplicitlyEnabled() && !containerImageConfig.isPushExplicitlyEnabled()
&& !buildRequest.isPresent() && !pushRequest.isPresent()) {
if (containerImageConfig.isBuildExplicitlyDisabled()) {
return;
}

Expand All @@ -117,7 +125,8 @@ public void buildFromNative(ContainerImageConfig containerImageConfig, Buildpack
}

log.info("Starting (local) container image build for native binary using buildpack.");
String targetImageName = runBuildpackBuild(buildpackConfig, containerImage, containerImageConfig, pushRequest,
String targetImageName = runBuildpackBuild(buildpackConfig, containerImage, containerImageConfig, buildContainerImage,
pushContainerImage,
outputTarget, true /* isNative */);

artifactResultProducer.produce(new ArtifactResultBuildItem(null, "native-container",
Expand Down Expand Up @@ -149,7 +158,8 @@ private Map<ProjectDirs, Path> getPaths(OutputTargetBuildItem outputTarget) {
private String runBuildpackBuild(BuildpackConfig buildpackConfig,
ContainerImageInfoBuildItem containerImage,
ContainerImageConfig containerImageConfig,
Optional<ContainerImagePushRequestBuildItem> pushRequest,
boolean buildContainerImage,
boolean pushContainerImage,
OutputTargetBuildItem outputTarget,
boolean isNativeBuild) {

Expand All @@ -171,39 +181,42 @@ private String runBuildpackBuild(BuildpackConfig buildpackConfig,
envMap.put(QUARKUS_CONTAINER_IMAGE_BUILD, "false");
envMap.put(QUARKUS_CONTAINER_IMAGE_PUSH, "false");

log.info("Initiating Buildpack build");
Buildpack buildpack = Buildpack.builder()
.addNewFileContent(dirs.get(ProjectDirs.ROOT).toFile())
.withFinalImage(targetImageName)
.withEnvironment(envMap)
.withLogLevel(buildpackConfig.logLevel)
.withPullTimeoutSeconds(buildpackConfig.pullTimeoutSeconds)
.withLogger(new BuildpackLogger())
.accept(BuildpackBuilder.class, b -> {

if (isNativeBuild) {
buildpackConfig.nativeBuilderImage.ifPresent(i -> b.withBuilderImage(i));
} else {
buildpackConfig.jvmBuilderImage.ifPresent(i -> b.withBuilderImage(i));
}

if (buildpackConfig.runImage.isPresent()) {
log.info("Using Run image of " + buildpackConfig.runImage.get());
b.withRunImage(buildpackConfig.runImage.get());
}
if (buildpackConfig.dockerHost.isPresent()) {
log.info("Using DockerHost of " + buildpackConfig.dockerHost.get());
b.withDockerHost(buildpackConfig.dockerHost.get());
}

}).build();

if (buildpack.getExitCode() != 0) {
throw new IllegalStateException("Buildpack build failed");
if (buildContainerImage) {
log.info("Initiating Buildpack build");
Buildpack buildpack = Buildpack.builder()
.addNewFileContent(dirs.get(ProjectDirs.ROOT).toFile())
.withFinalImage(targetImageName)
.withEnvironment(envMap)
.withLogLevel(buildpackConfig.logLevel)
.withPullTimeoutSeconds(buildpackConfig.pullTimeoutSeconds)
.withLogger(new BuildpackLogger())
.accept(BuildpackBuilder.class, b -> {

if (isNativeBuild) {
buildpackConfig.nativeBuilderImage.ifPresent(i -> b.withBuilderImage(i));
} else {
buildpackConfig.jvmBuilderImage.ifPresent(i -> b.withBuilderImage(i));
}

if (buildpackConfig.runImage.isPresent()) {
log.info("Using Run image of " + buildpackConfig.runImage.get());
b.withRunImage(buildpackConfig.runImage.get());
}
if (buildpackConfig.dockerHost.isPresent()) {
log.info("Using DockerHost of " + buildpackConfig.dockerHost.get());
b.withDockerHost(buildpackConfig.dockerHost.get());
}

}).build();

if (buildpack.getExitCode() != 0) {
throw new IllegalStateException("Buildpack build failed");
}

log.info("Buildpack build complete");
}

log.info("Buildpack build complete");
if (containerImageConfig.isPushExplicitlyEnabled() || pushRequest.isPresent()) {
if (pushContainerImage) {
var registry = containerImage.getRegistry()
.orElseGet(() -> {
log.info("No container image registry was set, so 'docker.io' will be used");
Expand All @@ -216,7 +229,10 @@ private String runBuildpackBuild(BuildpackConfig buildpackConfig,

log.info("Pushing image to " + authConfig.getRegistryAddress());
Stream.concat(Stream.of(containerImage.getImage()), containerImage.getAdditionalImageTags().stream()).forEach(i -> {
ResultCallback.Adapter<PushResponseItem> callback = buildpack.getDockerClient().pushImageCmd(i).start();
//If no dockerHost is specified use empty String. The util will take care of the rest.
String dockerHost = buildpackConfig.dockerHost.orElse("");
ResultCallback.Adapter<PushResponseItem> callback = DockerClientUtils.getDockerClient(dockerHost)
.pushImageCmd(i).start();
try {
callback.awaitCompletion();
log.info("Push complete");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,14 @@ public void dockerBuildFromJar(DockerConfig dockerConfig,
dockerFileBaseInformation.get().getBaseImage()));
}

log.info("Starting (local) container image build for jar using docker.");
if (buildContainerImage) {
log.info("Starting (local) container image build for jar using docker.");
}

ImageIdReader reader = new ImageIdReader();
String builtContainerImage = createContainerImage(containerImageConfig, dockerConfig, containerImageInfo, out, reader,
false,
buildContainerImage,
pushContainerImage, packageConfig);

// a pull is not required when using this image locally because the docker strategy always builds the container image
Expand Down Expand Up @@ -148,6 +151,7 @@ public void dockerBuildFromNativeImage(DockerConfig dockerConfig,

ImageIdReader reader = new ImageIdReader();
String builtContainerImage = createContainerImage(containerImageConfig, dockerConfig, containerImage, out, reader, true,
buildContainerImage,
pushContainerImage, packageConfig);

// a pull is not required when using this image locally because the docker strategy always builds the container image
Expand All @@ -160,11 +164,11 @@ public void dockerBuildFromNativeImage(DockerConfig dockerConfig,

private String createContainerImage(ContainerImageConfig containerImageConfig, DockerConfig dockerConfig,
ContainerImageInfoBuildItem containerImageInfo,
OutputTargetBuildItem out, ImageIdReader reader, boolean forNative, boolean pushRequested,
OutputTargetBuildItem out, ImageIdReader reader, boolean forNative, boolean buildContainerImage,
boolean pushContainerImage,
PackageConfig packageConfig) {

var useBuildx = dockerConfig.buildx.useBuildx();
var pushImages = pushRequested || containerImageConfig.isPushExplicitlyEnabled();

// useBuildx: Whether or not any of the buildx parameters are set
//
Expand All @@ -184,41 +188,45 @@ private String createContainerImage(ContainerImageConfig containerImageConfig, D

DockerfilePaths dockerfilePaths = getDockerfilePaths(dockerConfig, forNative, packageConfig, out);
String[] dockerArgs = getDockerArgs(containerImageInfo.getImage(), dockerfilePaths, containerImageConfig, dockerConfig,
containerImageInfo, pushImages);
containerImageInfo, pushContainerImage);

if (useBuildx && pushImages) {
if (useBuildx && pushContainerImage) {
// Needed because buildx will push all the images in a single step
loginToRegistryIfNeeded(containerImageConfig, containerImageInfo, dockerConfig);
}

log.infof("Executing the following command to build docker image: '%s %s'", dockerConfig.executableName,
String.join(" ", dockerArgs));
boolean buildSuccessful = ExecUtil.exec(out.getOutputDirectory().toFile(), reader, dockerConfig.executableName,
dockerArgs);
if (!buildSuccessful) {
throw dockerException(dockerArgs);
}
if (buildContainerImage) {
log.infof("Executing the following command to build docker image: '%s %s'", dockerConfig.executableName,
String.join(" ", dockerArgs));
boolean buildSuccessful = ExecUtil.exec(out.getOutputDirectory().toFile(), reader, dockerConfig.executableName,
dockerArgs);
if (!buildSuccessful) {
throw dockerException(dockerArgs);
}

dockerConfig.buildx.platform
.filter(platform -> platform.size() > 1)
.ifPresentOrElse(
platform -> log.infof("Built container image %s (%s platform(s))\n", containerImageInfo.getImage(),
String.join(",", platform)),
() -> log.infof("Built container image %s (%s)\n", containerImageInfo.getImage(), reader.getImageId()));
dockerConfig.buildx.platform
.filter(platform -> platform.size() > 1)
.ifPresentOrElse(
platform -> log.infof("Built container image %s (%s platform(s))\n", containerImageInfo.getImage(),
String.join(",", platform)),
() -> log.infof("Built container image %s (%s)\n", containerImageInfo.getImage(),
reader.getImageId()));

}

if (!useBuildx) {
if (!useBuildx && buildContainerImage) {
// If we didn't use buildx, now we need to process any tags
if (!containerImageInfo.getAdditionalImageTags().isEmpty()) {
createAdditionalTags(containerImageInfo.getImage(), containerImageInfo.getAdditionalImageTags(), dockerConfig);
}
}

if (pushImages) {
// If not using buildx, push the images
loginToRegistryIfNeeded(containerImageConfig, containerImageInfo, dockerConfig);
if (pushContainerImage) {
// If not using buildx, push the images
loginToRegistryIfNeeded(containerImageConfig, containerImageInfo, dockerConfig);

Stream.concat(containerImageInfo.getAdditionalImageTags().stream(), Stream.of(containerImageInfo.getImage()))
.forEach(imageToPush -> pushImage(imageToPush, dockerConfig));
}
Stream.concat(containerImageInfo.getAdditionalImageTags().stream(), Stream.of(containerImageInfo.getImage()))
.forEach(imageToPush -> pushImage(imageToPush, dockerConfig));
}

return containerImageInfo.getImage();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ public void buildFromNative(ContainerImageConfig containerImageConfig, JibConfig

log.info("Starting (local) container image build for native binary using jib.");
JibContainer container = containerize(containerImageConfig, jibConfig, containerImage, jibContainerBuilder,
pushRequest.isPresent());
pushContainerImage);
writeOutputFiles(container, jibConfig, outputTarget);

artifactResultProducer.produce(new ArtifactResultBuildItem(null, "native-container",
Expand Down

0 comments on commit e67b133

Please sign in to comment.