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

Introduce the quarkus.container-image.image property #12070

Merged
merged 3 commits into from
Sep 14, 2020
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
1 change: 1 addition & 0 deletions docs/src/main/asciidoc/config.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,7 @@ quarkus.datasource.jdbc.url=jdbc:mysql://${application.server}:3306/mydatabase?u
It does result in one more line in this example but the value of `application.server` can be reused in other properties,
diminishing the possibility of typos and providing more flexibility in property definitions.

[#combine-property-env-var]
=== Combining Property Expressions and Environment Variables

Quarkus also supports the combination of both property expressions and environment variables.
Expand Down
16 changes: 16 additions & 0 deletions docs/src/main/asciidoc/container-image.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,22 @@ The following properties can be used to customize the container image build proc

include::{generated-dir}/config/quarkus-container-image.adoc[opts=optional, leveloffset=+1]

==== Using CI Environments

Various CI environments provide a ready to use container-image registry which can be combined with the container-image Quarkus extensions in order to
effortlessly create and push a Quarkus application to said registry.

For example, https://gitlab.com/[GitLab] provides such a registry and in the provided CI environment,
makes available the `CI_REGISTRY_IMAGE` environment variable
(see GitLab's https://docs.gitlab.com/ee/ci/variables/[documentation]) for more information), which can be used in Quarkus like so:

[source]
----
quarkus.container-image.image=${CI_REGISTRY_IMAGE}
----

NOTE: See link:config.adoc#combine-property-env-var[this] for more information on how to combine properties with environment variables.

=== Jib Options

In addition to the generic container image options, the `container-image-jib` also provides the following options:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ public CapabilityBuildItem capability() {

@BuildStep(onlyIf = { IsNormal.class, DockerBuild.class }, onlyIfNot = NativeBuild.class)
public void dockerBuildFromJar(DockerConfig dockerConfig,
ContainerImageConfig containerImageConfig, // TODO: use to check whether we need to also push to registry
ContainerImageConfig containerImageConfig,
OutputTargetBuildItem out,
ContainerImageInfoBuildItem containerImage,
ContainerImageInfoBuildItem containerImageInfo,
Optional<ContainerImageBuildRequestBuildItem> buildRequest,
Optional<ContainerImagePushRequestBuildItem> pushRequest,
BuildProducer<ArtifactResultBuildItem> artifactResultProducer,
Expand All @@ -79,11 +79,8 @@ public void dockerBuildFromJar(DockerConfig dockerConfig,

log.info("Building docker image for jar.");

String image = containerImage.getImage();
List<String> additionalImageTags = containerImage.getAdditionalImageTags();

ImageIdReader reader = new ImageIdReader();
createContainerImage(containerImageConfig, dockerConfig, image, additionalImageTags, out, reader, false,
createContainerImage(containerImageConfig, dockerConfig, containerImageInfo, out, reader, false,
pushRequest.isPresent(), packageConfig);

artifactResultProducer.produce(new ArtifactResultBuildItem(null, "jar-container", Collections.emptyMap()));
Expand Down Expand Up @@ -117,41 +114,38 @@ public void dockerBuildFromNativeImage(DockerConfig dockerConfig,

log.info("Starting docker image build");

String image = containerImage.getImage();
List<String> additionalImageTags = containerImage.getAdditionalImageTags();

ImageIdReader reader = new ImageIdReader();
createContainerImage(containerImageConfig, dockerConfig, image, additionalImageTags, out, reader, true,
createContainerImage(containerImageConfig, dockerConfig, containerImage, out, reader, true,
pushRequest.isPresent(), packageConfig);
artifactResultProducer.produce(new ArtifactResultBuildItem(null, "native-container", Collections.emptyMap()));
}

private void createContainerImage(ContainerImageConfig containerImageConfig, DockerConfig dockerConfig, String image,
List<String> additionalImageTags,
private void createContainerImage(ContainerImageConfig containerImageConfig, DockerConfig dockerConfig,
ContainerImageInfoBuildItem containerImageInfo,
OutputTargetBuildItem out, ImageIdReader reader, boolean forNative, boolean pushRequested,
PackageConfig packageConfig) {

DockerfilePaths dockerfilePaths = getDockerfilePaths(dockerConfig, forNative, packageConfig, out);
String[] dockerArgs = getDockerArgs(image, dockerfilePaths, dockerConfig);
String[] dockerArgs = getDockerArgs(containerImageInfo.getImage(), dockerfilePaths, dockerConfig);
log.infof("Executing the following command to build docker image: '%s %s'", DOCKER_BINARY_NAME,
String.join(" ", dockerArgs));
boolean buildSuccessful = ExecUtil.exec(out.getOutputDirectory().toFile(), reader, DOCKER_BINARY_NAME, dockerArgs);
if (!buildSuccessful) {
throw dockerException(dockerArgs);
}

log.infof("Built container image %s (%s)\n", image, reader.getImageId());
log.infof("Built container image %s (%s)\n", containerImageInfo.getImage(), reader.getImageId());

if (!additionalImageTags.isEmpty()) {
createAdditionalTags(image, additionalImageTags);
if (!containerImageInfo.getAdditionalImageTags().isEmpty()) {
createAdditionalTags(containerImageInfo.getImage(), containerImageInfo.getAdditionalImageTags());
}

if (pushRequested || containerImageConfig.push) {
String registry = "docker.io";
if (!containerImageConfig.registry.isPresent()) {
if (!containerImageInfo.getRegistry().isPresent()) {
log.info("No container image registry was set, so 'docker.io' will be used");
} else {
registry = containerImageConfig.registry.get();
registry = containerImageInfo.getRegistry().get();
}
// Check if we need to login first
if (containerImageConfig.username.isPresent() && containerImageConfig.password.isPresent()) {
Expand All @@ -162,8 +156,8 @@ private void createContainerImage(ContainerImageConfig containerImageConfig, Doc
}
}

List<String> imagesToPush = new ArrayList<>(additionalImageTags);
imagesToPush.add(image);
List<String> imagesToPush = new ArrayList<>(containerImageInfo.getAdditionalImageTags());
imagesToPush.add(containerImageInfo.getImage());
for (String imageToPush : imagesToPush) {
pushImage(imageToPush);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import com.google.cloud.tools.jib.frontend.CredentialRetrieverFactory;

import io.quarkus.bootstrap.util.ZipUtils;
import io.quarkus.builder.Version;
import io.quarkus.container.image.deployment.ContainerImageConfig;
import io.quarkus.container.image.deployment.util.NativeBinaryUtil;
import io.quarkus.container.spi.ContainerImageBuildRequestBuildItem;
Expand All @@ -46,7 +47,6 @@
import io.quarkus.deployment.IsNormal;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.ApplicationInfoBuildItem;
import io.quarkus.deployment.builditem.CapabilityBuildItem;
import io.quarkus.deployment.builditem.MainClassBuildItem;
import io.quarkus.deployment.pkg.PackageConfig;
Expand Down Expand Up @@ -76,7 +76,7 @@ public void buildFromJar(ContainerImageConfig containerImageConfig, JibConfig ji
ContainerImageInfoBuildItem containerImage,
JarBuildItem sourceJar,
MainClassBuildItem mainClass,
OutputTargetBuildItem outputTarget, ApplicationInfoBuildItem applicationInfo,
OutputTargetBuildItem outputTarget,
Optional<ContainerImageBuildRequestBuildItem> buildRequest,
Optional<ContainerImagePushRequestBuildItem> pushRequest,
List<ContainerImageLabelBuildItem> containerImageLabels,
Expand All @@ -99,7 +99,7 @@ public void buildFromJar(ContainerImageConfig containerImageConfig, JibConfig ji
"Package type '" + packageType + "' is not supported by the container-image-jib extension");
}
handleExtraFiles(outputTarget, jibContainerBuilder);
containerize(applicationInfo, containerImageConfig, containerImage, jibContainerBuilder,
containerize(containerImageConfig, containerImage, jibContainerBuilder,
pushRequest.isPresent());

artifactResultProducer.produce(new ArtifactResultBuildItem(null, "jar-container", Collections.emptyMap()));
Expand All @@ -109,7 +109,6 @@ public void buildFromJar(ContainerImageConfig containerImageConfig, JibConfig ji
public void buildFromNative(ContainerImageConfig containerImageConfig, JibConfig jibConfig,
ContainerImageInfoBuildItem containerImage,
NativeImageBuildItem nativeImage,
ApplicationInfoBuildItem applicationInfo,
OutputTargetBuildItem outputTarget,
Optional<ContainerImageBuildRequestBuildItem> buildRequest,
Optional<ContainerImagePushRequestBuildItem> pushRequest,
Expand All @@ -129,15 +128,15 @@ public void buildFromNative(ContainerImageConfig containerImageConfig, JibConfig
JibContainerBuilder jibContainerBuilder = createContainerBuilderFromNative(containerImageConfig, jibConfig,
nativeImage, containerImageLabels);
handleExtraFiles(outputTarget, jibContainerBuilder);
containerize(applicationInfo, containerImageConfig, containerImage, jibContainerBuilder,
containerize(containerImageConfig, containerImage, jibContainerBuilder,
pushRequest.isPresent());

artifactResultProducer.produce(new ArtifactResultBuildItem(null, "native-container", Collections.emptyMap()));
}

private JibContainer containerize(ApplicationInfoBuildItem applicationInfo, ContainerImageConfig containerImageConfig,
private JibContainer containerize(ContainerImageConfig containerImageConfig,
ContainerImageInfoBuildItem containerImage, JibContainerBuilder jibContainerBuilder, boolean pushRequested) {
Containerizer containerizer = createContainerizer(containerImageConfig, containerImage, applicationInfo, pushRequested);
Containerizer containerizer = createContainerizer(containerImageConfig, containerImage, pushRequested);
for (String additionalTag : containerImage.getAdditionalTags()) {
containerizer.withAdditionalTag(additionalTag);
}
Expand All @@ -156,16 +155,10 @@ private JibContainer containerize(ApplicationInfoBuildItem applicationInfo, Cont

private Containerizer createContainerizer(ContainerImageConfig containerImageConfig,
ContainerImageInfoBuildItem containerImage,
ApplicationInfoBuildItem applicationInfo, boolean pushRequested) {
boolean pushRequested) {
Containerizer containerizer;
ImageReference imageReference = getImageReference(containerImageConfig, containerImage, applicationInfo);

for (String additionalTag : containerImage.getAdditionalTags()) {
if (!ImageReference.isValidTag(additionalTag)) {
throw new IllegalArgumentException(
"The supplied container-image additional tag '" + additionalTag + "' is invalid");
}
}
ImageReference imageReference = ImageReference.of(containerImage.getRegistry().orElse(null),
containerImage.getRepository(), containerImage.getTag());

if (pushRequested || containerImageConfig.push) {
if (!containerImageConfig.registry.isPresent()) {
Expand All @@ -178,6 +171,7 @@ private Containerizer createContainerizer(ContainerImageConfig containerImageCon
containerizer = Containerizer.to(DockerDaemonImage.named(imageReference));
}
containerizer.setToolName("Quarkus");
containerizer.setToolVersion(Version.getVersion());
containerizer.addEventHandler(LogEvent.class, (e) -> {
if (!e.getMessage().isEmpty()) {
log.log(toJBossLoggingLevel(e.getLevel()), e.getMessage());
Expand Down Expand Up @@ -212,29 +206,6 @@ private Logger.Level toJBossLoggingLevel(LogEvent.Level level) {
}
}

private ImageReference getImageReference(ContainerImageConfig containerImageConfig,
ContainerImageInfoBuildItem containerImage,
ApplicationInfoBuildItem applicationInfo) {

String registry = containerImageConfig.registry.orElse(null);
if ((registry != null) && !ImageReference.isValidRegistry(registry)) {
throw new IllegalArgumentException("The supplied container-image registry '" + registry + "' is invalid");
}

String repository = (containerImageConfig.getEffectiveGroup().map(s -> s + "/").orElse(""))
+ containerImageConfig.name.orElse(applicationInfo.getName());
if (!ImageReference.isValidRepository(repository)) {
throw new IllegalArgumentException("The supplied container-image repository '" + repository + "' is invalid");
}

final String tag = containerImage.getTag();
if (!ImageReference.isValidTag(tag)) {
throw new IllegalArgumentException("The supplied container-image tag '" + tag + "' is invalid");
}

return ImageReference.of(registry, repository, tag);
}

/**
* We don't use Jib's JavaContainerBuilder here because we need to support the custom fast-jar format
* We create the following layers (least likely to change to most likely to change):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ public class ContainerImageConfig {
@ConfigItem
public Optional<String> registry;

/**
* Represents the entire image string.
* If set, then {@code group}, {@code name}, {@code registry}, {@code tags}, {@code additionalTags}
* are ignored
*/
@ConfigItem
public Optional<String> image;

/**
* The username to use to authenticate with the registry where the built image will be pushed
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package io.quarkus.container.image.deployment;

import java.util.Collections;
import java.util.Optional;

import io.quarkus.container.spi.ContainerImageInfoBuildItem;
import io.quarkus.container.spi.ImageReference;
import io.quarkus.deployment.Capabilities;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.ApplicationInfoBuildItem;

public class ContainerImageProcessor {

@BuildStep
public ContainerImageInfoBuildItem publishImageInfo(ApplicationInfoBuildItem app,
ContainerImageConfig containerImageConfig, Capabilities capabilities) {

ensureSingleContainerImageExtension(capabilities);

// additionalTags are used even containerImageConfig.image is set because that string cannot contain multiple tags
if (containerImageConfig.additionalTags.isPresent()) {
for (String additionalTag : containerImageConfig.additionalTags.get()) {
if (!ImageReference.isValidTag(additionalTag)) {
throw new IllegalArgumentException(
"The supplied additional container-image tag '" + additionalTag + "' is invalid");
}
}
}

// if the user supplied the entire image string, use it
if (containerImageConfig.image.isPresent()) {
ImageReference imageReference = ImageReference.parse(containerImageConfig.image.get());
String repository = imageReference.getRepository();
return new ContainerImageInfoBuildItem(Optional.of(imageReference.getRegistry()), repository,
imageReference.getTag(), containerImageConfig.additionalTags.orElse(Collections.emptyList()));
}

String registry = containerImageConfig.registry.orElse(null);
if ((registry != null) && !ImageReference.isValidRegistry(registry)) {
throw new IllegalArgumentException("The supplied container-image registry '" + registry + "' is invalid");
}

String effectiveName = containerImageConfig.name.orElse(app.getName());
String repository = (containerImageConfig.getEffectiveGroup().map(s -> s + "/").orElse("")) + effectiveName;
if (!ImageReference.isValidRepository(repository)) {
throw new IllegalArgumentException("The supplied combination of container-image group '"
+ containerImageConfig.getEffectiveGroup().orElse("") + "' and name '" + effectiveName + "' is invalid");
}

final String effectiveTag = containerImageConfig.tag.orElse(app.getVersion());
if (!ImageReference.isValidTag(effectiveTag)) {
throw new IllegalArgumentException("The supplied container-image tag '" + effectiveTag + "' is invalid");
}

return new ContainerImageInfoBuildItem(containerImageConfig.registry,
containerImageConfig.getEffectiveGroup(),
effectiveName, effectiveTag,
containerImageConfig.additionalTags.orElse(Collections.emptyList()));
}

private void ensureSingleContainerImageExtension(Capabilities capabilities) {
ContainerImageCapabilitiesUtil.getActiveContainerImageCapability(capabilities);
}
}

This file was deleted.

Loading