From 13cc3f5d63a7ae6eac6eb6e28b125d611303b6ce Mon Sep 17 00:00:00 2001 From: Matheus Cruz Date: Fri, 4 Oct 2024 01:53:54 -0300 Subject: [PATCH] Add ingress and rules fir kind/minikube clusters --- .../kind/deployment/KindProcessor.java | 1 - .../deployment/DevClusterHelper.java | 21 ++++ .../kubernetes/deployment/IngressConfig.java | 4 + .../deployment/KubernetesConfig.java | 4 + .../kubernetes/KubernetesWithKindIngress.java | 97 +++++++++++++++++++ .../KubernetesWithMinikubeIngress.java | 97 +++++++++++++++++++ 6 files changed, 223 insertions(+), 1 deletion(-) create mode 100644 integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KubernetesWithKindIngress.java create mode 100644 integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KubernetesWithMinikubeIngress.java diff --git a/extensions/kubernetes/kind/deployment/src/main/java/io/quarkus/kind/deployment/KindProcessor.java b/extensions/kubernetes/kind/deployment/src/main/java/io/quarkus/kind/deployment/KindProcessor.java index b3b2ce748ddf23..f0303377ca61ce 100644 --- a/extensions/kubernetes/kind/deployment/src/main/java/io/quarkus/kind/deployment/KindProcessor.java +++ b/extensions/kubernetes/kind/deployment/src/main/java/io/quarkus/kind/deployment/KindProcessor.java @@ -164,7 +164,6 @@ void externalizeInitTasks( BuildProducer roles, BuildProducer roleBindings, BuildProducer serviceAccount, - BuildProducer decorators) { final String name = ResourceNameUtil.getResourceName(config, applicationInfo); if (config.isExternalizeInit()) { diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/DevClusterHelper.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/DevClusterHelper.java index a8260e326e3c49..6c1f6893c02cb8 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/DevClusterHelper.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/DevClusterHelper.java @@ -1,6 +1,7 @@ package io.quarkus.kubernetes.deployment; +import static io.quarkus.kubernetes.deployment.Constants.INGRESS; import static io.quarkus.kubernetes.deployment.Constants.KUBERNETES; import static io.quarkus.kubernetes.deployment.Constants.LIVENESS_PROBE; import static io.quarkus.kubernetes.deployment.Constants.MAX_NODE_PORT_VALUE; @@ -23,8 +24,11 @@ import io.dekorate.kubernetes.annotation.ServiceType; import io.dekorate.kubernetes.config.EnvBuilder; +import io.dekorate.kubernetes.config.IngressRuleBuilder; import io.dekorate.kubernetes.config.Port; +import io.dekorate.kubernetes.decorator.AddAnnotationDecorator; import io.dekorate.kubernetes.decorator.AddEnvVarDecorator; +import io.dekorate.kubernetes.decorator.AddIngressRuleDecorator; import io.dekorate.kubernetes.decorator.ApplicationContainerDecorator; import io.dekorate.kubernetes.decorator.ApplyImagePullPolicyDecorator; import io.dekorate.project.Project; @@ -153,6 +157,23 @@ public static List createDecorators(String clusterKind, ports, config.ports)); + for (Map.Entry annotation : config.getIngress().getAnnotations().entrySet()) { + result.add(new DecoratorBuildItem(clusterKind, + new AddAnnotationDecorator(name, annotation.getKey(), annotation.getValue(), INGRESS))); + } + + for (IngressRuleConfig rule : config.ingress.rules.values()) { + result.add(new DecoratorBuildItem(clusterKind, new AddIngressRuleDecorator(name, port, + new IngressRuleBuilder() + .withHost(rule.host) + .withPath(rule.path) + .withPathType(rule.pathType) + .withServiceName(rule.serviceName.orElse(null)) + .withServicePortName(rule.servicePortName.orElse(null)) + .withServicePortNumber(rule.servicePortNumber.orElse(-1)) + .build()))); + } + // Handle init Containers result.addAll(KubernetesCommonHelper.createInitContainerDecorators(clusterKind, name, initContainers, result)); result.addAll(KubernetesCommonHelper.createInitJobDecorators(clusterKind, name, jobs, result)); diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/IngressConfig.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/IngressConfig.java index d1ea33b6c830ae..7282b16b3b62e5 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/IngressConfig.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/IngressConfig.java @@ -1,5 +1,6 @@ package io.quarkus.kubernetes.deployment; +import java.util.Collections; import java.util.Map; import java.util.Optional; @@ -54,4 +55,7 @@ public class IngressConfig { @ConfigItem Map rules; + public Map getAnnotations() { + return Collections.unmodifiableMap(annotations); + } } 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 33feeca5700e61..861c45927b514d 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 @@ -643,4 +643,8 @@ public Map getInitTasks() { public InitTaskConfig getInitTaskDefaults() { return initTaskDefaults; } + + public IngressConfig getIngress() { + return ingress; + } } diff --git a/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KubernetesWithKindIngress.java b/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KubernetesWithKindIngress.java new file mode 100644 index 00000000000000..bd907a7a276d1e --- /dev/null +++ b/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KubernetesWithKindIngress.java @@ -0,0 +1,97 @@ +package io.quarkus.it.kubernetes; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.List; +import java.util.function.Predicate; + +import org.assertj.core.api.SoftAssertions; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.api.model.networking.v1.Ingress; +import io.fabric8.kubernetes.api.model.networking.v1.IngressRule; +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; + +public class KubernetesWithKindIngress { + + private static final String LOCALHOST = "localhost"; + @RegisterExtension + static final QuarkusProdModeTest config = new QuarkusProdModeTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class).addClasses(GreetingResource.class)) + .setApplicationName("kind-ingress") + .setApplicationVersion("0.1-SNAPSHOT") + .setLogFileName("k8s.log") + // Configuration provided by issue: https://github.com/quarkusio/quarkus/issues/42294 + .overrideConfigKey("quarkus.kubernetes.ingress.expose", "true") + .overrideConfigKey("quarkus.kubernetes.ingress.annotations.\"nginx.ingress.kubernetes.io/rewrite-target\"", "/$2") + .overrideConfigKey("quarkus.kubernetes.ingress.rules.1.host", LOCALHOST) + .overrideConfigKey("quarkus.kubernetes.ingress.rules.1.path", "/game(/|$)(.*)") + .overrideConfigKey("quarkus.kubernetes.ingress.rules.1.path-type", "ImplementationSpecific") + .overrideConfigKey("quarkus.kubernetes.ingress.ingress-class-name", "Nginx") + .setForcedDependencies(Arrays.asList( + Dependency.of("io.quarkus", "quarkus-kind", Version.getVersion()), + Dependency.of("io.quarkus", "quarkus-kubernetes", Version.getVersion()))); + private static final Logger log = LoggerFactory.getLogger(KubernetesWithKindIngress.class); + + @ProdBuildResults + private ProdModeTestResults prodModeTestResults; + + @Test + void shouldCreateKindResourcesWithIngressAnnotationsCorrectly() throws IOException { + final Path kubernetesFile = prodModeTestResults.getBuildDir().resolve("kubernetes").resolve("kind.yml"); + List kubernetesList = DeserializationUtil.deserializeAsList(kubernetesFile); + + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(kubernetesList).filteredOn(k -> k.getKind().equals("Ingress")) + .singleElement().isInstanceOfSatisfying(Ingress.class, ingress -> { + + softly.assertThat(ingress.getMetadata().getAnnotations()) + .anySatisfy((key, value) -> { + assertThat(key).isEqualTo("nginx.ingress.kubernetes.io/rewrite-target"); + assertThat(value).isEqualTo("/$2"); + }); + + softly.assertThat(ingress.getSpec()).satisfies(spec -> { + + softly.assertThat(spec.getIngressClassName()).isEqualTo("Nginx"); + + softly.assertThat(spec.getRules()).hasSize(2); + + softly.assertThat(spec.getRules()).filteredOn(byLocalhost()) + .singleElement() + .satisfies(rule -> { + + softly.assertThat(rule.getHttp().getPaths()).hasSize(1); + softly.assertThat(rule.getHttp().getPaths().get(0)).satisfies(path -> { + + softly.assertThat(path.getPathType()).isEqualTo("ImplementationSpecific"); + softly.assertThat(path.getPath()).isEqualTo("/game(/|$)(.*)"); + softly.assertThat(path.getBackend().getService().getPort().getName()) + .isEqualTo("http"); + softly.assertThat(path.getBackend().getService().getName()) + .isEqualTo("kind-ingress"); + + }); + }); + }); + }); + }); + } + + private static Predicate byLocalhost() { + return rule -> rule.getHost() != null && rule.getHost().equals(LOCALHOST); + } +} diff --git a/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KubernetesWithMinikubeIngress.java b/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KubernetesWithMinikubeIngress.java new file mode 100644 index 00000000000000..2ef8b913938b4b --- /dev/null +++ b/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KubernetesWithMinikubeIngress.java @@ -0,0 +1,97 @@ +package io.quarkus.it.kubernetes; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.List; +import java.util.function.Predicate; + +import org.assertj.core.api.SoftAssertions; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.api.model.networking.v1.Ingress; +import io.fabric8.kubernetes.api.model.networking.v1.IngressRule; +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; + +public class KubernetesWithMinikubeIngress { + + private static final String LOCALHOST = "localhost"; + @RegisterExtension + static final QuarkusProdModeTest config = new QuarkusProdModeTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class).addClasses(GreetingResource.class)) + .setApplicationName("kind-ingress") + .setApplicationVersion("0.1-SNAPSHOT") + .setLogFileName("k8s.log") + // Configuration provided by issue: https://github.com/quarkusio/quarkus/issues/42294 + .overrideConfigKey("quarkus.kubernetes.ingress.expose", "true") + .overrideConfigKey("quarkus.kubernetes.ingress.annotations.\"nginx.ingress.kubernetes.io/rewrite-target\"", "/$2") + .overrideConfigKey("quarkus.kubernetes.ingress.rules.1.host", LOCALHOST) + .overrideConfigKey("quarkus.kubernetes.ingress.rules.1.path", "/game(/|$)(.*)") + .overrideConfigKey("quarkus.kubernetes.ingress.rules.1.path-type", "ImplementationSpecific") + .overrideConfigKey("quarkus.kubernetes.ingress.ingress-class-name", "Nginx") + .setForcedDependencies(Arrays.asList( + Dependency.of("io.quarkus", "quarkus-minikube", Version.getVersion()), + Dependency.of("io.quarkus", "quarkus-kubernetes", Version.getVersion()))); + private static final Logger log = LoggerFactory.getLogger(KubernetesWithMinikubeIngress.class); + + @ProdBuildResults + private ProdModeTestResults prodModeTestResults; + + @Test + void shouldCreateKindResourcesWithIngressAnnotationsCorrectly() throws IOException { + final Path kubernetesFile = prodModeTestResults.getBuildDir().resolve("kubernetes").resolve("minikube.yml"); + List kubernetesList = DeserializationUtil.deserializeAsList(kubernetesFile); + + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(kubernetesList).filteredOn(k -> k.getKind().equals("Ingress")) + .singleElement().isInstanceOfSatisfying(Ingress.class, ingress -> { + + softly.assertThat(ingress.getMetadata().getAnnotations()) + .anySatisfy((key, value) -> { + assertThat(key).isEqualTo("nginx.ingress.kubernetes.io/rewrite-target"); + assertThat(value).isEqualTo("/$2"); + }); + + softly.assertThat(ingress.getSpec()).satisfies(spec -> { + + softly.assertThat(spec.getIngressClassName()).isEqualTo("Nginx"); + + softly.assertThat(spec.getRules()).hasSize(2); + + softly.assertThat(spec.getRules()).filteredOn(byLocalhost()) + .singleElement() + .satisfies(rule -> { + + softly.assertThat(rule.getHttp().getPaths()).hasSize(1); + softly.assertThat(rule.getHttp().getPaths().get(0)).satisfies(path -> { + + softly.assertThat(path.getPathType()).isEqualTo("ImplementationSpecific"); + softly.assertThat(path.getPath()).isEqualTo("/game(/|$)(.*)"); + softly.assertThat(path.getBackend().getService().getPort().getName()) + .isEqualTo("http"); + softly.assertThat(path.getBackend().getService().getName()) + .isEqualTo("kind-ingress"); + + }); + }); + }); + }); + }); + } + + private static Predicate byLocalhost() { + return rule -> rule.getHost() != null && rule.getHost().equals(LOCALHOST); + } +}