From 818837f486b43675f1d155852fd249442e320b49 Mon Sep 17 00:00:00 2001 From: fedinskiy Date: Tue, 31 Jan 2023 09:08:50 +0100 Subject: [PATCH] Cover Vert.X-specific metrics. (#1019) Add new module, which uses Vert.X-based HTTP server Verify, that it works, and that it creates all required metrics Required for https://issues.redhat.com/browse/QUARKUS-2829 --- README.md | 7 + http/vertx/pom.xml | 38 ++++++ .../main/java/io/quarkus/ts/vertx/Hello.java | 14 ++ .../io/quarkus/ts/vertx/HelloResource.java | 33 +++++ .../src/main/resources/application.properties | 6 + .../vertx/src/main/resources/message.template | 1 + .../io/quarkus/ts/vertx/AbstractVertxIT.java | 121 ++++++++++++++++++ .../io/quarkus/ts/vertx/LocalVertxIT.java | 18 +++ ...ngExtensionDockerBuildStrategyVertxIT.java | 21 +++ .../vertx/OpenShiftUsingExtensionVertxIT.java | 21 +++ .../io/quarkus/ts/vertx/OpenShiftVertxIT.java | 17 +++ ...onDockerBuildStrategyOpenShiftVertxIT.java | 22 ++++ .../ServerlessExtensionOpenShiftVertxIT.java | 22 ++++ pom.xml | 1 + 14 files changed, 342 insertions(+) create mode 100644 http/vertx/pom.xml create mode 100644 http/vertx/src/main/java/io/quarkus/ts/vertx/Hello.java create mode 100644 http/vertx/src/main/java/io/quarkus/ts/vertx/HelloResource.java create mode 100644 http/vertx/src/main/resources/application.properties create mode 100644 http/vertx/src/main/resources/message.template create mode 100644 http/vertx/src/test/java/io/quarkus/ts/vertx/AbstractVertxIT.java create mode 100644 http/vertx/src/test/java/io/quarkus/ts/vertx/LocalVertxIT.java create mode 100644 http/vertx/src/test/java/io/quarkus/ts/vertx/OpenShiftUsingExtensionDockerBuildStrategyVertxIT.java create mode 100644 http/vertx/src/test/java/io/quarkus/ts/vertx/OpenShiftUsingExtensionVertxIT.java create mode 100644 http/vertx/src/test/java/io/quarkus/ts/vertx/OpenShiftVertxIT.java create mode 100644 http/vertx/src/test/java/io/quarkus/ts/vertx/ServerlessExtensionDockerBuildStrategyOpenShiftVertxIT.java create mode 100644 http/vertx/src/test/java/io/quarkus/ts/vertx/ServerlessExtensionOpenShiftVertxIT.java diff --git a/README.md b/README.md index 613e87e46..cf681d968 100644 --- a/README.md +++ b/README.md @@ -349,6 +349,13 @@ Exclusions: XML test. Reason: https://quarkus.io/blog/resteasy-reactive/#what-ja Verifies HTTP endpoints validation using `quarkus-hibernate-validator` works correctly in Resteasy Classic and Resteasy Reactive. This module will setup a simple endpoint and will validate the right message format is set when there are validation errors. +### `http/vertx` +Verifies that you can deploy a simple Vert.X-based HTTP endpoint to OpenShift, access it and retrieve metrics for it. +It also verifies multiple deployment strategies like: +- Serverless +- Using OpenShift quarkus extension +- Using OpenShift quarkus extension and Docker Build strategy + #### Additions * *@Deprecated* annotation has been added for test regression purposes to ensure `java.lang` annotations are allowed for resources * Resource with multipart body support, provided parts are text, image and binary data, charset checked with `us-ascii` and `utf-8` diff --git a/http/vertx/pom.xml b/http/vertx/pom.xml new file mode 100644 index 000000000..1d2addb2f --- /dev/null +++ b/http/vertx/pom.xml @@ -0,0 +1,38 @@ + + + 4.0.0 + + io.quarkus.ts.qe + parent + 1.0.0-SNAPSHOT + ../.. + + vertx + jar + Quarkus QE TS: HTTP: Vert.X + + + io.quarkus + quarkus-resteasy-reactive-jackson + + + io.quarkus + quarkus-vertx + + + io.quarkus + quarkus-micrometer-registry-prometheus + + + + + deploy-to-openshift-using-extension + + + io.quarkus + quarkus-openshift + + + + + diff --git a/http/vertx/src/main/java/io/quarkus/ts/vertx/Hello.java b/http/vertx/src/main/java/io/quarkus/ts/vertx/Hello.java new file mode 100644 index 000000000..8560f7554 --- /dev/null +++ b/http/vertx/src/main/java/io/quarkus/ts/vertx/Hello.java @@ -0,0 +1,14 @@ +package io.quarkus.ts.vertx; + +public class Hello { + + private final String content; + + public Hello(String content) { + this.content = content; + } + + public String getContent() { + return content; + } +} diff --git a/http/vertx/src/main/java/io/quarkus/ts/vertx/HelloResource.java b/http/vertx/src/main/java/io/quarkus/ts/vertx/HelloResource.java new file mode 100644 index 000000000..f65db94cc --- /dev/null +++ b/http/vertx/src/main/java/io/quarkus/ts/vertx/HelloResource.java @@ -0,0 +1,33 @@ +package io.quarkus.ts.vertx; + +import javax.inject.Inject; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; + +import io.smallrye.mutiny.Uni; +import io.vertx.mutiny.core.Vertx; +import io.vertx.mutiny.core.buffer.Buffer; + +@Path("/hello") +public class HelloResource { + private final Vertx vertx; + + @Inject + public HelloResource(Vertx vertx) { + this.vertx = vertx; + } + + @GET + @Produces(MediaType.APPLICATION_JSON) + public Uni getMessage(@QueryParam("name") @DefaultValue("World") String name) { + return vertx.fileSystem() + .readFile("message.template") + .map(Buffer::toString) + .map(string -> String.format(string, name).trim()) + .map(Hello::new); + } +} diff --git a/http/vertx/src/main/resources/application.properties b/http/vertx/src/main/resources/application.properties new file mode 100644 index 000000000..d8b77f2fe --- /dev/null +++ b/http/vertx/src/main/resources/application.properties @@ -0,0 +1,6 @@ +quarkus.application.name=test-http +quarkus.native.resources.includes=message.template +%ServerlessExtensionOpenShiftHttpMinimumIT.quarkus.kubernetes.deployment-target=knative +%ServerlessExtensionOpenShiftHttpMinimumIT.quarkus.container-image.registry=image-registry.openshift-image-registry.svc:5000 +%ServerlessExtensionDockerBuildStrategyOpenShiftHttpMinimumIT.quarkus.kubernetes.deployment-target=knative +%ServerlessExtensionDockerBuildStrategyOpenShiftHttpMinimumIT.quarkus.container-image.registry=image-registry.openshift-image-registry.svc:5000 diff --git a/http/vertx/src/main/resources/message.template b/http/vertx/src/main/resources/message.template new file mode 100644 index 000000000..6807eef81 --- /dev/null +++ b/http/vertx/src/main/resources/message.template @@ -0,0 +1 @@ +Hello, %s! diff --git a/http/vertx/src/test/java/io/quarkus/ts/vertx/AbstractVertxIT.java b/http/vertx/src/test/java/io/quarkus/ts/vertx/AbstractVertxIT.java new file mode 100644 index 000000000..7bc567a2f --- /dev/null +++ b/http/vertx/src/test/java/io/quarkus/ts/vertx/AbstractVertxIT.java @@ -0,0 +1,121 @@ +package io.quarkus.ts.vertx; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.testcontainers.shaded.org.hamcrest.MatcherAssert.assertThat; +import static org.testcontainers.shaded.org.hamcrest.Matchers.containsString; + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.Test; + +import io.restassured.response.Response; +import io.restassured.specification.RequestSpecification; + +public abstract class AbstractVertxIT { + + @Test + public void httpServerAndMetrics() { + requests().get("/hello").then() + .statusCode(HttpStatus.SC_OK) + .body("content", is("Hello, World!")); + + Response response = requests().get("/q/metrics"); + assertEquals(HttpStatus.SC_OK, response.statusCode()); + assertThat(response.getContentType(), containsString("application/openmetrics-text")); + String body = response.body().asString(); + Map metrics = parseMetrics(body); + assertTrue(metrics.containsKey("worker_pool_active")); + assertTrue(metrics.containsKey("worker_pool_completed_total")); + assertTrue(metrics.containsKey("worker_pool_queue_size")); + } + + @Test + public void httpServerParsing() { + requests().get("/hello?name=you").then().statusCode(HttpStatus.SC_OK).body("content", is("Hello, you!")); + } + + public abstract RequestSpecification requests(); + + private Map parseMetrics(String body) { + Map metrics = new HashMap<>(128); + Arrays.stream(body.split("\n")) + .filter(line -> !line.startsWith("#")) + .map(Metric::new) + .forEach(metric -> metrics.put(metric.name, metric)); + return metrics; + } + + private class Metric { + private final String value; + private final String name; + + /** + * + * @param source metric from the file, eg: + * worker_pool_queue_size{pool_name="vert.x-internal-blocking",pool_type="worker"} 0.0 + * content in curly brackets is ignored (for now) + * since we do not care about values, we store them as strings, and ignore duplicated keys. + */ + public Metric(String source) { + final int DEFAULT = -1; + int space = DEFAULT; + int closing = DEFAULT; + int opening = DEFAULT; + byte[] bytes = source.getBytes(StandardCharsets.UTF_8); + for (int i = bytes.length - 1; i >= 0; i--) { + byte current = bytes[i]; + if (current == ' ' && space == DEFAULT) { + space = i; + } + if (current == '}' && closing == DEFAULT) { + closing = i; + } + if (current == '{' && opening == DEFAULT) { + opening = i; + } + } + String key; + if (space > 0) { + value = source.substring(space); + key = source.substring(0, space); + } else { + throw new IllegalArgumentException("Metric " + source + " doesn't contain a value"); + } + if (closing < space && opening < closing && opening > 0) { + name = source.substring(0, opening); + } else { + name = key; + } + } + + public String getValue() { + return value; + } + + public String getName() { + return name; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + Metric metric = (Metric) o; + return value.equals(metric.value) && name.equals(metric.name); + } + + @Override + public int hashCode() { + return Objects.hash(value, name); + } + } +} diff --git a/http/vertx/src/test/java/io/quarkus/ts/vertx/LocalVertxIT.java b/http/vertx/src/test/java/io/quarkus/ts/vertx/LocalVertxIT.java new file mode 100644 index 000000000..f69279815 --- /dev/null +++ b/http/vertx/src/test/java/io/quarkus/ts/vertx/LocalVertxIT.java @@ -0,0 +1,18 @@ +package io.quarkus.ts.vertx; + +import io.quarkus.test.bootstrap.RestService; +import io.quarkus.test.scenarios.QuarkusScenario; +import io.quarkus.test.services.QuarkusApplication; +import io.restassured.specification.RequestSpecification; + +@QuarkusScenario +public class LocalVertxIT extends AbstractVertxIT { + + @QuarkusApplication + static RestService app = new RestService(); + + @Override + public RequestSpecification requests() { + return app.given(); + } +} diff --git a/http/vertx/src/test/java/io/quarkus/ts/vertx/OpenShiftUsingExtensionDockerBuildStrategyVertxIT.java b/http/vertx/src/test/java/io/quarkus/ts/vertx/OpenShiftUsingExtensionDockerBuildStrategyVertxIT.java new file mode 100644 index 000000000..798fa7291 --- /dev/null +++ b/http/vertx/src/test/java/io/quarkus/ts/vertx/OpenShiftUsingExtensionDockerBuildStrategyVertxIT.java @@ -0,0 +1,21 @@ +package io.quarkus.ts.vertx; + +import org.junit.jupiter.api.Tag; + +import io.quarkus.test.bootstrap.RestService; +import io.quarkus.test.scenarios.OpenShiftDeploymentStrategy; +import io.quarkus.test.scenarios.OpenShiftScenario; +import io.quarkus.test.services.QuarkusApplication; +import io.restassured.specification.RequestSpecification; + +@Tag("use-quarkus-openshift-extension") +@OpenShiftScenario(deployment = OpenShiftDeploymentStrategy.UsingOpenShiftExtensionAndDockerBuildStrategy) +public class OpenShiftUsingExtensionDockerBuildStrategyVertxIT extends AbstractVertxIT { + @QuarkusApplication + static RestService app = new RestService(); + + @Override + public RequestSpecification requests() { + return app.given(); + } +} diff --git a/http/vertx/src/test/java/io/quarkus/ts/vertx/OpenShiftUsingExtensionVertxIT.java b/http/vertx/src/test/java/io/quarkus/ts/vertx/OpenShiftUsingExtensionVertxIT.java new file mode 100644 index 000000000..627a99211 --- /dev/null +++ b/http/vertx/src/test/java/io/quarkus/ts/vertx/OpenShiftUsingExtensionVertxIT.java @@ -0,0 +1,21 @@ +package io.quarkus.ts.vertx; + +import org.junit.jupiter.api.Tag; + +import io.quarkus.test.bootstrap.RestService; +import io.quarkus.test.scenarios.OpenShiftDeploymentStrategy; +import io.quarkus.test.scenarios.OpenShiftScenario; +import io.quarkus.test.services.QuarkusApplication; +import io.restassured.specification.RequestSpecification; + +@Tag("use-quarkus-openshift-extension") +@OpenShiftScenario(deployment = OpenShiftDeploymentStrategy.UsingOpenShiftExtension) +public class OpenShiftUsingExtensionVertxIT extends AbstractVertxIT { + @QuarkusApplication + static RestService app = new RestService(); + + @Override + public RequestSpecification requests() { + return app.given(); + } +} diff --git a/http/vertx/src/test/java/io/quarkus/ts/vertx/OpenShiftVertxIT.java b/http/vertx/src/test/java/io/quarkus/ts/vertx/OpenShiftVertxIT.java new file mode 100644 index 000000000..5206d96a2 --- /dev/null +++ b/http/vertx/src/test/java/io/quarkus/ts/vertx/OpenShiftVertxIT.java @@ -0,0 +1,17 @@ +package io.quarkus.ts.vertx; + +import io.quarkus.test.bootstrap.RestService; +import io.quarkus.test.scenarios.OpenShiftScenario; +import io.quarkus.test.services.QuarkusApplication; +import io.restassured.specification.RequestSpecification; + +@OpenShiftScenario +public class OpenShiftVertxIT extends AbstractVertxIT { + @QuarkusApplication + static RestService app = new RestService(); + + @Override + public RequestSpecification requests() { + return app.given(); + } +} diff --git a/http/vertx/src/test/java/io/quarkus/ts/vertx/ServerlessExtensionDockerBuildStrategyOpenShiftVertxIT.java b/http/vertx/src/test/java/io/quarkus/ts/vertx/ServerlessExtensionDockerBuildStrategyOpenShiftVertxIT.java new file mode 100644 index 000000000..6da0b51c2 --- /dev/null +++ b/http/vertx/src/test/java/io/quarkus/ts/vertx/ServerlessExtensionDockerBuildStrategyOpenShiftVertxIT.java @@ -0,0 +1,22 @@ +package io.quarkus.ts.vertx; + +import org.junit.jupiter.api.Tag; + +import io.quarkus.test.bootstrap.RestService; +import io.quarkus.test.scenarios.OpenShiftDeploymentStrategy; +import io.quarkus.test.scenarios.OpenShiftScenario; +import io.quarkus.test.services.QuarkusApplication; +import io.restassured.specification.RequestSpecification; + +@Tag("use-quarkus-openshift-extension") +@Tag("serverless") +@OpenShiftScenario(deployment = OpenShiftDeploymentStrategy.UsingOpenShiftExtensionAndDockerBuildStrategy) +public class ServerlessExtensionDockerBuildStrategyOpenShiftVertxIT extends AbstractVertxIT { + @QuarkusApplication + static RestService app = new RestService(); + + @Override + public RequestSpecification requests() { + return app.given().relaxedHTTPSValidation(); + } +} diff --git a/http/vertx/src/test/java/io/quarkus/ts/vertx/ServerlessExtensionOpenShiftVertxIT.java b/http/vertx/src/test/java/io/quarkus/ts/vertx/ServerlessExtensionOpenShiftVertxIT.java new file mode 100644 index 000000000..fdb7acd7b --- /dev/null +++ b/http/vertx/src/test/java/io/quarkus/ts/vertx/ServerlessExtensionOpenShiftVertxIT.java @@ -0,0 +1,22 @@ +package io.quarkus.ts.vertx; + +import org.junit.jupiter.api.Tag; + +import io.quarkus.test.bootstrap.RestService; +import io.quarkus.test.scenarios.OpenShiftDeploymentStrategy; +import io.quarkus.test.scenarios.OpenShiftScenario; +import io.quarkus.test.services.QuarkusApplication; +import io.restassured.specification.RequestSpecification; + +@Tag("use-quarkus-openshift-extension") +@Tag("serverless") +@OpenShiftScenario(deployment = OpenShiftDeploymentStrategy.UsingOpenShiftExtension) +public class ServerlessExtensionOpenShiftVertxIT extends AbstractVertxIT { + @QuarkusApplication + static RestService app = new RestService(); + + @Override + public RequestSpecification requests() { + return app.given().relaxedHTTPSValidation(); + } +} diff --git a/pom.xml b/pom.xml index e5f2b88b2..660e30f29 100644 --- a/pom.xml +++ b/pom.xml @@ -456,6 +456,7 @@ http/hibernate-validator http/graphql http/graphql-telemetry + http/vertx