Skip to content

Commit

Permalink
fix: ExposedApp tests should now properly run (#572)
Browse files Browse the repository at this point in the history
Get things ready to run in native mode as well, now building samples natively.
  • Loading branch information
metacosm authored May 9, 2023
1 parent 8db46ce commit 1caf94a
Show file tree
Hide file tree
Showing 12 changed files with 126 additions and 73 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build-for-quarkus-version.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
48 changes: 48 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -85,5 +85,53 @@
<module>samples</module>
</modules>
</profile>
<profile>
<id>native-image</id>
<activation>
<property>
<name>native</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skipTests>${native.surefire.skip}</skipTests>
<systemPropertyVariables>
<java.util.logging.manager>org.jboss.logmanager.LogManager
</java.util.logging.manager>
<maven.home>${maven.home}</maven.home>
</systemPropertyVariables>
</configuration>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
<configuration>
<systemPropertyVariables>
<native.image.path>
${project.build.directory}/${project.build.finalName}-runner
</native.image.path>
<java.util.logging.manager>org.jboss.logmanager.LogManager
</java.util.logging.manager>
<maven.home>${maven.home}</maven.home>
</systemPropertyVariables>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<properties>
<quarkus.package.type>native</quarkus.package.type>
<skipDocs>true</skipDocs>
</properties>
</profile>
</profiles>
</project>
9 changes: 0 additions & 9 deletions samples/exposedapp/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,4 @@
</plugin>
</plugins>
</build>

<profiles>
<profile>
<id>native</id>
<properties>
<quarkus.package.type>native</quarkus.package.type>
</properties>
</profile>
</profiles>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,11 @@ public UpdateControl<ExposedApp> reconcile(ExposedApp exposedApp, Context<Expose
if (wrs.allDependentResourcesReady()) {
final var url = IngressDependent.getExposedURL(
context.getSecondaryResource(Ingress.class).orElseThrow());
log.info("App {} is exposed and ready to be used at {}", name, url);
exposedApp.setStatus(new ExposedAppStatus(url));
exposedApp.setStatus(new ExposedAppStatus(url, exposedApp.getSpec().getEndpoint()));
log.info("App {} is exposed and ready to be used at {}", name, exposedApp.getStatus().getHost());
return UpdateControl.updateStatus(exposedApp);
} else {
final var duration = Duration.ofSeconds(5);
final var duration = Duration.ofSeconds(1);
log.info("App {} is not ready yet, rescheduling reconciliation after {}s", name, duration.toSeconds());
return UpdateControl.<ExposedApp> noUpdate().rescheduleAfter(duration);
}
Expand Down
10 changes: 10 additions & 0 deletions samples/exposedapp/src/main/java/io/halkyon/ExposedAppSpec.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@ public class ExposedAppSpec {
private String imageRef;
private Map<String, String> env;

private String endpoint;

public String getEndpoint() {
return endpoint;
}

public void setEndpoint(String endpoint) {
this.endpoint = endpoint;
}

public String getImageRef() {
return imageRef;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
29 changes: 24 additions & 5 deletions samples/exposedapp/src/main/java/io/halkyon/IngressDependent.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<Ingress, ExposedApp> implements
Condition<Ingress, ExposedApp> {
Condition<Ingress, ExposedApp>, Matcher<Ingress, ExposedApp>, ResourceUpdatePreProcessor<Ingress> {

public IngressDependent() {
super(Ingress.class);
}

@Override
public Result<Ingress> match(Ingress actualResource, ExposedApp primary,
Context<ExposedApp> context) {
return GenericKubernetesResourceMatcher.match(this, actualResource, primary, context, true);
}

@Override
@SuppressWarnings("unchecked")
public Ingress desired(ExposedApp exposedApp, Context context) {
final var labels = (Map<String, String>) 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()
Expand Down Expand Up @@ -77,4 +88,12 @@ public boolean isMet(DependentResource<Ingress, ExposedApp> 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;
}
}
2 changes: 1 addition & 1 deletion samples/exposedapp/src/main/resources/app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ kind: ExposedApp
metadata:
name: hello-quarkus
spec:
imageRef: 'claprun/hello:1.0.0-SNAPSHOT'
imageRef: 'quay.io/metacosm/hello:1.0.0-SNAPSHOT'
4 changes: 3 additions & 1 deletion samples/exposedapp/src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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
%test.quarkus.operator-sdk.close-client-on-stop=false
%test.quarkus.operator-sdk.start-operator=true
quarkus.kubernetes-client.devservices.flavor=k3s
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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();
Expand All @@ -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());
});
}
}
Original file line number Diff line number Diff line change
@@ -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 {

}

0 comments on commit 1caf94a

Please sign in to comment.