Skip to content

Commit

Permalink
Cover Vert.X-specific metrics.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
fedinskiy committed Jan 30, 2023
1 parent 58519ac commit 17598d8
Show file tree
Hide file tree
Showing 14 changed files with 342 additions and 0 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
38 changes: 38 additions & 0 deletions http/vertx/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.quarkus.ts.qe</groupId>
<artifactId>parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../..</relativePath>
</parent>
<artifactId>vertx</artifactId>
<packaging>jar</packaging>
<name>Quarkus QE TS: HTTP: Vert.X</name>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-vertx</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-micrometer-registry-prometheus</artifactId>
</dependency>
</dependencies>
<profiles>
<profile>
<id>deploy-to-openshift-using-extension</id>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-openshift</artifactId>
</dependency>
</dependencies>
</profile>
</profiles>
</project>
14 changes: 14 additions & 0 deletions http/vertx/src/main/java/io/quarkus/ts/vertx/Hello.java
Original file line number Diff line number Diff line change
@@ -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;
}
}
33 changes: 33 additions & 0 deletions http/vertx/src/main/java/io/quarkus/ts/vertx/HelloResource.java
Original file line number Diff line number Diff line change
@@ -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<Hello> 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);
}
}
6 changes: 6 additions & 0 deletions http/vertx/src/main/resources/application.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
quarkus.application.name=test-http

%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
1 change: 1 addition & 0 deletions http/vertx/src/main/resources/message.template
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Hello, %s!
121 changes: 121 additions & 0 deletions http/vertx/src/test/java/io/quarkus/ts/vertx/AbstractVertxIT.java
Original file line number Diff line number Diff line change
@@ -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<String, Metric> 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<String, Metric> parseMetrics(String body) {
Map<String, Metric> 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);
}
}
}
18 changes: 18 additions & 0 deletions http/vertx/src/test/java/io/quarkus/ts/vertx/LocalVertxIT.java
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -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();
}
}
17 changes: 17 additions & 0 deletions http/vertx/src/test/java/io/quarkus/ts/vertx/OpenShiftVertxIT.java
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -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();
}
}
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,7 @@
<module>http/hibernate-validator</module>
<module>http/graphql</module>
<module>http/graphql-telemetry</module>
<module>http/vertx</module>
</modules>
</profile>
<profile>
Expand Down

0 comments on commit 17598d8

Please sign in to comment.