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 49ce2c288cc17..108dceae4c84a 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 @@ -7,8 +7,11 @@ import static io.quarkus.kubernetes.spi.KubernetesDeploymentTargetBuildItem.DEFAULT_PRIORITY; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; import java.util.stream.Stream; import io.dekorate.knative.decorator.AddAwsElasticBlockStoreVolumeToRevisionDecorator; @@ -28,13 +31,17 @@ import io.dekorate.knative.decorator.ApplyRevisionNameDecorator; import io.dekorate.knative.decorator.ApplyServiceAccountToRevisionSpecDecorator; import io.dekorate.knative.decorator.ApplyTrafficDecorator; +import io.dekorate.kubernetes.config.ConfigMapVolumeBuilder; import io.dekorate.kubernetes.config.EnvBuilder; +import io.dekorate.kubernetes.config.MountBuilder; import io.dekorate.kubernetes.config.Port; +import io.dekorate.kubernetes.config.SecretVolumeBuilder; import io.dekorate.kubernetes.decorator.AddConfigMapDataDecorator; import io.dekorate.kubernetes.decorator.AddConfigMapResourceProvidingDecorator; import io.dekorate.kubernetes.decorator.AddEnvVarDecorator; import io.dekorate.kubernetes.decorator.AddImagePullSecretToServiceAccountDecorator; import io.dekorate.kubernetes.decorator.AddLabelDecorator; +import io.dekorate.kubernetes.decorator.AddMountDecorator; import io.dekorate.kubernetes.decorator.AddServiceAccountResourceDecorator; import io.dekorate.kubernetes.decorator.ApplicationContainerDecorator; import io.dekorate.kubernetes.decorator.ApplyImagePullPolicyDecorator; @@ -311,6 +318,7 @@ public List createDecorators(ApplicationInfoBuildItem applic //Add revision decorators result.addAll(createVolumeDecorators(project, name, config)); + result.addAll(createAppConfigVolumeAndEnvDecorators(project, name, config)); config.getHostAliases().entrySet().forEach(e -> { result.add(new DecoratorBuildItem(KNATIVE, new AddHostAliasesToRevisionDecorator(name, HostAliasConverter.convert(e)))); @@ -375,4 +383,45 @@ private static List createVolumeDecorators(Optional }); return result; } + + private static List createAppConfigVolumeAndEnvDecorators(Optional project, String name, + PlatformConfiguration config) { + + List result = new ArrayList<>(); + Set paths = new HashSet<>(); + + config.getAppSecret().ifPresent(s -> { + result.add(new DecoratorBuildItem(KNATIVE, new AddSecretVolumeToRevisionDecorator(new SecretVolumeBuilder() + .withSecretName(s) + .withVolumeName("app-secret") + .build()))); + result.add(new DecoratorBuildItem(KNATIVE, new AddMountDecorator(new MountBuilder() + .withName("app-secret") + .withPath("/mnt/app-secret") + .build()))); + paths.add("/mnt/app-secret"); + }); + + config.getAppConfigMap().ifPresent(s -> { + result.add(new DecoratorBuildItem(KNATIVE, new AddConfigMapVolumeToRevisionDecorator(new ConfigMapVolumeBuilder() + .withConfigMapName(s) + .withVolumeName("app-config-map") + .build()))); + result.add(new DecoratorBuildItem(KNATIVE, new AddMountDecorator(new MountBuilder() + .withName("app-config-map") + .withPath("/mnt/app-config-map") + .build()))); + paths.add("/mnt/app-config-map"); + }); + + if (!paths.isEmpty()) { + result.add(new DecoratorBuildItem(KNATIVE, + new AddEnvVarDecorator(ApplicationContainerDecorator.ANY, name, new EnvBuilder() + .withName("SMALLRYE_CONFIG_LOCATIONS") + .withValue(paths.stream().collect(Collectors.joining(","))) + .build()))); + } + return result; + } + } diff --git a/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KnativeWithAppConfigMapTest.java b/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KnativeWithAppConfigMapTest.java new file mode 100644 index 0000000000000..cec524807dbb0 --- /dev/null +++ b/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KnativeWithAppConfigMapTest.java @@ -0,0 +1,79 @@ +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.assertj.core.api.AbstractObjectAssert; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.fabric8.knative.serving.v1.RevisionSpec; +import io.fabric8.knative.serving.v1.Service; +import io.fabric8.kubernetes.api.model.EnvVar; +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.api.model.Volume; +import io.fabric8.kubernetes.api.model.VolumeMount; +import io.quarkus.test.ProdBuildResults; +import io.quarkus.test.ProdModeTestResults; +import io.quarkus.test.QuarkusProdModeTest; + +public class KnativeWithAppConfigMapTest { + + @RegisterExtension + static final QuarkusProdModeTest config = new QuarkusProdModeTest() + .withApplicationRoot((jar) -> jar.addClasses(GreetingResource.class)) + .setApplicationName("knative-with-app-config-map") + .setApplicationVersion("0.1-SNAPSHOT") + .withConfigurationResource("knative-with-app-config-map.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("knative-with-app-config-map"); + }); + }); + }); + + AbstractObjectAssert specAssert = assertThat(i).extracting("spec"); + specAssert.extracting("template").extracting("spec").isInstanceOfSatisfying(RevisionSpec.class, + revisionSpec -> { + assertThat(revisionSpec.getContainers()).singleElement().satisfies(container -> { + List envVars = container.getEnv(); + assertThat(envVars).anySatisfy(envVar -> { + assertThat(envVar.getName()).isEqualTo("SMALLRYE_CONFIG_LOCATIONS"); + assertThat(envVar.getValue()).isEqualTo("/mnt/app-config-map"); + }); + + List mounts = container.getVolumeMounts(); + assertThat(mounts).anySatisfy(mount -> { + assertThat(mount.getName()).isEqualTo("app-config-map"); + assertThat(mount.getMountPath()).isEqualTo("/mnt/app-config-map"); + }); + }); + List volumes = revisionSpec.getVolumes(); + assertThat(volumes).anySatisfy(volume -> { + assertThat(volume.getName()).isEqualTo("app-config-map"); + assertThat(volume.getConfigMap().getName()).isEqualTo("my-kn-config-map"); + }); + }); + }); + } +} diff --git a/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KnativeWithAppSecretTest.java b/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KnativeWithAppSecretTest.java new file mode 100644 index 0000000000000..820cdf22395fc --- /dev/null +++ b/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KnativeWithAppSecretTest.java @@ -0,0 +1,79 @@ +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.assertj.core.api.AbstractObjectAssert; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.fabric8.knative.serving.v1.RevisionSpec; +import io.fabric8.knative.serving.v1.Service; +import io.fabric8.kubernetes.api.model.EnvVar; +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.api.model.Volume; +import io.fabric8.kubernetes.api.model.VolumeMount; +import io.quarkus.test.ProdBuildResults; +import io.quarkus.test.ProdModeTestResults; +import io.quarkus.test.QuarkusProdModeTest; + +public class KnativeWithAppSecretTest { + + @RegisterExtension + static final QuarkusProdModeTest config = new QuarkusProdModeTest() + .withApplicationRoot((jar) -> jar.addClasses(GreetingResource.class)) + .setApplicationName("knative-with-app-secret") + .setApplicationVersion("0.1-SNAPSHOT") + .withConfigurationResource("knative-with-app-secret.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("knative-with-app-secret"); + }); + }); + }); + + AbstractObjectAssert specAssert = assertThat(i).extracting("spec"); + specAssert.extracting("template").extracting("spec").isInstanceOfSatisfying(RevisionSpec.class, + revisionSpec -> { + assertThat(revisionSpec.getContainers()).singleElement().satisfies(container -> { + List envVars = container.getEnv(); + assertThat(envVars).anySatisfy(envVar -> { + assertThat(envVar.getName()).isEqualTo("SMALLRYE_CONFIG_LOCATIONS"); + assertThat(envVar.getValue()).isEqualTo("/mnt/app-secret"); + }); + + List mounts = container.getVolumeMounts(); + assertThat(mounts).anySatisfy(mount -> { + assertThat(mount.getName()).isEqualTo("app-secret"); + assertThat(mount.getMountPath()).isEqualTo("/mnt/app-secret"); + }); + }); + List volumes = revisionSpec.getVolumes(); + assertThat(volumes).anySatisfy(volume -> { + assertThat(volume.getName()).isEqualTo("app-secret"); + assertThat(volume.getSecret().getSecretName()).isEqualTo("my-kn-secret"); + }); + }); + }); + } +} diff --git a/integration-tests/kubernetes/quarkus-standard-way/src/test/resources/knative-with-app-config-map.properties b/integration-tests/kubernetes/quarkus-standard-way/src/test/resources/knative-with-app-config-map.properties new file mode 100644 index 0000000000000..ea7497503112b --- /dev/null +++ b/integration-tests/kubernetes/quarkus-standard-way/src/test/resources/knative-with-app-config-map.properties @@ -0,0 +1,2 @@ +quarkus.kubernetes.deployment-target=knative +quarkus.knative.app-config-map=my-kn-config-map diff --git a/integration-tests/kubernetes/quarkus-standard-way/src/test/resources/knative-with-app-secret.properties b/integration-tests/kubernetes/quarkus-standard-way/src/test/resources/knative-with-app-secret.properties new file mode 100644 index 0000000000000..baabf949dea8e --- /dev/null +++ b/integration-tests/kubernetes/quarkus-standard-way/src/test/resources/knative-with-app-secret.properties @@ -0,0 +1,2 @@ +quarkus.kubernetes.deployment-target=knative +quarkus.knative.app-secret=my-kn-secret