From ad4d8258dc4562335fa5035765d48cb05fe6bda0 Mon Sep 17 00:00:00 2001 From: Yelzhas Suleimenov Date: Sat, 2 Apr 2022 01:52:15 +0200 Subject: [PATCH] kubernetes/openshift/knative specified container name support;fix#10875 returned; --- .../ChangeContainerNameDecorator.java | 30 +++++++ ...ainerNameInDeploymentTriggerDecorator.java | 41 +++++++++ .../kubernetes/deployment/KnativeConfig.java | 11 +++ .../deployment/KnativeProcessor.java | 8 +- .../deployment/KubernetesConfig.java | 11 +++ .../deployment/OpenshiftConfig.java | 11 +++ .../deployment/OpenshiftProcessor.java | 18 ++-- .../deployment/PlatformConfiguration.java | 2 + .../VanillaKubernetesProcessor.java | 3 + ...KnativeWithSpecifiedContainerNameTest.java | 55 ++++++++++++ ...ernetesWithSpecifiedContainerNameTest.java | 83 +++++++++++++++++++ ...e-with-specified-container-name.properties | 4 + ...s-with-specified-container-name.properties | 10 +++ 13 files changed, 277 insertions(+), 10 deletions(-) create mode 100644 extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ChangeContainerNameDecorator.java create mode 100644 extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ChangeContainerNameInDeploymentTriggerDecorator.java create mode 100644 integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KnativeWithSpecifiedContainerNameTest.java create mode 100644 integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KubernetesWithSpecifiedContainerNameTest.java create mode 100644 integration-tests/kubernetes/quarkus-standard-way/src/test/resources/knative-with-specified-container-name.properties create mode 100644 integration-tests/kubernetes/quarkus-standard-way/src/test/resources/kubernetes-with-specified-container-name.properties 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..4d73dd046cd60 --- /dev/null +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ChangeContainerNameDecorator.java @@ -0,0 +1,30 @@ +package io.quarkus.kubernetes.deployment; + +import io.dekorate.kubernetes.decorator.AddInitContainerDecorator; +import io.dekorate.kubernetes.decorator.AddSidecarDecorator; +import io.dekorate.kubernetes.decorator.ApplicationContainerDecorator; +import io.dekorate.kubernetes.decorator.ApplyImageDecorator; +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[] { ResourceProvidingDecorator.class, AddSidecarDecorator.class, AddInitContainerDecorator.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..3cc4c0e8ef3d4 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 @@ -1,9 +1,6 @@ package io.quarkus.kubernetes.deployment; -import static io.quarkus.kubernetes.deployment.Constants.KNATIVE; -import static io.quarkus.kubernetes.deployment.Constants.KNATIVE_SERVICE; -import static io.quarkus.kubernetes.deployment.Constants.KNATIVE_SERVICE_GROUP; -import static io.quarkus.kubernetes.deployment.Constants.KNATIVE_SERVICE_VERSION; +import static io.quarkus.kubernetes.deployment.Constants.*; import static io.quarkus.kubernetes.spi.KubernetesDeploymentTargetBuildItem.DEFAULT_PRIORITY; import java.util.ArrayList; @@ -152,6 +149,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..bdfb09f2ec83f 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 @@ -1,12 +1,7 @@ package io.quarkus.kubernetes.deployment; -import static io.quarkus.kubernetes.deployment.Constants.DEFAULT_HTTP_PORT; -import static io.quarkus.kubernetes.deployment.Constants.DEFAULT_S2I_IMAGE_NAME; -import static io.quarkus.kubernetes.deployment.Constants.HTTP_PORT; -import static io.quarkus.kubernetes.deployment.Constants.OPENSHIFT; -import static io.quarkus.kubernetes.deployment.Constants.OPENSHIFT_APP_RUNTIME; -import static io.quarkus.kubernetes.deployment.Constants.QUARKUS; +import static io.quarkus.kubernetes.deployment.Constants.*; import static io.quarkus.kubernetes.deployment.OpenshiftConfig.OpenshiftFlavor.v3; import static io.quarkus.kubernetes.spi.KubernetesDeploymentTargetBuildItem.DEFAULT_PRIORITY; @@ -151,6 +146,7 @@ public List createConfigurators(ApplicationInfoBuildItem image.map(ContainerImageInfoBuildItem::getGroup).ifPresent(g -> { result.add(new ConfiguratorBuildItem(new ApplyImageGroupConfigurator(g))); }); + } return result; } @@ -226,6 +222,16 @@ 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)) { + 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..e75989ca668ba --- /dev/null +++ b/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KnativeWithSpecifiedContainerNameTest.java @@ -0,0 +1,55 @@ +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.getName()).isEqualTo("kbar"); + }); + }); + }); + }); + } +} 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..f4110762361b9 --- /dev/null +++ b/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KubernetesWithSpecifiedContainerNameTest.java @@ -0,0 +1,83 @@ +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")); + }); + + 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) + .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..b2957c3c3ef79 --- /dev/null +++ b/integration-tests/kubernetes/quarkus-standard-way/src/test/resources/knative-with-specified-container-name.properties @@ -0,0 +1,4 @@ +# Configuration file +quarkus.kubernetes.deployment-target=knative +quarkus.knative.name=kfoo +quarkus.knative.container-name=kbar \ 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..3674efa507a27 --- /dev/null +++ b/integration-tests/kubernetes/quarkus-standard-way/src/test/resources/kubernetes-with-specified-container-name.properties @@ -0,0 +1,10 @@ +quarkus.kubernetes.deployment-target=kubernetes,openshift + +quarkus.application.name=test +quarkus.kubernetes.name=foo +quarkus.kubernetes.container-name=bar + +quarkus.openshift.name=ofoo +quarkus.openshift.container-name=obar + +quarkus.s2i.name=s2ifoo \ No newline at end of file