diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ChangeContainerNameDecorator.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ChangeContainerNameDecorator.java new file mode 100644 index 0000000000000..b8c7f7a1bc2b8 --- /dev/null +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ChangeContainerNameDecorator.java @@ -0,0 +1,52 @@ +package io.quarkus.kubernetes.deployment; + +import io.dekorate.kubernetes.decorator.AddEnvVarDecorator; +import io.dekorate.kubernetes.decorator.AddInitContainerDecorator; +import io.dekorate.kubernetes.decorator.AddLivenessProbeDecorator; +import io.dekorate.kubernetes.decorator.AddMountDecorator; +import io.dekorate.kubernetes.decorator.AddPortDecorator; +import io.dekorate.kubernetes.decorator.AddReadinessProbeDecorator; +import io.dekorate.kubernetes.decorator.AddSidecarDecorator; +import io.dekorate.kubernetes.decorator.ApplicationContainerDecorator; +import io.dekorate.kubernetes.decorator.ApplyArgsDecorator; +import io.dekorate.kubernetes.decorator.ApplyCommandDecorator; +import io.dekorate.kubernetes.decorator.ApplyImageDecorator; +import io.dekorate.kubernetes.decorator.ApplyImagePullPolicyDecorator; +import io.dekorate.kubernetes.decorator.ApplyLimitsCpuDecorator; +import io.dekorate.kubernetes.decorator.ApplyLimitsMemoryDecorator; +import io.dekorate.kubernetes.decorator.ApplyPortNameDecorator; +import io.dekorate.kubernetes.decorator.ApplyRequestsCpuDecorator; +import io.dekorate.kubernetes.decorator.ApplyRequestsMemoryDecorator; +import io.dekorate.kubernetes.decorator.ApplyWorkingDirDecorator; +import io.dekorate.kubernetes.decorator.Decorator; +import io.dekorate.kubernetes.decorator.ResourceProvidingDecorator; +import io.fabric8.kubernetes.api.model.ContainerFluent; + +public class ChangeContainerNameDecorator extends ApplicationContainerDecorator> { + + private final String name; + + public ChangeContainerNameDecorator(String name) { + this.name = name; + } + + @Override + public void andThenVisit(ContainerFluent containerFluent) { + containerFluent.withName(name); + } + + @Override + public Class[] after() { + return new Class[] { + ApplyRequestsMemoryDecorator.class, AddEnvVarDecorator.class, AddMountDecorator.class, + AddPortDecorator.class, ApplyArgsDecorator.class, ApplyCommandDecorator.class, + ApplyImagePullPolicyDecorator.class, + ApplyLimitsCpuDecorator.class, ApplyLimitsMemoryDecorator.class, ApplyPortNameDecorator.class, + ApplyRequestsCpuDecorator.class, + ApplyWorkingDirDecorator.class, ResourceProvidingDecorator.class, AddSidecarDecorator.class, + AddInitContainerDecorator.class, + AddLivenessProbeDecorator.class, AddReadinessProbeDecorator.class, ApplyImageDecorator.class + }; + } + +} diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ChangeContainerNameInDeploymentTriggerDecorator.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ChangeContainerNameInDeploymentTriggerDecorator.java new file mode 100644 index 0000000000000..8dcb7469505a5 --- /dev/null +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ChangeContainerNameInDeploymentTriggerDecorator.java @@ -0,0 +1,41 @@ +package io.quarkus.kubernetes.deployment; + +import io.dekorate.kubernetes.decorator.*; +import io.dekorate.kubernetes.decorator.AddSidecarDecorator; +import io.dekorate.openshift.decorator.ApplyDeploymentTriggerDecorator; +import io.fabric8.kubernetes.api.model.ObjectMeta; +import io.fabric8.openshift.api.model.DeploymentConfigSpecFluent; + +public class ChangeContainerNameInDeploymentTriggerDecorator extends NamedResourceDecorator> { + + private final String containerName; + + public ChangeContainerNameInDeploymentTriggerDecorator(String containerName) { + this.containerName = containerName; + } + + @Override + public void andThenVisit(DeploymentConfigSpecFluent deploymentConfigSpecFluent, ObjectMeta objectMeta) { + if (deploymentConfigSpecFluent.hasTriggers()) { + deploymentConfigSpecFluent + .editFirstTrigger() + .editImageChangeParams() + .withContainerNames(containerName) + .endImageChangeParams() + .endTrigger() + .buildTriggers(); + } + } + + @Override + public Class[] after() { + return new Class[] { ApplyDeploymentTriggerDecorator.class, AddEnvVarDecorator.class, AddPortDecorator.class, + AddMountDecorator.class, AddPvcVolumeDecorator.class, AddAwsElasticBlockStoreVolumeDecorator.class, + AddAzureDiskVolumeDecorator.class, AddAwsElasticBlockStoreVolumeDecorator.class, ApplyImageDecorator.class, + ApplyImagePullPolicyDecorator.class, ApplyWorkingDirDecorator.class, ApplyCommandDecorator.class, + ApplyArgsDecorator.class, ApplyServiceAccountNamedDecorator.class, AddReadinessProbeDecorator.class, + AddLivenessProbeDecorator.class, ApplyApplicationContainerDecorator.class, AddSidecarDecorator.class, + AddInitContainerDecorator.class }; + } + +} diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KnativeConfig.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KnativeConfig.java index 0d979fe4ef81a..f10ef9eac00c3 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KnativeConfig.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KnativeConfig.java @@ -186,6 +186,12 @@ public class KnativeConfig implements PlatformConfiguration { @ConfigItem Map azureDiskVolumes; + /** + * If set, it will change the name of the container according to the configuration + */ + @ConfigItem + Optional containerName; + /** * Init containers */ @@ -264,6 +270,11 @@ public Optional getHost() { return host; } + @Override + public Optional getContainerName() { + return containerName; + } + public Map getPorts() { return ports; } diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KnativeProcessor.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KnativeProcessor.java index 42c6b455214d6..5f2ab02c79480 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KnativeProcessor.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KnativeProcessor.java @@ -152,6 +152,9 @@ public List createDecorators(ApplicationInfoBuildItem applic result.add(new DecoratorBuildItem(KNATIVE, new ApplyContainerImageDecorator(name, i.getImage()))); }); + config.getContainerName().ifPresent(containerName -> result + .add(new DecoratorBuildItem(KNATIVE, new ChangeContainerNameDecorator(containerName)))); + Stream.concat(config.convertToBuildItems().stream(), envs.stream().filter(e -> e.getTarget() == null || KNATIVE.equals(e.getTarget()))).forEach(e -> { result.add(new DecoratorBuildItem(KNATIVE, diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesConfig.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesConfig.java index 21178dbce308a..4d29e06f77f46 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesConfig.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesConfig.java @@ -298,6 +298,12 @@ public enum DeploymentResourceKind { @ConfigItem SecurityContextConfig securityContext; + /** + * If set, it will change the name of the container according to the configuration + */ + @ConfigItem + Optional containerName; + /** * Debug configuration to be set in pods. */ @@ -395,6 +401,11 @@ public Optional getHost() { return host; } + @Override + public Optional getContainerName() { + return containerName; + } + public Map getPorts() { return ports; } diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/OpenshiftConfig.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/OpenshiftConfig.java index 3decd57778c16..a44104a17d82a 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/OpenshiftConfig.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/OpenshiftConfig.java @@ -283,6 +283,12 @@ public static enum DeploymentResourceKind { @ConfigItem ResourcesConfig resources; + /** + * If set, it will change the name of the container according to the configuration + */ + @ConfigItem + Optional containerName; + /** * If true, an Openshift Route will be created * @@ -351,6 +357,11 @@ public Optional getHost() { return host; } + @Override + public Optional getContainerName() { + return containerName; + } + public Integer getReplicas() { return replicas; } 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 fce53fda3d200..0057b9b83a0ff 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 @@ -151,6 +151,7 @@ public List createConfigurators(ApplicationInfoBuildItem image.map(ContainerImageInfoBuildItem::getGroup).ifPresent(g -> { result.add(new ConfiguratorBuildItem(new ApplyImageGroupConfigurator(g))); }); + } return result; } @@ -226,6 +227,17 @@ public List createDecorators(ApplicationInfoBuildItem applic image.ifPresent(i -> { result.add(new DecoratorBuildItem(OPENSHIFT, new ApplyContainerImageDecorator(name, i.getImage()))); }); + if ((!capabilities.isPresent(Capability.CONTAINER_IMAGE_S2I) && !capabilities.isPresent(Capability.OPENSHIFT)) + || (capabilities.isPresent(Capability.OPENSHIFT) && !(capabilities.isPresent(Capability.CONTAINER_IMAGE_S2I) + || capabilities.isPresent(Capability.CONTAINER_IMAGE_OPENSHIFT)))) { + result.add(new DecoratorBuildItem(OPENSHIFT, new RemoveDeploymentTriggerDecorator())); + } + + config.getContainerName().ifPresent(containerName -> { + result.add(new DecoratorBuildItem(OPENSHIFT, new ChangeContainerNameDecorator(containerName))); + result.add(new DecoratorBuildItem(OPENSHIFT, new ChangeContainerNameInDeploymentTriggerDecorator(containerName))); + }); + result.add(new DecoratorBuildItem(OPENSHIFT, new ApplyImagePullPolicyDecorator(name, config.getImagePullPolicy()))); result.add(new DecoratorBuildItem(OPENSHIFT, new AddLabelDecorator(name, OPENSHIFT_APP_RUNTIME, QUARKUS))); diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/PlatformConfiguration.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/PlatformConfiguration.java index 16c871b957854..9cb78aa7ff170 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/PlatformConfiguration.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/PlatformConfiguration.java @@ -34,6 +34,8 @@ public interface PlatformConfiguration extends EnvVarHolder { Optional getHost(); + Optional getContainerName(); + Map getPorts(); ServiceType getServiceType(); diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/VanillaKubernetesProcessor.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/VanillaKubernetesProcessor.java index b409c1d991005..b5c85c700fe8e 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/VanillaKubernetesProcessor.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/VanillaKubernetesProcessor.java @@ -165,6 +165,9 @@ public List createDecorators(ApplicationInfoBuildItem applic result.add(new DecoratorBuildItem(KUBERNETES, new RemoveFromMatchingLabelsDecorator(name, Labels.VERSION))); } + config.getContainerName().ifPresent(containerName -> result + .add(new DecoratorBuildItem(KUBERNETES, new ChangeContainerNameDecorator(containerName)))); + // Service handling result.add(new DecoratorBuildItem(KUBERNETES, new ApplyServiceTypeDecorator(name, config.getServiceType().name()))); if ((config.getServiceType() == ServiceType.NodePort)) { diff --git a/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KnativeWithSpecifiedContainerNameTest.java b/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KnativeWithSpecifiedContainerNameTest.java new file mode 100644 index 0000000000000..764cf5d6522f5 --- /dev/null +++ b/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KnativeWithSpecifiedContainerNameTest.java @@ -0,0 +1,57 @@ +package io.quarkus.it.kubernetes; + +import static org.assertj.core.api.Assertions.assertThat; + +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.knative.serving.v1.Service; +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.quarkus.test.ProdBuildResults; +import io.quarkus.test.ProdModeTestResults; +import io.quarkus.test.QuarkusProdModeTest; + +public class KnativeWithSpecifiedContainerNameTest { + + @RegisterExtension + static final QuarkusProdModeTest config = new QuarkusProdModeTest() + .withApplicationRoot((jar) -> jar.addClasses(GreetingResource.class)) + .setApplicationName("knative-with-specified-container-name") + .setApplicationVersion("0.1-SNAPSHOT") + .withConfigurationResource("knative-with-specified-container-name.properties"); + + @ProdBuildResults + private ProdModeTestResults prodModeTestResults; + + @Test + public void assertGeneratedResources() throws IOException { + Path kubernetesDir = prodModeTestResults.getBuildDir().resolve("kubernetes"); + assertThat(kubernetesDir) + .isDirectoryContaining(p -> p.getFileName().endsWith("knative.json")) + .isDirectoryContaining(p -> p.getFileName().endsWith("knative.yml")) + .satisfies(p -> assertThat(p.toFile().listFiles()).hasSize(2)); + + List kubernetesList = DeserializationUtil + .deserializeAsList(kubernetesDir.resolve("knative.yml")); + + assertThat(kubernetesList).filteredOn(i -> "Service".equals(i.getKind())).singleElement().satisfies(i -> { + assertThat(i).isInstanceOfSatisfying(Service.class, s -> { + assertThat(s.getSpec()).satisfies(spec -> { + assertThat(s.getMetadata()).satisfies(m -> { + assertThat(m.getName()).isEqualTo("kfoo"); + }); + + assertThat(spec.getTemplate().getSpec().getContainers()).singleElement().satisfies(container -> { + assertThat(container.getCommand()).contains("my-command"); + assertThat(container.getName()).isEqualTo("kbar"); + assertThat(container.getArgs()).containsExactly("A", "B"); + }); + }); + }); + }); + } +} diff --git a/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KubernetesWithSpecifiedContainerNameTest.java b/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KubernetesWithSpecifiedContainerNameTest.java new file mode 100644 index 0000000000000..c50bbdad02ef4 --- /dev/null +++ b/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KubernetesWithSpecifiedContainerNameTest.java @@ -0,0 +1,90 @@ +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.Arrays; +import java.util.Collection; +import java.util.List; + +import org.assertj.core.api.AbstractObjectAssert; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.fabric8.kubernetes.api.model.*; +import io.fabric8.kubernetes.api.model.apps.Deployment; +import io.fabric8.openshift.api.model.DeploymentTriggerPolicy; +import io.quarkus.bootstrap.model.AppArtifact; +import io.quarkus.builder.Version; +import io.quarkus.test.ProdBuildResults; +import io.quarkus.test.ProdModeTestResults; +import io.quarkus.test.QuarkusProdModeTest; + +public class KubernetesWithSpecifiedContainerNameTest { + + @RegisterExtension + static final QuarkusProdModeTest config = new QuarkusProdModeTest() + .withApplicationRoot((jar) -> jar.addClasses(GreetingResource.class)) + .setApplicationName("kubernetes-with-specified-container-name") + .setApplicationVersion("0.1-SNAPSHOT") + .withConfigurationResource("kubernetes-with-specified-container-name.properties") + .setForcedDependencies( + Arrays.asList( + new AppArtifact("io.quarkus", "quarkus-kubernetes", Version.getVersion()), + new AppArtifact("io.quarkus", "quarkus-container-image-s2i", Version.getVersion()))); + + @ProdBuildResults + private ProdModeTestResults prodModeTestResults; + + @Test + public void assertGeneratedResources() throws IOException { + Path kubernetesDir = prodModeTestResults.getBuildDir().resolve("kubernetes"); + assertThat(kubernetesDir) + .isDirectoryContaining(p -> p.getFileName().endsWith("kubernetes.json")) + .isDirectoryContaining(p -> p.getFileName().endsWith("kubernetes.yml")); + + List kubernetesList = DeserializationUtil + .deserializeAsList(kubernetesDir.resolve("kubernetes.yml")); + assertThat(kubernetesList.get(0)).isInstanceOfSatisfying(Deployment.class, d -> { + assertThat(d.getMetadata()).satisfies(m -> { + assertThat(m.getName()).isEqualTo("foo"); + assertThat(m.getLabels()).contains(entry("app.kubernetes.io/name", "foo")); + }); + + assertThat(d.getSpec().getTemplate().getSpec().getContainers().get(0)) + .satisfies(c -> assertThat(c.getName()).isEqualTo("bar")); + assertThat(d.getSpec().getTemplate().getSpec().getContainers().get(0)) + .satisfies(c -> assertThat(c.getCommand()).contains("my-command")); + assertThat(d.getSpec().getTemplate().getSpec().getContainers().get(0)) + .satisfies(c -> assertThat(c.getArgs()).containsExactly("A", "B")); + }); + + List openshiftList = DeserializationUtil + .deserializeAsList(kubernetesDir.resolve("openshift.yml")); + assertThat(openshiftList).filteredOn(h -> "DeploymentConfig".equals(h.getKind())).singleElement().satisfies(h -> { + AbstractObjectAssert specAssert = assertThat(h).extracting("spec"); + specAssert.extracting("template").extracting("spec").isInstanceOfSatisfying(PodSpec.class, + podSpec -> { + assertThat(podSpec.getContainers()).singleElement().satisfies(container -> { + assertThat(container.getCommand()) + .contains("my-openshift-command"); + assertThat(container.getArgs()).containsExactly("C", "D"); + assertThat(container) + .satisfies(c -> assertThat(c.getName()).isEqualTo("obar")); + }); + }); + + specAssert.extracting("triggers").isInstanceOfSatisfying(Collection.class, c -> { + assertThat(c).singleElement().satisfies(trigger -> { + assertThat(((DeploymentTriggerPolicy) trigger).getImageChangeParams().getContainerNames()) + .contains("obar"); + }); + }); + + assertThat(h.getMetadata().getName()).isIn("ofoo", "foo", "openjdk-11"); + assertThat(h.getMetadata().getLabels()).contains(entry("app.kubernetes.io/name", "ofoo")); + }); + } +} diff --git a/integration-tests/kubernetes/quarkus-standard-way/src/test/resources/knative-with-specified-container-name.properties b/integration-tests/kubernetes/quarkus-standard-way/src/test/resources/knative-with-specified-container-name.properties new file mode 100644 index 0000000000000..c42c24befcaa6 --- /dev/null +++ b/integration-tests/kubernetes/quarkus-standard-way/src/test/resources/knative-with-specified-container-name.properties @@ -0,0 +1,6 @@ +# Configuration file +quarkus.kubernetes.deployment-target=knative +quarkus.knative.name=kfoo +quarkus.knative.container-name=kbar +quarkus.knative.command=my-command +quarkus.knative.arguments=A,B \ No newline at end of file diff --git a/integration-tests/kubernetes/quarkus-standard-way/src/test/resources/kubernetes-with-specified-container-name.properties b/integration-tests/kubernetes/quarkus-standard-way/src/test/resources/kubernetes-with-specified-container-name.properties new file mode 100644 index 0000000000000..11060234e7ba7 --- /dev/null +++ b/integration-tests/kubernetes/quarkus-standard-way/src/test/resources/kubernetes-with-specified-container-name.properties @@ -0,0 +1,14 @@ +quarkus.kubernetes.deployment-target=kubernetes,openshift + +quarkus.application.name=test +quarkus.kubernetes.name=foo +quarkus.kubernetes.container-name=bar +quarkus.kubernetes.command=my-command +quarkus.kubernetes.arguments=A,B + +quarkus.openshift.name=ofoo +quarkus.openshift.container-name=obar +quarkus.openshift.command=my-openshift-command +quarkus.openshift.arguments=C,D + +quarkus.s2i.name=s2ifoo \ No newline at end of file