From 2b483fa2165907e5d0f18687896915ccab05e07c Mon Sep 17 00:00:00 2001 From: Fedor Dudinskiy Date: Wed, 29 Mar 2023 13:40:09 +0200 Subject: [PATCH] Cover separate management interface Now Quarkus can expose endpoints for metrics and health on a separate network interface. We verify, that this is supported, and we can use custom port, switch HTTPS on and off and change api path. This requires supports on the side of the FW and enabling one of the disabled tests. Test plans for the change: https://github.com/quarkus-qe/quarkus-test-plans/blob/main/QUARKUS-2738.md Related links: https://github.com/quarkusio/quarkus/pull/30506 https://issues.redhat.com/browse/QUARKUS-2738 --- README.md | 3 + http/management/pom.xml | 47 +++++++++ .../main/java/io/quarkus/qe/HttpResource.java | 16 +++ .../META-INF/resources/server.keystore | Bin 0 -> 2407 bytes .../src/main/resources/application.properties | 3 + .../java/io/quarkus/qe/LocalEndpointsIT.java | 57 +++++++++++ .../java/io/quarkus/qe/LocalOptionsIT.java | 94 ++++++++++++++++++ .../io/quarkus/qe/OpenShiftEndpointsIT.java | 7 ++ .../io/quarkus/qe/OpenShiftExtensionIT.java | 32 ++++++ .../io/quarkus/qe/OpenShiftOptionsIT.java | 15 +++ .../src/main/resources/application.properties | 2 +- .../prometheus/HttpServerMetricsIT.java | 10 +- .../prometheus/OpenShiftCustomMetricsIT.java | 15 ++- pom.xml | 3 +- 14 files changed, 296 insertions(+), 8 deletions(-) create mode 100644 http/management/pom.xml create mode 100644 http/management/src/main/java/io/quarkus/qe/HttpResource.java create mode 100644 http/management/src/main/resources/META-INF/resources/server.keystore create mode 100644 http/management/src/main/resources/application.properties create mode 100644 http/management/src/test/java/io/quarkus/qe/LocalEndpointsIT.java create mode 100644 http/management/src/test/java/io/quarkus/qe/LocalOptionsIT.java create mode 100644 http/management/src/test/java/io/quarkus/qe/OpenShiftEndpointsIT.java create mode 100644 http/management/src/test/java/io/quarkus/qe/OpenShiftExtensionIT.java create mode 100644 http/management/src/test/java/io/quarkus/qe/OpenShiftOptionsIT.java diff --git a/README.md b/README.md index 0601f1f48..0e13c084c 100644 --- a/README.md +++ b/README.md @@ -356,6 +356,9 @@ It also verifies multiple deployment strategies like: - Using OpenShift quarkus extension - Using OpenShift quarkus extension and Docker Build strategy +### `http/management` +Verifies, that management interface (micrometer metrics and health endpoints) can be hosted on a separate port + #### 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/management/pom.xml b/http/management/pom.xml new file mode 100644 index 000000000..c8bf9c40c --- /dev/null +++ b/http/management/pom.xml @@ -0,0 +1,47 @@ + + + 4.0.0 + + io.quarkus.ts.qe + parent + 1.0.0-SNAPSHOT + ../.. + + http-management + jar + Quarkus QE TS: HTTP: Management Interface + + + io.quarkus + quarkus-resteasy + + + io.quarkus + quarkus-smallrye-health + + + io.quarkus + quarkus-micrometer-registry-prometheus + + + io.quarkus + quarkus-smallrye-openapi + + + io.quarkus.qe + quarkus-test-openshift + test + + + + + deploy-to-openshift-using-extension + + + io.quarkus + quarkus-openshift + + + + + diff --git a/http/management/src/main/java/io/quarkus/qe/HttpResource.java b/http/management/src/main/java/io/quarkus/qe/HttpResource.java new file mode 100644 index 000000000..ad27953d6 --- /dev/null +++ b/http/management/src/main/java/io/quarkus/qe/HttpResource.java @@ -0,0 +1,16 @@ +package io.quarkus.qe; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +@Path("/ping") +public class HttpResource { + + @GET + @Produces(MediaType.TEXT_PLAIN) + public String pong() { + return "pong"; + } +} diff --git a/http/management/src/main/resources/META-INF/resources/server.keystore b/http/management/src/main/resources/META-INF/resources/server.keystore new file mode 100644 index 0000000000000000000000000000000000000000..cdef1caf3c6da29b3dfff68f816ecd0171727dbf GIT binary patch literal 2407 zcmY+EX*d)L7stn#8OD-*3_}=OXv~bA8e|%~OR`4EPEiVpFiaS^Mp4OnQI?E5wn8Ja zX1OBS*BFGkV~MN@Z})xP_ul7zIOjai?|;tu_`{H(o`ak~p903x( zGl05=yRmd*Gp>W~N}RCU*j@peu_J#<>?zJD+15MH-1Jsa@ByP4IA)-dF-pzBspX z98Mv|&(NP7%&wF8%3@0<5c*Q4QnlMLi@$z!?xd#Ma%>i3p*hO)AnJSub#QYobQh2~ z7ApM=p&?g8*`8}GQ~G5RP3Y11H6V)7Vjo47luePUYGvp-rKBb&$7koL`F0R!NBe~2 zPrr9x3pWyXFfnqeHjz(tLQb1IEOpbex)W!i@2#x4Y;I9}GLEf#Nl zZih>2E{u@YUd#uS^$)U#$sf60PLxm%RT7 z{Y=;remnSQ#KISw9k+^RYfg<5`+1fn*|8jJ&mZkI;LJh8L3*d-F&{J0rdNoPPjtbF z;A`_J`LW9mgIjlv-v-o#QoUMz1Wv)&O>GR6)d~@U3e#_Q*k4&cNZIv6iXVv%>V4yr zZcFR1y2=_937LOi`r^)RY9>+7)x1gl*b+C;+`4s8qOEv1Qz61s{w8L!UO;_{VV*5? z8-ip8bchVBAC1r?{LYvp<@ng2C>=JaSyT^?;K(U#Q0F6>%IpGe6&R@226wJ%*~XfB zOR@JS&vV~=OofU28WuU`YI+9oi{1)`DdK0tY+3v@_Vjt}=2^7!PL&ed><3)0_Vi7! z_z5PYg*+j#kQ-c29!)l$%A2FZV27$Sh1%1%3Aati@TdplB`zAMT9*^0x2A*XfPM)-PiWWuMe`b4OVoER!w&>cRfk_qNlR7 zH;h}nP+i$6(sg@ceNfwG7WGpx?J9Y3NHre(mPt<`mY0neL^p!xo}cUUjyz_(8H=AC zxKuz^Epo&Olu~Smpy9RV277=+1DajJu3pls#n3A*5VtTHX`$@S&`pf~_ zvQ%~2ih_r(p60P5)~7BrfU=2r&x7`bM4oe&m{cSShtm zQF15TL%`);PJ1kH$urU%VG}*qkCYW(I&`=+f ze;c?1C%F4(Mbt&&@;c{HD(8IXJ5iwvdhmxFKGm~{=^#KN-NBRwwxmL!B+>dNgQ zSVIirZ&gBtu_sRQ0D=MDfZKpOe|3z^KcNU#7$j)tALuQO)xv1&YGW{3nwpwg7!v!F zzn3^5`6TxEpC}f{2KYH5{z=&WWmw2xhNbxEkFu92TyT=-hMsbnU7uXj(EqPtV@T|V z^dU1BrlP__h7lF%BI6+^FKfM~!mlFVhnR7y>E~fe4kb0MRu+GB4ln5+k58Hyh_n^x zAAZc$kJgCr{4oKQ#}rhHoHc{i@;WdEodxSBb29vN$lT$udVV(!)EnR;nRz9nRH@Mz z(M3y&=vh=<_j2#mHkeL|%KI2+p|wZJ@O-HpvsQkkO8e2{*YwqqR{5yJb;9Nj9}>5O z|J<5L8^T64=S9d^HWf~By7>_+bOqw}w1t-$22Q=Y6 zA$_RFa1~J|RTCcKjy<1%5d1TZMQO=ni)oR8O9gImb188iQjUsV&p;1$@f#Dlk|nYZ z(bZ?K7|?0K=I~WbE_p6{I!`rpt=hc;riglQrHg-JOxGt@h%<)p^>zXqtKi5zu zD$poUvufFaw>n*U^t{+KmH*Txb%OG1x=H&z_m5yp5~)om5V3b%x9jV(wd-A`{FY8ttiy_#I>UVR&>t7K^tM;_1yE?59Uy(n zWGG#GG+5sTvJ$MGv7;b%S(Y|Zq|<;wzj;+lRvGeSqOe|53iSgvup*K35HM8VcX<;p^XmPZ}jWHI^} z84MHxRyn~A6bA!9!oxkh8y?8tzwn!7iwge!p-i~XbsHq?`qUS#CFl3(DiX7ghgeh& NtHU8ccD984{{ZbYXC(jt literal 0 HcmV?d00001 diff --git a/http/management/src/main/resources/application.properties b/http/management/src/main/resources/application.properties new file mode 100644 index 000000000..ac943493f --- /dev/null +++ b/http/management/src/main/resources/application.properties @@ -0,0 +1,3 @@ +# Only run tests annotated with @QuarkusTest +quarkus.test.type=quarkus-test +quarkus.management.enabled=true diff --git a/http/management/src/test/java/io/quarkus/qe/LocalEndpointsIT.java b/http/management/src/test/java/io/quarkus/qe/LocalEndpointsIT.java new file mode 100644 index 000000000..6b5078421 --- /dev/null +++ b/http/management/src/test/java/io/quarkus/qe/LocalEndpointsIT.java @@ -0,0 +1,57 @@ +package io.quarkus.qe; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +import io.quarkus.test.bootstrap.RestService; +import io.quarkus.test.scenarios.QuarkusScenario; +import io.quarkus.test.services.QuarkusApplication; +import io.restassured.response.Response; + +@QuarkusScenario +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class LocalEndpointsIT { + + @QuarkusApplication + static final RestService app = new RestService(); + + @Test + @Order(1) + public void greeting() { + Response response = app.given().get("/ping"); + assertEquals(200, response.statusCode()); + assertEquals("pong", response.body().asString()); + } + + @Test + public void health() { + app.management().get("q/health").then().statusCode(HttpStatus.SC_OK); + } + + @Test + public void oldHealth() { + app.given().get("q/health").then().statusCode(HttpStatus.SC_NOT_FOUND); + } + + @Test + public void metrics() { + Response response = app.management().get("q/metrics"); + assertEquals(200, response.statusCode()); + String metric = null; + String body = response.body().asString(); + for (String line : body.split("\n")) { + if (line.contains("http_server_requests_seconds_count") && line.contains("SUCCESS")) { + metric = line; + } + } + assertNotNull(metric, "Metric 'http_server_requests_seconds_count' was not found in the response: " + body); + assertTrue(metric.endsWith("1.0"), "requests count is wrong: " + metric); + } +} diff --git a/http/management/src/test/java/io/quarkus/qe/LocalOptionsIT.java b/http/management/src/test/java/io/quarkus/qe/LocalOptionsIT.java new file mode 100644 index 000000000..d62837129 --- /dev/null +++ b/http/management/src/test/java/io/quarkus/qe/LocalOptionsIT.java @@ -0,0 +1,94 @@ +package io.quarkus.qe; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Arrays; + +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.Test; + +import io.quarkus.test.bootstrap.RestService; +import io.quarkus.test.scenarios.QuarkusScenario; +import io.quarkus.test.services.QuarkusApplication; +import io.restassured.path.json.JsonPath; +import io.restassured.response.Response; + +@QuarkusScenario +public class LocalOptionsIT { + + @QuarkusApplication + static final RestService app = new RestService(); + + @QuarkusApplication + static final RestService custom = new RestService() + .withProperty("quarkus.management.port", "9002"); + + @QuarkusApplication + static final RestService tls = new RestService() + .withProperty("quarkus.management.port", "9003") + .withProperty("quarkus.management.ssl.certificate.key-store-file", "META-INF/resources/server.keystore") + .withProperty("quarkus.management.ssl.certificate.key-store-password", "password"); + + @QuarkusApplication + static final RestService unmanaged = new RestService() + .withProperty("quarkus.management.enabled", "false"); + + @QuarkusApplication + static final RestService redirected = new RestService() + .withProperty("quarkus.management.port", "9004") + .withProperty("quarkus.http.root-path", "/api") + .withProperty("quarkus.http.non-application-root-path", "/query") + .withProperty("quarkus.management.root-path", "management"); + + @Test + public void greeting() { + for (RestService service : Arrays.asList(app, custom, tls, unmanaged)) { + Response response = service.given().get("/ping"); + assertEquals(200, response.statusCode()); + assertEquals("pong", response.body().asString()); + + Response openapi = service.given().get("/q/openapi?format=json"); //openapi should be on the old interface + assertEquals(200, openapi.statusCode()); + JsonPath json = openapi.body().jsonPath(); + assertEquals("OK", json.getString("paths.\"/ping\".get.responses.\"200\".description")); + } + } + + @Test + public void unmanaged() { + unmanaged.given().get("q/health").then().statusCode(HttpStatus.SC_OK); + } + + @Test + public void managed() { + app.management().get("q/health").then().statusCode(HttpStatus.SC_OK); + } + + @Test + public void customPort() { + custom.management() + .get("q/health").then().statusCode(HttpStatus.SC_OK); + } + + @Test + public void tls() { + tls.management() + .relaxedHTTPSValidation() + .get("q/health").then().statusCode(HttpStatus.SC_OK); + } + + @Test + public void redirected() { + Response response = redirected.given().get("api/ping"); + assertEquals(200, response.statusCode()); + assertEquals("pong", response.body().asString()); + + redirected.management().get("management/health").then() + .statusCode(HttpStatus.SC_OK); + + Response openapi = redirected.given().get("query/openapi?format=json"); + assertEquals(200, openapi.statusCode()); + JsonPath json = openapi.body().jsonPath(); + assertEquals("OK", json.getString("paths.\"/api/ping\".get.responses.\"200\".description")); + } +} diff --git a/http/management/src/test/java/io/quarkus/qe/OpenShiftEndpointsIT.java b/http/management/src/test/java/io/quarkus/qe/OpenShiftEndpointsIT.java new file mode 100644 index 000000000..49e5c6cc3 --- /dev/null +++ b/http/management/src/test/java/io/quarkus/qe/OpenShiftEndpointsIT.java @@ -0,0 +1,7 @@ +package io.quarkus.qe; + +import io.quarkus.test.scenarios.OpenShiftScenario; + +@OpenShiftScenario +public class OpenShiftEndpointsIT extends LocalEndpointsIT { +} diff --git a/http/management/src/test/java/io/quarkus/qe/OpenShiftExtensionIT.java b/http/management/src/test/java/io/quarkus/qe/OpenShiftExtensionIT.java new file mode 100644 index 000000000..105307a6c --- /dev/null +++ b/http/management/src/test/java/io/quarkus/qe/OpenShiftExtensionIT.java @@ -0,0 +1,32 @@ +package io.quarkus.qe; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +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.response.Response; + +@OpenShiftScenario(deployment = OpenShiftDeploymentStrategy.UsingOpenShiftExtension) +@Disabled("Requires fixing https://github.com/quarkusio/quarkus/issues/32135 and changes in the Framework") +public class OpenShiftExtensionIT { + @QuarkusApplication + static final RestService app = new RestService(); + + @Test + public void payload() { + Response response = app.given().get("/ping"); + assertEquals(200, response.statusCode()); + assertEquals("pong", response.body().asString()); + } + + @Test + public void health() { + app.management().get("q/health").then().statusCode(HttpStatus.SC_OK); + } +} diff --git a/http/management/src/test/java/io/quarkus/qe/OpenShiftOptionsIT.java b/http/management/src/test/java/io/quarkus/qe/OpenShiftOptionsIT.java new file mode 100644 index 000000000..3297a592f --- /dev/null +++ b/http/management/src/test/java/io/quarkus/qe/OpenShiftOptionsIT.java @@ -0,0 +1,15 @@ +package io.quarkus.qe; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import io.quarkus.test.scenarios.OpenShiftScenario; + +@OpenShiftScenario +public class OpenShiftOptionsIT extends LocalOptionsIT { + @Test + @Override + @Disabled("SSL on openshift is not supported by the FW (yet)") + public void tls() { + } +} diff --git a/monitoring/micrometer-prometheus/src/main/resources/application.properties b/monitoring/micrometer-prometheus/src/main/resources/application.properties index b59057ded..72392166b 100644 --- a/monitoring/micrometer-prometheus/src/main/resources/application.properties +++ b/monitoring/micrometer-prometheus/src/main/resources/application.properties @@ -1,4 +1,4 @@ quarkus.openshift.labels.app-with-metrics=quarkus-app # MP Integration -quarkus.micrometer.binder.mp-metrics.enabled=true \ No newline at end of file +quarkus.micrometer.binder.mp-metrics.enabled=true diff --git a/monitoring/micrometer-prometheus/src/test/java/io/quarkus/ts/micrometer/prometheus/HttpServerMetricsIT.java b/monitoring/micrometer-prometheus/src/test/java/io/quarkus/ts/micrometer/prometheus/HttpServerMetricsIT.java index 46ce7f514..bb703fa9a 100644 --- a/monitoring/micrometer-prometheus/src/test/java/io/quarkus/ts/micrometer/prometheus/HttpServerMetricsIT.java +++ b/monitoring/micrometer-prometheus/src/test/java/io/quarkus/ts/micrometer/prometheus/HttpServerMetricsIT.java @@ -14,7 +14,9 @@ import org.apache.http.HttpStatus; import org.junit.jupiter.api.Test; +import io.quarkus.test.bootstrap.RestService; import io.quarkus.test.scenarios.QuarkusScenario; +import io.quarkus.test.services.QuarkusApplication; @QuarkusScenario public class HttpServerMetricsIT { @@ -31,6 +33,10 @@ public class HttpServerMetricsIT { private static final String HTTP_SERVER_REQUESTS_METRICS_FORMAT = "http_server_requests_seconds_%s{method=\"GET\",outcome=\"SUCCESS\",status=\"200\",uri=\"%s\""; private static final String PING_PONG_ENDPOINT = "/without-metrics-pingpong"; + @QuarkusApplication + static RestService app = new RestService() + .withProperty("quarkus.management.enabled", "true"); + @Test public void testHttpServerRequestsCountShouldBeRegistered() { thenHttpServerRequestsMetricsAreNotPresent(); @@ -47,7 +53,7 @@ private void whenCallPingPong() { } private void thenHttpServerRequestsMetricsAreNotPresent() { - String metrics = when().get("/q/metrics").then() + String metrics = app.management().get("/q/metrics").then() .statusCode(HttpStatus.SC_OK).extract().asString(); for (String metricSuffix : HTTP_SERVER_REQUESTS_METRICS_SUFFIX) { @@ -57,7 +63,7 @@ private void thenHttpServerRequestsMetricsAreNotPresent() { } private void thenHttpServerRequestsMetricsArePresent() { - String metrics = when().get("/q/metrics").then() + String metrics = app.management().get("/q/metrics").then() .statusCode(HttpStatus.SC_OK).extract().asString(); for (String metricSuffix : HTTP_SERVER_REQUESTS_METRICS_SUFFIX) { diff --git a/monitoring/micrometer-prometheus/src/test/java/io/quarkus/ts/micrometer/prometheus/OpenShiftCustomMetricsIT.java b/monitoring/micrometer-prometheus/src/test/java/io/quarkus/ts/micrometer/prometheus/OpenShiftCustomMetricsIT.java index 4ea4760f8..fb95c6797 100644 --- a/monitoring/micrometer-prometheus/src/test/java/io/quarkus/ts/micrometer/prometheus/OpenShiftCustomMetricsIT.java +++ b/monitoring/micrometer-prometheus/src/test/java/io/quarkus/ts/micrometer/prometheus/OpenShiftCustomMetricsIT.java @@ -12,7 +12,6 @@ import org.apache.http.HttpStatus; import org.jboss.logging.Logger; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import io.quarkus.test.bootstrap.RestService; @@ -27,7 +26,6 @@ * - `prime_number_max_{uniqueId}`: max prime number that is found. * - `prime_number_test_{uniqueId}`: with information about the calculation of the prime number. */ -@Disabled("https://github.com/quarkusio/quarkus/issues/31228") @OpenShiftScenario(deployment = OpenShiftDeploymentStrategy.UsingOpenShiftExtension) public class OpenShiftCustomMetricsIT { @@ -51,7 +49,16 @@ public class OpenShiftCustomMetricsIT { static final Integer ANY_VALUE = null; @QuarkusApplication - static RestService app = new RestService().onPostStart(OpenShiftCustomMetricsIT::loadServiceMonitor); + static RestService app = new RestService() + /* + * TODO fix deployment with OpenShiftDeploymentStrategies in the Framework + * see https://github.com/quarkusio/quarkus/issues/32135#issuecomment-1486740862 for details + * .withProperty("quarkus.management.ssl.certificate.key-store-file", + * "META-INF/resources/server.keystore") + * .withProperty("quarkus.management.ssl.certificate.key-store-password", "password") + * .withProperty("quarkus.management.enabled", "true") + */ + .onPostStart(OpenShiftCustomMetricsIT::loadServiceMonitor); @Inject static OpenShiftClient client; @@ -121,7 +128,7 @@ private void thenMetricIsExposedInServiceEndpoint(String name, Integer expected) shouldContain += " " + expected; } - app.given().get("/q/metrics").then() + app.management().get("/q/metrics").then() .statusCode(HttpStatus.SC_OK) .body(containsString(shouldContain)); }); diff --git a/pom.xml b/pom.xml index 01582e541..8aff64cd7 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ quarkus-bom io.quarkus 999-SNAPSHOT - 1.3.0.Beta9 + 1.3.0.Beta10 0.42.0 2.12.1.Final 4.5.14 @@ -451,6 +451,7 @@ http/graphql http/graphql-telemetry http/vertx + http/management