From 1caf94a329ab028244f31d25c0d0775d6c4e8ef6 Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Tue, 9 May 2023 21:32:29 +0200 Subject: [PATCH] fix: ExposedApp tests should now properly run (#572) Get things ready to run in native mode as well, now building samples natively. --- .../workflows/build-for-quarkus-version.yml | 2 +- .../operatorsdk/runtime/Version.java | 2 +- pom.xml | 48 +++++++++++++ samples/exposedapp/pom.xml | 9 --- .../java/io/halkyon/ExposedAppReconciler.java | 6 +- .../main/java/io/halkyon/ExposedAppSpec.java | 10 +++ .../java/io/halkyon/ExposedAppStatus.java | 4 +- .../java/io/halkyon/IngressDependent.java | 29 ++++++-- samples/exposedapp/src/main/resources/app.yml | 2 +- .../src/main/resources/application.properties | 4 +- .../io/halkyon/ExposedAppReconcilerTest.java | 72 ++++++------------- .../halkyon/NativeExposedAppReconcilerIT.java | 11 +++ 12 files changed, 126 insertions(+), 73 deletions(-) create mode 100644 samples/exposedapp/src/test/java/io/halkyon/NativeExposedAppReconcilerIT.java diff --git a/.github/workflows/build-for-quarkus-version.yml b/.github/workflows/build-for-quarkus-version.yml index dc1c3ea6..01447462 100644 --- a/.github/workflows/build-for-quarkus-version.yml +++ b/.github/workflows/build-for-quarkus-version.yml @@ -44,7 +44,7 @@ jobs: run: mvn -B formatter:validate install --file pom.xml - name: Build with Maven (Native) - run: mvn -B formatter:validate install -Dnative --file pom.xml + run: mvn -B formatter:validate install -Dnative --file pom.xml -pl '!docs' - name: Kubernetes KinD Cluster uses: container-tools/kind-action@v2 diff --git a/core/runtime/src/main/java/io/quarkiverse/operatorsdk/runtime/Version.java b/core/runtime/src/main/java/io/quarkiverse/operatorsdk/runtime/Version.java index c8fb00a7..3ec86962 100644 --- a/core/runtime/src/main/java/io/quarkiverse/operatorsdk/runtime/Version.java +++ b/core/runtime/src/main/java/io/quarkiverse/operatorsdk/runtime/Version.java @@ -63,7 +63,7 @@ public static Version loadFromProperties() { log.warnf("Couldn't load extension version information: {0}", e.getMessage()); } } else { - log.warn("Couldn't find version.properties file. Default version information will be used."); + log.warn("Couldn't find extension-version.properties file. Default version information will be used."); } Date builtTime; diff --git a/pom.xml b/pom.xml index cf5cfcd5..53fd1782 100644 --- a/pom.xml +++ b/pom.xml @@ -85,5 +85,53 @@ samples + + native-image + + + native + + + + + + maven-surefire-plugin + + ${native.surefire.skip} + + org.jboss.logmanager.LogManager + + ${maven.home} + + + + + maven-failsafe-plugin + + + + integration-test + verify + + + + + ${project.build.directory}/${project.build.finalName}-runner + + org.jboss.logmanager.LogManager + + ${maven.home} + + + + + + + + + native + true + + diff --git a/samples/exposedapp/pom.xml b/samples/exposedapp/pom.xml index 552fabb7..86ea6798 100644 --- a/samples/exposedapp/pom.xml +++ b/samples/exposedapp/pom.xml @@ -75,13 +75,4 @@ - - - - native - - native - - - \ No newline at end of file diff --git a/samples/exposedapp/src/main/java/io/halkyon/ExposedAppReconciler.java b/samples/exposedapp/src/main/java/io/halkyon/ExposedAppReconciler.java index ae9330b4..52ad9372 100644 --- a/samples/exposedapp/src/main/java/io/halkyon/ExposedAppReconciler.java +++ b/samples/exposedapp/src/main/java/io/halkyon/ExposedAppReconciler.java @@ -48,11 +48,11 @@ public UpdateControl reconcile(ExposedApp exposedApp, Context noUpdate().rescheduleAfter(duration); } diff --git a/samples/exposedapp/src/main/java/io/halkyon/ExposedAppSpec.java b/samples/exposedapp/src/main/java/io/halkyon/ExposedAppSpec.java index f6d7b851..6979adc9 100644 --- a/samples/exposedapp/src/main/java/io/halkyon/ExposedAppSpec.java +++ b/samples/exposedapp/src/main/java/io/halkyon/ExposedAppSpec.java @@ -9,6 +9,16 @@ public class ExposedAppSpec { private String imageRef; private Map env; + private String endpoint; + + public String getEndpoint() { + return endpoint; + } + + public void setEndpoint(String endpoint) { + this.endpoint = endpoint; + } + public String getImageRef() { return imageRef; } diff --git a/samples/exposedapp/src/main/java/io/halkyon/ExposedAppStatus.java b/samples/exposedapp/src/main/java/io/halkyon/ExposedAppStatus.java index 839e49e2..f1e0e064 100644 --- a/samples/exposedapp/src/main/java/io/halkyon/ExposedAppStatus.java +++ b/samples/exposedapp/src/main/java/io/halkyon/ExposedAppStatus.java @@ -13,9 +13,9 @@ public ExposedAppStatus() { message = "processing"; } - public ExposedAppStatus(String hostname) { + public ExposedAppStatus(String hostname, String endpoint) { this.message = "exposed"; - this.host = hostname; + this.host = endpoint != null && !endpoint.isBlank() ? hostname + '/' + endpoint : hostname; ready = true; waitTime = System.currentTimeMillis() - waitTime; } diff --git a/samples/exposedapp/src/main/java/io/halkyon/IngressDependent.java b/samples/exposedapp/src/main/java/io/halkyon/IngressDependent.java index 8a3dbcfd..9794f53a 100644 --- a/samples/exposedapp/src/main/java/io/halkyon/IngressDependent.java +++ b/samples/exposedapp/src/main/java/io/halkyon/IngressDependent.java @@ -5,30 +5,41 @@ import java.util.Map; +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.fabric8.kubernetes.api.model.networking.v1.Ingress; import io.fabric8.kubernetes.api.model.networking.v1.IngressBuilder; import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; +import io.javaoperatorsdk.operator.processing.dependent.Matcher; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.GenericKubernetesResourceMatcher; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.ResourceUpdatePreProcessor; import io.javaoperatorsdk.operator.processing.dependent.workflow.Condition; public class IngressDependent extends CRUDKubernetesDependentResource implements - Condition { + Condition, Matcher, ResourceUpdatePreProcessor { public IngressDependent() { super(Ingress.class); } + @Override + public Result match(Ingress actualResource, ExposedApp primary, + Context context) { + return GenericKubernetesResourceMatcher.match(this, actualResource, primary, context, true); + } + @Override @SuppressWarnings("unchecked") public Ingress desired(ExposedApp exposedApp, Context context) { final var labels = (Map) context.managedDependentResourceContext() .getMandatory(LABELS_CONTEXT_KEY, Map.class); final var metadata = createMetadata(exposedApp, labels); - metadata.setAnnotations(Map.of( - "nginx.ingress.kubernetes.io/rewrite-target", "/", - "kubernetes.io/ingress.class", "nginx")); - + /* + * metadata.setAnnotations(Map.of( + * "nginx.ingress.kubernetes.io/rewrite-target", "/", + * "kubernetes.io/ingress.class", "nginx")); + */ return new IngressBuilder() .withMetadata(metadata) .withNewSpec() @@ -77,4 +88,12 @@ public boolean isMet(DependentResource dependentResource, return false; }).orElse(false); } + + @Override + public Ingress replaceSpecOnActual(Ingress actual, Ingress desired, Context context) { + final var metadata = new ObjectMetaBuilder(desired.getMetadata()).build(); + actual.setMetadata(metadata); + actual.setSpec(desired.getSpec()); + return actual; + } } diff --git a/samples/exposedapp/src/main/resources/app.yml b/samples/exposedapp/src/main/resources/app.yml index 5a4b546e..e0a9a81b 100644 --- a/samples/exposedapp/src/main/resources/app.yml +++ b/samples/exposedapp/src/main/resources/app.yml @@ -3,4 +3,4 @@ kind: ExposedApp metadata: name: hello-quarkus spec: - imageRef: 'claprun/hello:1.0.0-SNAPSHOT' \ No newline at end of file + imageRef: 'quay.io/metacosm/hello:1.0.0-SNAPSHOT' \ No newline at end of file diff --git a/samples/exposedapp/src/main/resources/application.properties b/samples/exposedapp/src/main/resources/application.properties index 07e12c8e..236ddb7e 100644 --- a/samples/exposedapp/src/main/resources/application.properties +++ b/samples/exposedapp/src/main/resources/application.properties @@ -4,4 +4,6 @@ # set to true to automatically apply CRDs to the cluster when they get regenerated quarkus.operator-sdk.crd.apply=true quarkus.kubernetes-client.devservices.override-kubeconfig=true -%test.quarkus.operator-sdk.close-client-on-stop=false \ No newline at end of file +%test.quarkus.operator-sdk.close-client-on-stop=false +%test.quarkus.operator-sdk.start-operator=true +quarkus.kubernetes-client.devservices.flavor=k3s \ No newline at end of file diff --git a/samples/exposedapp/src/test/java/io/halkyon/ExposedAppReconcilerTest.java b/samples/exposedapp/src/test/java/io/halkyon/ExposedAppReconcilerTest.java index 65a65a1f..e317377c 100644 --- a/samples/exposedapp/src/test/java/io/halkyon/ExposedAppReconcilerTest.java +++ b/samples/exposedapp/src/test/java/io/halkyon/ExposedAppReconcilerTest.java @@ -7,66 +7,21 @@ import java.util.concurrent.TimeUnit; -import jakarta.inject.Inject; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.fabric8.kubernetes.client.KubernetesClient; -import io.javaoperatorsdk.operator.Operator; +import io.quarkus.kubernetes.client.runtime.KubernetesClientUtils; import io.quarkus.test.junit.QuarkusTest; @QuarkusTest class ExposedAppReconcilerTest { public static final String TEST_APP = "test-app"; - @Inject - KubernetesClient client; - - @Inject - Operator operator; - - @BeforeEach - void startOperator() { - operator.start(); - } - @AfterEach - void stopOperator() { - operator.stop(); - } + protected final KubernetesClient client = KubernetesClientUtils.createClient(); @Test - @Order(2) - void deleteShouldWork() { - final var app = new ExposedApp(); - final var metadata = new ObjectMetaBuilder() - .withName(TEST_APP) - .withNamespace(client.getNamespace()) - .build(); - app.setMetadata(metadata); - app.getSpec().setImageRef("group/imageName:tag"); - - client.resource(app).delete(); - - await().atMost(30, TimeUnit.SECONDS).untilAsserted(() -> { - assertThat(client.apps().deployments() - .inNamespace(metadata.getNamespace()) - .withName(metadata.getName()).get(), nullValue()); - assertThat(client.services() - .inNamespace(metadata.getNamespace()) - .withName(metadata.getName()).get(), nullValue()); - assertThat(client.network().v1().ingresses() - .inNamespace(metadata.getNamespace()) - .withName(metadata.getName()).get(), nullValue()); - }); - } - - @Test - @Order(1) void reconcileShouldWork() { final var app = new ExposedApp(); final var metadata = new ObjectMetaBuilder() @@ -100,9 +55,12 @@ void reconcileShouldWork() { // check that the ingress is created final var ingress = client.network().v1().ingresses() .inNamespace(metadata.getNamespace()).withName(metadata.getName()).get(); - final var annotations = ingress.getMetadata().getAnnotations(); - assertThat(annotations.size(), is(2)); - assertThat(annotations.get("kubernetes.io/ingress.class"), is("nginx")); + // not using nginx controller on k3s + /* + * final var annotations = ingress.getMetadata().getAnnotations(); + * assertThat(annotations.size(), is(2)); + * assertThat(annotations.get("kubernetes.io/ingress.class"), is("nginx")); + */ final var rules = ingress.getSpec().getRules(); assertThat(rules.size(), is(1)); final var paths = rules.get(0).getHttp().getPaths(); @@ -114,5 +72,19 @@ void reconcileShouldWork() { assertThat(serviceBackend.getName(), is(service.getMetadata().getName())); assertThat(serviceBackend.getPort().getNumber(), is(port)); }); + + client.resource(app).delete(); + + await().atMost(30, TimeUnit.SECONDS).untilAsserted(() -> { + assertThat(client.apps().deployments() + .inNamespace(metadata.getNamespace()) + .withName(metadata.getName()).get(), nullValue()); + assertThat(client.services() + .inNamespace(metadata.getNamespace()) + .withName(metadata.getName()).get(), nullValue()); + assertThat(client.network().v1().ingresses() + .inNamespace(metadata.getNamespace()) + .withName(metadata.getName()).get(), nullValue()); + }); } } diff --git a/samples/exposedapp/src/test/java/io/halkyon/NativeExposedAppReconcilerIT.java b/samples/exposedapp/src/test/java/io/halkyon/NativeExposedAppReconcilerIT.java new file mode 100644 index 00000000..7d2aef47 --- /dev/null +++ b/samples/exposedapp/src/test/java/io/halkyon/NativeExposedAppReconcilerIT.java @@ -0,0 +1,11 @@ +package io.halkyon; + +import org.junit.jupiter.api.Disabled; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +@QuarkusIntegrationTest +@Disabled("Currently not possible to inject the dev service-provided k8s client in native app") +public class NativeExposedAppReconcilerIT extends ExposedAppReconcilerTest { + +}