Skip to content

Commit

Permalink
Merge pull request #32026 from cescoffier/management-interface-for-app
Browse files Browse the repository at this point in the history
Allows applications to expose management endpoints
  • Loading branch information
gsmet committed Mar 28, 2023
2 parents 7c235f0 + 8edf45d commit 3787a0f
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 2 deletions.
30 changes: 29 additions & 1 deletion docs/src/main/asciidoc/management-interface-reference.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,10 @@ The `quarkus.http.root-path` property is only applied to the main HTTP server an
In addition, the `quarkus.http.non-application-root-path` property is not used for endpoint exposed on the management interface.
====

== Create a management endpoint
[[management-endpoint-extension]]
== Create a management endpoint in an extension

NOTE: To expose an endpoint on the management interface from the code of an application, refer to xref:#management-endpoint-application[the application section].

SmallRye Health Checks, SmallRye Metrics, and Micrometer endpoints will be declared as management endpoints when the management interface is enabled.

Expand Down Expand Up @@ -114,6 +117,31 @@ Otherwise, it will be exposed on: `http://localhost:8080/q/my-path`.

IMPORTANT: Management endpoints can only be declared by extensions and not from the application code.

[[management-endpoint-application]]
== Exposing an endpoint on the management interface (as an application)

You can expose endpoints on the management interface by registering routes on the management router.
To access the router use the following code:

[source,java]
----
public void registerManagementRoutes(@Observes ManagementInterface mi) {
mi.router().get("/admin").handler(rc ->
rc.response().end("admin it is")
);
}
----

The `io.quarkus.vertx.http.ManagementInterface` event is fired when the management interface is initialized.
So, if the management interface is not enabled, the method won't be called.

The `router()` method returns a `io.vertx.ext.web.Router` object which can be used to register routes.
The paths are relative to `/`.
For example, the previous snippet registers a route on `/admin`.
This route is accessible on `http://0.0.0.0:9000/admin`, if you use the default host and port.

More details about the `Router` API can be found on https://vertx.io/docs/vertx-web/java/[the Vert.x Web documentation].

== Management Interface Configuration

include::{generated-dir}/config/quarkus-management-management-management-interface-build-time-config.adoc[leveloffset=+1, opts=optional]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.quarkus.vertx.http;

import io.vertx.ext.web.Router;

/**
* A class allowing to access the management router.
* You can access the instance of this class using a CDI observer:
* {@code public void init(@Observe ManagementInterface mi) {...}}
* <p>
* If the management interface is disabled, the event is not fired.
*/
public interface ManagementInterface {

/**
* @return the management router
*/
Router router();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.quarkus.vertx.http.runtime;

import io.quarkus.vertx.http.ManagementInterface;
import io.vertx.ext.web.Router;

public class ManagementInterfaceImpl implements ManagementInterface {

private final Router router;

public ManagementInterfaceImpl(Router router) {
this.router = router;
}

@Override
public Router router() {
return router;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import java.util.regex.Pattern;

import jakarta.enterprise.event.Event;
import jakarta.enterprise.inject.Default;

import org.crac.Resource;
import org.jboss.logging.Logger;
Expand Down Expand Up @@ -60,6 +61,7 @@
import io.quarkus.vertx.core.runtime.VertxCoreRecorder;
import io.quarkus.vertx.core.runtime.config.VertxConfiguration;
import io.quarkus.vertx.http.HttpServerOptionsCustomizer;
import io.quarkus.vertx.http.ManagementInterface;
import io.quarkus.vertx.http.runtime.HttpConfiguration.InsecureRequests;
import io.quarkus.vertx.http.runtime.devmode.RemoteSyncHandler;
import io.quarkus.vertx.http.runtime.devmode.VertxHttpHotReplacementSetup;
Expand Down Expand Up @@ -380,7 +382,7 @@ public void finalizeRouter(BeanContainer container, Consumer<Route> defaultRoute
filterList.addAll(filters.getFilters());

// Then, fire the resuming router
event.select(Router.class).fire(httpRouteRouter);
event.select(Router.class, Default.Literal.INSTANCE).fire(httpRouteRouter);
// Also fires the Mutiny one
event.select(io.vertx.mutiny.ext.web.Router.class).fire(mutinyRouter.getValue());

Expand Down Expand Up @@ -550,6 +552,8 @@ public void handle(RoutingContext event) {
Handler<HttpServerRequest> handler = HttpServerCommonHandlers.enforceDuplicatedContext(mr);
handler = HttpServerCommonHandlers.applyProxy(managementConfiguration.getValue().proxy, handler, vertx);

event.select(ManagementInterface.class).fire(new ManagementInterfaceImpl(managementRouter.getValue()));

VertxHttpRecorder.managementRouter = handler;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package io.quarkus.it.management;

import jakarta.enterprise.event.Observes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

import io.quarkus.vertx.http.ManagementInterface;
import io.vertx.ext.web.Router;

@Path("/service")
public class GreetingResource {

Expand All @@ -17,4 +21,12 @@ public String hello() {
public String goodbye() {
return "goodbye";
}

public void initManagement(@Observes ManagementInterface mi) {
mi.router().get("/admin").handler(rc -> rc.response().end("admin it is"));
}

public void initMain(@Observes Router router) {
router.get("/main").handler(rc -> rc.response().end("main"));
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.quarkus.it.management;

import org.hamcrest.Matchers;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import io.quarkus.test.junit.QuarkusTest;
Expand Down Expand Up @@ -33,4 +34,24 @@ void verifyThatMetricsAreExposedOnManagementInterface() {
.then().statusCode(404);
}

@Test
void verifyAccessToTheManagementInterface() {
String s = RestAssured.get(getPrefix() + "/admin")
.then().statusCode(200)
.extract()
.asString();
Assertions.assertEquals("admin it is", s);

s = RestAssured.get("/main")
.then().statusCode(200)
.extract()
.asString();
Assertions.assertEquals("main", s);

RestAssured.get("/admin")
.then().statusCode(404);
RestAssured.get(getPrefix() + "/main")
.then().statusCode(404);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package io.quarkus.it.vertx;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Observes;

import io.quarkus.vertx.http.ManagementInterface;
import io.vertx.ext.web.Router;

@ApplicationScoped
public class NoManagementInterfaceRoute {

boolean exposed;

void initMain(@Observes Router router) {
router.get("/management-interface-test").handler(rc -> {
if (exposed) {
rc.response().setStatusCode(500).end("KO");
} else {
rc.response().end("OK");
}
});
}

void init(@Observes ManagementInterface mi) {
exposed = true;
mi.router().get("/admin").handler(rc -> rc.response().end("admin it is"));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ public void testRouteRegistration() {
get("/my-path").then().body(containsString("OK"));
}

@Test
public void testManagementInterfaceMissing() {
get("/management-interface-test").then().body(containsString("OK"));
}

@Test
public void testRouteRegistrationMTLS() {
RequestSpecification spec = new RequestSpecBuilder()
Expand Down

0 comments on commit 3787a0f

Please sign in to comment.