Skip to content

Commit

Permalink
Unblock SmallRye Health exposed routes
Browse files Browse the repository at this point in the history
  • Loading branch information
xstefank committed Jan 18, 2024
1 parent 02471b8 commit 188c343
Show file tree
Hide file tree
Showing 11 changed files with 122 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
Expand Down Expand Up @@ -217,7 +216,6 @@ public void defineHealthRoutes(BuildProducer<RouteBuildItem> routes,
.routeConfigKey("quarkus.smallrye-health.root-path")
.handler(new SmallRyeHealthHandler())
.displayOnNotFoundPage()
.blockingRoute()
.build());

// Register the liveness handler
Expand All @@ -226,7 +224,6 @@ public void defineHealthRoutes(BuildProducer<RouteBuildItem> routes,
.nestedRoute(healthConfig.rootPath, healthConfig.livenessPath)
.handler(new SmallRyeLivenessHandler())
.displayOnNotFoundPage()
.blockingRoute()
.build());

// Register the readiness handler
Expand All @@ -235,29 +232,14 @@ public void defineHealthRoutes(BuildProducer<RouteBuildItem> routes,
.nestedRoute(healthConfig.rootPath, healthConfig.readinessPath)
.handler(new SmallRyeReadinessHandler())
.displayOnNotFoundPage()
.blockingRoute()
.build());

// Find all health groups
Set<String> healthGroups = new HashSet<>();
// with simple @HealthGroup annotations
for (AnnotationInstance healthGroupAnnotation : index.getAnnotations(HEALTH_GROUP)) {
healthGroups.add(healthGroupAnnotation.value().asString());
}
// with @HealthGroups repeatable annotations
for (AnnotationInstance healthGroupsAnnotation : index.getAnnotations(HEALTH_GROUPS)) {
for (AnnotationInstance healthGroupAnnotation : healthGroupsAnnotation.value().asNestedArray()) {
healthGroups.add(healthGroupAnnotation.value().asString());
}
}

// Register the health group handlers
routes.produce(nonApplicationRootPathBuildItem.routeBuilder()
.management("quarkus.smallrye-health.management.enabled")
.nestedRoute(healthConfig.rootPath, healthConfig.groupPath)
.handler(new SmallRyeHealthGroupHandler())
.displayOnNotFoundPage()
.blockingRoute()
.build());

SmallRyeIndividualHealthGroupHandler handler = new SmallRyeIndividualHealthGroupHandler();
Expand All @@ -266,7 +248,6 @@ public void defineHealthRoutes(BuildProducer<RouteBuildItem> routes,
.nestedRoute(healthConfig.rootPath, healthConfig.groupPath + "/*")
.handler(handler)
.displayOnNotFoundPage()
.blockingRoute()
.build());

// Register the wellness handler
Expand All @@ -275,7 +256,6 @@ public void defineHealthRoutes(BuildProducer<RouteBuildItem> routes,
.nestedRoute(healthConfig.rootPath, healthConfig.wellnessPath)
.handler(new SmallRyeWellnessHandler())
.displayOnNotFoundPage()
.blockingRoute()
.build());

// Register the startup handler
Expand All @@ -284,7 +264,6 @@ public void defineHealthRoutes(BuildProducer<RouteBuildItem> routes,
.nestedRoute(healthConfig.rootPath, healthConfig.startupPath)
.handler(new SmallRyeStartupHandler())
.displayOnNotFoundPage()
.blockingRoute()
.build());

}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package io.quarkus.smallrye.health.test;

import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.is;

import java.time.Duration;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;

import org.eclipse.microprofile.health.HealthCheck;
import org.eclipse.microprofile.health.HealthCheckResponse;
import org.eclipse.microprofile.health.Liveness;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusUnitTest;
import io.quarkus.vertx.web.Route;
import io.restassured.RestAssured;
import io.restassured.parsing.Parser;
import io.smallrye.common.annotation.Blocking;
import io.smallrye.health.SmallRyeHealthReporter;
import io.smallrye.mutiny.Uni;

