diff --git a/bom/application/pom.xml b/bom/application/pom.xml index a2801854fbb97..fbe671d5ed3d8 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -54,7 +54,7 @@ 1.0.13 2.6.0 2.11.0 - 3.7.1 + 3.8.0 1.2.1 1.3.5 3.0.3 diff --git a/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesHealthStartupPathBuildItem.java b/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesHealthStartupPathBuildItem.java new file mode 100644 index 0000000000000..d4f962996a641 --- /dev/null +++ b/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesHealthStartupPathBuildItem.java @@ -0,0 +1,19 @@ +package io.quarkus.kubernetes.spi; + +import io.quarkus.builder.item.SimpleBuildItem; + +public final class KubernetesHealthStartupPathBuildItem extends SimpleBuildItem { + + private final String path; + + public KubernetesHealthStartupPathBuildItem(String path) { + this.path = path; + } + + /** + * @return the path + */ + public String getPath() { + return path; + } +} diff --git a/extensions/smallrye-health/deployment/src/main/java/io/quarkus/smallrye/health/deployment/HealthOpenAPIFilter.java b/extensions/smallrye-health/deployment/src/main/java/io/quarkus/smallrye/health/deployment/HealthOpenAPIFilter.java index 96051e54fbc61..063a47ffcd6e0 100644 --- a/extensions/smallrye-health/deployment/src/main/java/io/quarkus/smallrye/health/deployment/HealthOpenAPIFilter.java +++ b/extensions/smallrye-health/deployment/src/main/java/io/quarkus/smallrye/health/deployment/HealthOpenAPIFilter.java @@ -36,11 +36,13 @@ public class HealthOpenAPIFilter implements OASFilter { private final String rootPath; private final String livenessPath; private final String readinessPath; + private final String startupPath; - public HealthOpenAPIFilter(String rootPath, String livenessPath, String readinessPath) { + public HealthOpenAPIFilter(String rootPath, String livenessPath, String readinessPath, String startupPath) { this.rootPath = rootPath; this.livenessPath = livenessPath; this.readinessPath = readinessPath; + this.startupPath = startupPath; } @Override @@ -64,6 +66,9 @@ public void filterOpenAPI(OpenAPI openAPI) { // Readiness paths.addPathItem(readinessPath, createReadinessPathItem()); + + // Startup + paths.addPathItem(startupPath, createStartupPathItem()); } private PathItem createHealthPathItem() { @@ -93,12 +98,21 @@ private PathItem createReadinessPathItem() { return pathItem; } + private PathItem createStartupPathItem() { + PathItem pathItem = new PathItemImpl(); + pathItem.setDescription("MicroProfile Health - Startup Endpoint"); + pathItem.setSummary( + "Startup checks are an used to tell when the application has started"); + pathItem.setGET(createStartupOperation()); + return pathItem; + } + private Operation createHealthOperation() { Operation operation = new OperationImpl(); operation.setDescription("Check the health of the application"); operation.setOperationId("microprofile_health_root"); operation.setTags(MICROPROFILE_HEALTH_TAG); - operation.setSummary("An aggregated view of the Liveness and Readiness of this application"); + operation.setSummary("An aggregated view of the Liveness, Readiness and Startup of this application"); operation.setResponses(createAPIResponses()); return operation; } @@ -123,6 +137,16 @@ private Operation createReadinessOperation() { return operation; } + private Operation createStartupOperation() { + Operation operation = new OperationImpl(); + operation.setDescription("Check the startup of the application"); + operation.setOperationId("microprofile_health_startup"); + operation.setTags(MICROPROFILE_HEALTH_TAG); + operation.setSummary("The Startup check of this application"); + operation.setResponses(createAPIResponses()); + return operation; + } + private APIResponses createAPIResponses() { APIResponses responses = new APIResponsesImpl(); responses.addAPIResponse("200", createAPIResponse("OK")); diff --git a/extensions/smallrye-health/deployment/src/main/java/io/quarkus/smallrye/health/deployment/SmallRyeHealthProcessor.java b/extensions/smallrye-health/deployment/src/main/java/io/quarkus/smallrye/health/deployment/SmallRyeHealthProcessor.java index 53b004acc3cd1..b328f96324008 100644 --- a/extensions/smallrye-health/deployment/src/main/java/io/quarkus/smallrye/health/deployment/SmallRyeHealthProcessor.java +++ b/extensions/smallrye-health/deployment/src/main/java/io/quarkus/smallrye/health/deployment/SmallRyeHealthProcessor.java @@ -20,6 +20,7 @@ import org.eclipse.microprofile.config.ConfigProvider; import org.eclipse.microprofile.health.Liveness; import org.eclipse.microprofile.health.Readiness; +import org.eclipse.microprofile.health.Startup; import org.eclipse.microprofile.health.spi.HealthCheckResponseProvider; import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.AnnotationTarget; @@ -59,6 +60,7 @@ import io.quarkus.deployment.util.WebJarUtil; import io.quarkus.kubernetes.spi.KubernetesHealthLivenessPathBuildItem; import io.quarkus.kubernetes.spi.KubernetesHealthReadinessPathBuildItem; +import io.quarkus.kubernetes.spi.KubernetesHealthStartupPathBuildItem; import io.quarkus.smallrye.health.deployment.spi.HealthBuildItem; import io.quarkus.smallrye.health.runtime.ShutdownReadinessListener; import io.quarkus.smallrye.health.runtime.SmallRyeHealthGroupHandler; @@ -85,6 +87,7 @@ class SmallRyeHealthProcessor { private static final DotName LIVENESS = DotName.createSimple(Liveness.class.getName()); private static final DotName READINESS = DotName.createSimple(Readiness.class.getName()); + private static final DotName STARTUP = DotName.createSimple(Startup.class.getName()); private static final DotName HEALTH_GROUP = DotName.createSimple(HealthGroup.class.getName()); private static final DotName HEALTH_GROUPS = DotName.createSimple(HealthGroups.class.getName()); private static final DotName WELLNESS = DotName.createSimple(Wellness.class.getName()); @@ -154,10 +157,11 @@ void build(SmallRyeHealthRecorder recorder, feature.produce(new FeatureBuildItem(Feature.SMALLRYE_HEALTH)); - // Discover the beans annotated with @Health, @Liveness, @Readiness, @HealthGroup, + // Discover the beans annotated with @Health, @Liveness, @Readiness, @Startup, @HealthGroup, // @HealthGroups and @Wellness even if no scope is defined beanDefiningAnnotation.produce(new BeanDefiningAnnotationBuildItem(LIVENESS)); beanDefiningAnnotation.produce(new BeanDefiningAnnotationBuildItem(READINESS)); + beanDefiningAnnotation.produce(new BeanDefiningAnnotationBuildItem(STARTUP)); beanDefiningAnnotation.produce(new BeanDefiningAnnotationBuildItem(HEALTH_GROUP)); beanDefiningAnnotation.produce(new BeanDefiningAnnotationBuildItem(HEALTH_GROUPS)); beanDefiningAnnotation.produce(new BeanDefiningAnnotationBuildItem(WELLNESS)); @@ -195,6 +199,7 @@ public void defineHealthRoutes(BuildProducer routes, // log a warning if users try to use MP Health annotations with JAX-RS @Path warnIfJaxRsPathUsed(index, LIVENESS); warnIfJaxRsPathUsed(index, READINESS); + warnIfJaxRsPathUsed(index, STARTUP); warnIfJaxRsPathUsed(index, WELLNESS); // Register the health handler @@ -291,7 +296,8 @@ public void includeInOpenAPIEndpoint(BuildProducer livenessPathItemProducer, - BuildProducer readinessPathItemProducer) { + BuildProducer readinessPathItemProducer, + BuildProducer startupPathItemProducer) { livenessPathItemProducer.produce( new KubernetesHealthLivenessPathBuildItem( @@ -332,6 +339,9 @@ public void kubernetes(NonApplicationRootPathBuildItem nonApplicationRootPathBui readinessPathItemProducer.produce( new KubernetesHealthReadinessPathBuildItem( nonApplicationRootPathBuildItem.resolveNestedPath(healthConfig.rootPath, healthConfig.readinessPath))); + startupPathItemProducer.produce( + new KubernetesHealthStartupPathBuildItem( + nonApplicationRootPathBuildItem.resolveNestedPath(healthConfig.rootPath, healthConfig.startupPath))); } @BuildStep @@ -355,6 +365,7 @@ AnnotationsTransformerBuildItem annotationTransformer(BeanArchiveIndexBuildItem List healthAnnotations = new ArrayList<>(5); healthAnnotations.add(LIVENESS); healthAnnotations.add(READINESS); + healthAnnotations.add(STARTUP); healthAnnotations.add(HEALTH_GROUP); healthAnnotations.add(HEALTH_GROUPS); healthAnnotations.add(WELLNESS); diff --git a/extensions/smallrye-health/deployment/src/test/java/io/quarkus/smallrye/health/test/ExpectedBeansUnitTest.java b/extensions/smallrye-health/deployment/src/test/java/io/quarkus/smallrye/health/test/ExpectedBeansUnitTest.java index ab5aea1fb279a..37a33ba11ce46 100644 --- a/extensions/smallrye-health/deployment/src/test/java/io/quarkus/smallrye/health/test/ExpectedBeansUnitTest.java +++ b/extensions/smallrye-health/deployment/src/test/java/io/quarkus/smallrye/health/test/ExpectedBeansUnitTest.java @@ -7,6 +7,7 @@ import org.eclipse.microprofile.health.HealthCheck; import org.eclipse.microprofile.health.Liveness; import org.eclipse.microprofile.health.Readiness; +import org.eclipse.microprofile.health.Startup; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.asset.EmptyAsset; import org.jboss.shrinkwrap.api.spec.JavaArchive; @@ -65,6 +66,9 @@ public void testHealthCheckMetadata() { selects = checks.select(Readiness.Literal.INSTANCE); Assertions.assertTrue(isUnique(selects)); + selects = checks.select(Startup.Literal.INSTANCE); + Assertions.assertTrue(isUnique(selects)); + selects = checks.select(HealthGroup.Literal.of("group1")); Assertions.assertTrue(isUnique(selects)); @@ -73,6 +77,7 @@ public void testHealthCheckMetadata() { selects = checks.select(Liveness.Literal.INSTANCE, Readiness.Literal.INSTANCE, + Startup.Literal.INSTANCE, HealthGroup.Literal.of("group1"), HealthGroup.Literal.of("group2")); Assertions.assertTrue(isUnique(selects)); diff --git a/extensions/smallrye-health/deployment/src/test/java/io/quarkus/smallrye/health/test/FailingHealthCheck.java b/extensions/smallrye-health/deployment/src/test/java/io/quarkus/smallrye/health/test/FailingHealthCheck.java index 25ef0fe463d4f..3347daf5257a7 100644 --- a/extensions/smallrye-health/deployment/src/test/java/io/quarkus/smallrye/health/test/FailingHealthCheck.java +++ b/extensions/smallrye-health/deployment/src/test/java/io/quarkus/smallrye/health/test/FailingHealthCheck.java @@ -10,12 +10,14 @@ import org.eclipse.microprofile.health.HealthCheckResponse; import org.eclipse.microprofile.health.Liveness; import org.eclipse.microprofile.health.Readiness; +import org.eclipse.microprofile.health.Startup; import io.smallrye.health.api.HealthGroup; @Dependent @Liveness @Readiness +@Startup @HealthGroup("group1") @HealthGroup("group2") public class FailingHealthCheck implements HealthCheck { diff --git a/extensions/smallrye-health/deployment/src/test/java/io/quarkus/smallrye/health/test/FailingUnitTest.java b/extensions/smallrye-health/deployment/src/test/java/io/quarkus/smallrye/health/test/FailingUnitTest.java index aad0dfc2ba741..544288c94be5f 100644 --- a/extensions/smallrye-health/deployment/src/test/java/io/quarkus/smallrye/health/test/FailingUnitTest.java +++ b/extensions/smallrye-health/deployment/src/test/java/io/quarkus/smallrye/health/test/FailingUnitTest.java @@ -35,6 +35,7 @@ public class FailingUnitTest { public void testHealthServlet() { RestAssured.when().get("/q/health/live").then().statusCode(503); RestAssured.when().get("/q/health/ready").then().statusCode(503); + RestAssured.when().get("/q/health/started").then().statusCode(503); RestAssured.when().get("/q/health").then().statusCode(503); } diff --git a/extensions/smallrye-health/deployment/src/test/java/io/quarkus/smallrye/health/test/HealthOpenAPITest.java b/extensions/smallrye-health/deployment/src/test/java/io/quarkus/smallrye/health/test/HealthOpenAPITest.java index 912f6fe7d498b..ab9615ee53067 100644 --- a/extensions/smallrye-health/deployment/src/test/java/io/quarkus/smallrye/health/test/HealthOpenAPITest.java +++ b/extensions/smallrye-health/deployment/src/test/java/io/quarkus/smallrye/health/test/HealthOpenAPITest.java @@ -32,6 +32,7 @@ public void testOpenApiPathAccessResource() { .header("Content-Type", "application/json;charset=UTF-8") .body("paths", Matchers.hasKey("/q/health/ready")) .body("paths", Matchers.hasKey("/q/health/live")) + .body("paths", Matchers.hasKey("/q/health/started")) .body("paths", Matchers.hasKey("/q/health")) .body("components.schemas.HealthCheckResponse.type", Matchers.equalTo("object")); diff --git a/extensions/smallrye-health/deployment/src/test/java/io/quarkus/smallrye/health/test/StartedHealthCheckTest.java b/extensions/smallrye-health/deployment/src/test/java/io/quarkus/smallrye/health/test/StartedHealthCheckTest.java new file mode 100644 index 0000000000000..0dd0e53420b74 --- /dev/null +++ b/extensions/smallrye-health/deployment/src/test/java/io/quarkus/smallrye/health/test/StartedHealthCheckTest.java @@ -0,0 +1,48 @@ +package io.quarkus.smallrye.health.test; + +import static io.restassured.RestAssured.when; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.is; + +import org.eclipse.microprofile.health.HealthCheck; +import org.eclipse.microprofile.health.HealthCheckResponse; +import org.eclipse.microprofile.health.Startup; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.restassured.RestAssured; +import io.restassured.parsing.Parser; + +public class StartedHealthCheckTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addClasses(StartupHC.class)); + + @Test + public void testStartup() { + try { + RestAssured.defaultParser = Parser.JSON; + when().get("/q/health/started").then() + .body("status", is("UP"), + "checks.status", contains("UP"), + "checks.name", contains(StartupHC.class.getName())); + } finally { + RestAssured.reset(); + } + } + + @Startup + static class StartupHC implements HealthCheck { + + @Override + public HealthCheckResponse call() { + return HealthCheckResponse.up(StartupHC.class.getName()); + } + } + +} diff --git a/extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/SmallRyeReactiveMessagingProcessor.java b/extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/SmallRyeReactiveMessagingProcessor.java index 539530a13aac3..6b3fb5996d6b2 100644 --- a/extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/SmallRyeReactiveMessagingProcessor.java +++ b/extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/SmallRyeReactiveMessagingProcessor.java @@ -80,6 +80,7 @@ import io.smallrye.reactive.messaging.extension.EmitterConfiguration; import io.smallrye.reactive.messaging.health.SmallRyeReactiveMessagingLivenessCheck; import io.smallrye.reactive.messaging.health.SmallRyeReactiveMessagingReadinessCheck; +import io.smallrye.reactive.messaging.health.SmallRyeReactiveMessagingStartupCheck; public class SmallRyeReactiveMessagingProcessor { @@ -346,6 +347,9 @@ public void enableHealth(ReactiveMessagingBuildTimeConfig buildTimeConfig, producer.produce( new HealthBuildItem(SmallRyeReactiveMessagingReadinessCheck.class.getName(), buildTimeConfig.healthEnabled)); + producer.produce( + new HealthBuildItem(SmallRyeReactiveMessagingStartupCheck.class.getName(), + buildTimeConfig.healthEnabled)); } @BuildStep