diff --git a/extensions/container-image/deployment/src/main/java/io/quarkus/container/image/deployment/ContainerImageProcessor.java b/extensions/container-image/deployment/src/main/java/io/quarkus/container/image/deployment/ContainerImageProcessor.java index 23df621db72e1..0558bd01afc5f 100644 --- a/extensions/container-image/deployment/src/main/java/io/quarkus/container/image/deployment/ContainerImageProcessor.java +++ b/extensions/container-image/deployment/src/main/java/io/quarkus/container/image/deployment/ContainerImageProcessor.java @@ -10,6 +10,7 @@ import org.jboss.logging.Logger; import io.quarkus.container.spi.ContainerImageBuildRequestBuildItem; +import io.quarkus.container.spi.ContainerImageCustomNameBuildItem; import io.quarkus.container.spi.ContainerImageInfoBuildItem; import io.quarkus.container.spi.ContainerImagePushRequestBuildItem; import io.quarkus.container.spi.FallbackContainerImageRegistryBuildItem; @@ -48,6 +49,7 @@ public void ignoreCredentialsChange(BuildProducer containerImageRegistry, + Optional containerImageCustomName, Capabilities capabilities, BuildProducer containerImage) { @@ -86,7 +88,9 @@ public void publishImageInfo(ApplicationInfoBuildItem app, throw new IllegalArgumentException("The supplied container-image registry '" + registry + "' is invalid"); } - String effectiveName = containerImageConfig.name.orElse(app.getName()); + String effectiveName = containerImageCustomName.map(ContainerImageCustomNameBuildItem::getName) + .or(() -> 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 '" diff --git a/extensions/container-image/deployment/src/test/java/io/quarkus/container/image/deployment/ContainerImageInfoTest.java b/extensions/container-image/deployment/src/test/java/io/quarkus/container/image/deployment/ContainerImageInfoTest.java index 70d2591059e1b..0a702f198d631 100644 --- a/extensions/container-image/deployment/src/test/java/io/quarkus/container/image/deployment/ContainerImageInfoTest.java +++ b/extensions/container-image/deployment/src/test/java/io/quarkus/container/image/deployment/ContainerImageInfoTest.java @@ -122,7 +122,7 @@ private void whenPublishImageInfo() { Capabilities capabilities = new Capabilities(Collections.emptySet()); BuildProducer containerImage = actualImageConfig -> actualContainerImageInfo = actualImageConfig; ContainerImageProcessor processor = new ContainerImageProcessor(); - processor.publishImageInfo(app, containerImageConfig, Optional.empty(), capabilities, containerImage); + processor.publishImageInfo(app, containerImageConfig, Optional.empty(), Optional.empty(), capabilities, containerImage); } private void thenImageIs(String expectedImage) { diff --git a/extensions/container-image/spi/src/main/java/io/quarkus/container/spi/ContainerImageCustomNameBuildItem.java b/extensions/container-image/spi/src/main/java/io/quarkus/container/spi/ContainerImageCustomNameBuildItem.java new file mode 100644 index 0000000000000..61dfd15ed223b --- /dev/null +++ b/extensions/container-image/spi/src/main/java/io/quarkus/container/spi/ContainerImageCustomNameBuildItem.java @@ -0,0 +1,25 @@ +package io.quarkus.container.spi; + +import io.quarkus.builder.item.BuildItem; +import io.quarkus.builder.item.SimpleBuildItem; + +/** + * This {@link BuildItem} can be used to override the default image name. + * It can be used in cases where the name of the image is customized externally. + * Example: The openshift extension may override the name. To ensure that things are in sync + * with the image name needs to be set. + * + */ +public final class ContainerImageCustomNameBuildItem extends SimpleBuildItem { + + private final String name; + + public ContainerImageCustomNameBuildItem(String name) { + this.name = name; + } + + public String getName() { + return name; + } + +} diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/OpenshiftProcessor.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/OpenshiftProcessor.java index 199b54be05228..710a1a892fe74 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/OpenshiftProcessor.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/OpenshiftProcessor.java @@ -37,6 +37,7 @@ import io.quarkus.container.image.deployment.ContainerImageConfig; import io.quarkus.container.image.deployment.util.ImageUtil; import io.quarkus.container.spi.BaseImageInfoBuildItem; +import io.quarkus.container.spi.ContainerImageCustomNameBuildItem; import io.quarkus.container.spi.ContainerImageInfoBuildItem; import io.quarkus.container.spi.ContainerImageLabelBuildItem; import io.quarkus.container.spi.FallbackContainerImageRegistryBuildItem; @@ -97,6 +98,12 @@ public void checkOpenshift(ApplicationInfoBuildItem applicationInfo, Capabilitie } } + @BuildStep + public void populateCustomImageName(OpenshiftConfig openshiftConfig, + BuildProducer containerImageCustomName) { + openshiftConfig.name.ifPresent(name -> containerImageCustomName.produce(new ContainerImageCustomNameBuildItem(name))); + } + @BuildStep public void populateInternalRegistry(OpenshiftConfig openshiftConfig, ContainerImageConfig containerImageConfig, Capabilities capabilities, @@ -381,4 +388,4 @@ void externalizeInitTasks( decorators); } } -} \ No newline at end of file +} diff --git a/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/OpenshiftWithCustomNameTest.java b/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/OpenshiftWithCustomNameTest.java new file mode 100644 index 0000000000000..b6852ef252c4c --- /dev/null +++ b/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/OpenshiftWithCustomNameTest.java @@ -0,0 +1,89 @@ + +package io.quarkus.it.kubernetes; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.List; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.api.model.apps.Deployment; +import io.quarkus.builder.Version; +import io.quarkus.maven.dependency.Dependency; +import io.quarkus.test.ProdBuildResults; +import io.quarkus.test.ProdModeTestResults; +import io.quarkus.test.QuarkusProdModeTest; + +// +// The purpose of this test is to assert that +// When: We run an in-cluster container builds targeting Openshift (and `Deployment` is used). +// Then: +// - A BuildConfg is generated. +// - Two ImageStream are generated (one named after the app). +// - A Deployment resource was created +// - image-registry.openshift-image-registry.svc:5000 is used as registry (why? so that Deployment can point to the incluster built image). +// +public class OpenshiftWithCustomNameTest { + + private static final String NAME = "openshift-with-custom-name"; + private static final String CUSTOM_NAME = "custom-name"; + + @RegisterExtension + static final QuarkusProdModeTest config = new QuarkusProdModeTest() + .withApplicationRoot((jar) -> jar.addClasses(GreetingResource.class)) + .setApplicationName(NAME) + .setApplicationVersion("0.1-SNAPSHOT") + .overrideConfigKey("quarkus.openshift.name", "custom-name") + .overrideConfigKey("quarkus.openshift.deployment-kind", "Deployment") + .overrideConfigKey("quarkus.container-image.group", "testme") + .setLogFileName("k8s.log") + .setForcedDependencies(List.of(Dependency.of("io.quarkus", "quarkus-openshift", Version.getVersion()))); + + @ProdBuildResults + private ProdModeTestResults prodModeTestResults; + + @Test + public void assertGeneratedResources() throws IOException { + final Path kubernetesDir = prodModeTestResults.getBuildDir().resolve("kubernetes"); + assertThat(kubernetesDir) + .isDirectoryContaining(p -> p.getFileName().endsWith("openshift.json")) + .isDirectoryContaining(p -> p.getFileName().endsWith("openshift.yml")); + List kubernetesList = DeserializationUtil + .deserializeAsList(kubernetesDir.resolve("openshift.yml")); + + assertThat(kubernetesList).filteredOn(h -> "BuildConfig".equals(h.getKind())).hasSize(1); + assertThat(kubernetesList).filteredOn(h -> "ImageStream".equals(h.getKind())).hasSize(2); + assertThat(kubernetesList).filteredOn(h -> "ImageStream".equals(h.getKind()) + && h.getMetadata().getName().equals(CUSTOM_NAME)).hasSize(1); + + assertThat(kubernetesList).filteredOn(i -> i instanceof Deployment).singleElement().satisfies(i -> { + assertThat(i).isInstanceOfSatisfying(Deployment.class, d -> { + assertThat(d.getMetadata()).satisfies(m -> { + assertThat(m.getName()).isEqualTo("custom-name"); + }); + + assertThat(d.getSpec()).satisfies(deploymentSpec -> { + assertThat(deploymentSpec.getTemplate()).satisfies(t -> { + assertThat(t.getMetadata()).satisfies(metadata -> assertThat(metadata.getLabels()).containsAnyOf( + entry("app.kubernetes.io/name", CUSTOM_NAME), + entry("app.kubernetes.io/version", "0.1-SNAPSHOT"))); + + assertThat(t.getSpec()).satisfies(podSpec -> { + assertThat(podSpec.getContainers()).singleElement().satisfies(container -> { + assertThat(container.getImage()) + .isEqualTo( + "image-registry.openshift-image-registry.svc:5000/testme/" + CUSTOM_NAME + + ":0.1-SNAPSHOT"); + }); + }); + }); + }); + }); + }); + } +}