public class BlockingNonBlockingTest {

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar
.addClasses(BlockingHealthCheck.class, Routes.class)
.addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml"));

@Test
public void testRegisterHealthOnBlockingThreadStep1() {
// initial startup health blocking call on worker thread
given()
.when().get("/start-health")
.then().statusCode(200);

try {
RestAssured.defaultParser = Parser.JSON;
// repeat the call a few times since the block isn't always logged
for (int i = 0; i < 3; i++) {
RestAssured.when().get("/q/health").then()
.body("status", is("UP"),
"checks.status", contains("UP"),
"checks.name", contains("blocking"));
}
} finally {
RestAssured.reset();
}
}

@Liveness
static final class BlockingHealthCheck implements HealthCheck {
@Override
public HealthCheckResponse call() {
// await() is illegal on the executor thread
Uni.createFrom().item(42).onItem().delayIt().by(Duration.ofMillis(10)).await().indefinitely();
return HealthCheckResponse.up("blocking");
}
}

@ApplicationScoped
static final class Routes {

@Inject
SmallRyeHealthReporter smallRyeHealthReporter;

@Route(path = "/start-health", methods = Route.HttpMethod.GET)
@Blocking
public String health() {
return smallRyeHealthReporter.getHealth().toString();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import org.eclipse.microprofile.health.HealthCheck;
import org.eclipse.microprofile.health.HealthCheckResponse;

import io.quarkus.runtime.BlockingOperationControl;
import io.smallrye.health.AsyncHealthCheckFactory;
import io.smallrye.health.api.AsyncHealthCheck;
import io.smallrye.mutiny.Uni;
Expand All @@ -28,14 +27,12 @@ public QuarkusAsyncHealthCheckFactory(Vertx vertx) {
@Override
public Uni<HealthCheckResponse> callSync(HealthCheck healthCheck) {
Uni<HealthCheckResponse> healthCheckResponseUni = super.callSync(healthCheck);
return BlockingOperationControl.isBlockingAllowed() ? healthCheckResponseUni
: healthCheckResponseUni.runSubscriptionOn(MutinyHelper.blockingExecutor(vertx, false));
return healthCheckResponseUni.runSubscriptionOn(MutinyHelper.blockingExecutor(vertx, false));
}

@Override
public Uni<HealthCheckResponse> callAsync(AsyncHealthCheck asyncHealthCheck) {
Uni<HealthCheckResponse> healthCheckResponseUni = super.callAsync(asyncHealthCheck);
return !BlockingOperationControl.isBlockingAllowed() ? healthCheckResponseUni
: healthCheckResponseUni.runSubscriptionOn(MutinyHelper.executor(vertx));
return healthCheckResponseUni.runSubscriptionOn(MutinyHelper.executor(vertx));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@

import io.smallrye.health.SmallRyeHealth;
import io.smallrye.health.SmallRyeHealthReporter;
import io.smallrye.mutiny.Uni;
import io.vertx.ext.web.RoutingContext;

public class SmallRyeHealthGroupHandler extends SmallRyeHealthHandlerBase {

@Override
protected SmallRyeHealth getHealth(SmallRyeHealthReporter reporter, RoutingContext ctx) {
return reporter.getHealthGroups();
protected Uni<SmallRyeHealth> getHealth(SmallRyeHealthReporter reporter, RoutingContext ctx) {
return reporter.getHealthGroupsAsync();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@

import io.smallrye.health.SmallRyeHealth;
import io.smallrye.health.SmallRyeHealthReporter;
import io.smallrye.mutiny.Uni;
import io.vertx.ext.web.RoutingContext;

public class SmallRyeHealthHandler extends SmallRyeHealthHandlerBase {

@Override
protected SmallRyeHealth getHealth(SmallRyeHealthReporter reporter, RoutingContext ctx) {
return reporter.getHealth();
protected Uni<SmallRyeHealth> getHealth(SmallRyeHealthReporter reporter, RoutingContext ctx) {
return reporter.getHealthAsync();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,19 @@
import io.quarkus.vertx.http.runtime.security.QuarkusHttpUser;
import io.smallrye.health.SmallRyeHealth;
import io.smallrye.health.SmallRyeHealthReporter;
import io.smallrye.mutiny.Uni;
import io.smallrye.mutiny.vertx.MutinyHelper;
import io.vertx.core.Context;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.HttpHeaders;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.ext.web.RoutingContext;

abstract class SmallRyeHealthHandlerBase implements Handler<RoutingContext> {

protected abstract SmallRyeHealth getHealth(SmallRyeHealthReporter reporter, RoutingContext routingContext);
protected abstract Uni<SmallRyeHealth> getHealth(SmallRyeHealthReporter reporter, RoutingContext routingContext);

@Override
public void handle(RoutingContext ctx) {
Expand All @@ -41,19 +45,21 @@ private void doHandle(RoutingContext ctx) {
Arc.container().instance(CurrentIdentityAssociation.class).get().setIdentity(user.getSecurityIdentity());
}
SmallRyeHealthReporter reporter = Arc.container().instance(SmallRyeHealthReporter.class).get();
SmallRyeHealth health = getHealth(reporter, ctx);
HttpServerResponse resp = ctx.response();
if (health.isDown()) {
resp.setStatusCode(503);
}
resp.headers().set(HttpHeaders.CONTENT_TYPE, "application/json; charset=UTF-8");
Buffer buffer = Buffer.buffer(256); // this size seems to cover the basic health checks
try (BufferOutputStream outputStream = new BufferOutputStream(buffer);) {
reporter.reportHealth(outputStream, health);
resp.end(buffer);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
Context context = Vertx.currentContext();
getHealth(reporter, ctx).emitOn(MutinyHelper.executor(context))
.subscribe().with(health -> {
HttpServerResponse resp = ctx.response();
if (health.isDown()) {
resp.setStatusCode(503);
}
resp.headers().set(HttpHeaders.CONTENT_TYPE, "application/json; charset=UTF-8");
Buffer buffer = Buffer.buffer(256); // this size seems to cover the basic health checks
try (BufferOutputStream outputStream = new BufferOutputStream(buffer);) {
reporter.reportHealth(outputStream, health);
resp.end(buffer);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
});
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@

import io.smallrye.health.SmallRyeHealth;
import io.smallrye.health.SmallRyeHealthReporter;
import io.smallrye.mutiny.Uni;
import io.vertx.ext.web.RoutingContext;

public class SmallRyeIndividualHealthGroupHandler extends SmallRyeHealthHandlerBase {

@Override
protected SmallRyeHealth getHealth(SmallRyeHealthReporter reporter, RoutingContext ctx) {
protected Uni<SmallRyeHealth> getHealth(SmallRyeHealthReporter reporter, RoutingContext ctx) {
String group = ctx.normalizedPath().substring(ctx.normalizedPath().lastIndexOf("/") + 1);
return reporter.getHealthGroup(group);
return reporter.getHealthGroupAsync(group);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@

import io.smallrye.health.SmallRyeHealth;
import io.smallrye.health.SmallRyeHealthReporter;
import io.smallrye.mutiny.Uni;
import io.vertx.ext.web.RoutingContext;

public class SmallRyeLivenessHandler extends SmallRyeHealthHandlerBase {

@Override
protected SmallRyeHealth getHealth(SmallRyeHealthReporter reporter, RoutingContext ctx) {
return reporter.getLiveness();
protected Uni<SmallRyeHealth> getHealth(SmallRyeHealthReporter reporter, RoutingContext ctx) {
return reporter.getLivenessAsync();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@

import io.smallrye.health.SmallRyeHealth;
import io.smallrye.health.SmallRyeHealthReporter;
import io.smallrye.mutiny.Uni;
import io.vertx.ext.web.RoutingContext;

public class SmallRyeReadinessHandler extends SmallRyeHealthHandlerBase {

@Override
protected SmallRyeHealth getHealth(SmallRyeHealthReporter reporter, RoutingContext routingContext) {
return reporter.getReadiness();
protected Uni<SmallRyeHealth> getHealth(SmallRyeHealthReporter reporter, RoutingContext ctx) {
return reporter.getReadinessAsync();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@

import io.smallrye.health.SmallRyeHealth;
import io.smallrye.health.SmallRyeHealthReporter;
import io.smallrye.mutiny.Uni;
import io.vertx.ext.web.RoutingContext;

public class SmallRyeStartupHandler extends SmallRyeHealthHandlerBase {

@Override
protected SmallRyeHealth getHealth(SmallRyeHealthReporter reporter, RoutingContext routingContext) {
return reporter.getStartup();
protected Uni<SmallRyeHealth> getHealth(SmallRyeHealthReporter reporter, RoutingContext ctx) {
return reporter.getStartupAsync();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@

import io.smallrye.health.SmallRyeHealth;
import io.smallrye.health.SmallRyeHealthReporter;
import io.smallrye.mutiny.Uni;
import io.vertx.ext.web.RoutingContext;

public class SmallRyeWellnessHandler extends SmallRyeHealthHandlerBase {

@Override
protected SmallRyeHealth getHealth(SmallRyeHealthReporter reporter, RoutingContext routingContext) {
return reporter.getWellness();
protected Uni<SmallRyeHealth> getHealth(SmallRyeHealthReporter reporter, RoutingContext ctx) {
return reporter.getWellnessAsync();
}
}

0 comments on commit 188c343

Please sign in to comment.