Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allows applications to expose management endpoints #32026

Merged
merged 1 commit into from
Mar 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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