From be63f1f582c09021bb5feeb0ab7cc344fb523df9 Mon Sep 17 00:00:00 2001 From: Anurag Singh Rajawat Date: Fri, 27 May 2022 12:46:23 +0530 Subject: [PATCH] feat (jkube-kit/quarkus): Add startup probe in QuarkusHealthCheckEnricher (#1468) Signed-off-by: Anurag Singh Rajawat --- CHANGELOG.md | 2 +- .../enricher/_jkube_healthcheck_quarkus.adoc | 10 ++- .../eclipse/jkube/quarkus/QuarkusUtils.java | 13 ++++ .../enricher/QuarkusHealthCheckEnricher.java | 11 +++- .../jkube/quarkus/QuarkusModeTest.java | 2 +- .../jkube/quarkus/QuarkusUtilsTest.java | 14 +++++ .../QuarkusHealthCheckEnricherTest.java | 61 +++++++++++++------ 7 files changed, 89 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 212848ec64..c13ae1bfbf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,7 +22,7 @@ Usage: ``` ### 1.9.0-SNAPSHOT * Fix #1279: Remove redundant log messages regarding plugin modes - +* Fix #1468: Add startup probe in QuarkusHealthCheckEnricher ### 1.8.0 (2022-05-24) * Fix #1188: Add support for specifying multiple tags in Zero configuration mode diff --git a/jkube-kit/doc/src/main/asciidoc/inc/enricher/_jkube_healthcheck_quarkus.adoc b/jkube-kit/doc/src/main/asciidoc/inc/enricher/_jkube_healthcheck_quarkus.adoc index 6e07205449..31ce652b03 100644 --- a/jkube-kit/doc/src/main/asciidoc/inc/enricher/_jkube_healthcheck_quarkus.adoc +++ b/jkube-kit/doc/src/main/asciidoc/inc/enricher/_jkube_healthcheck_quarkus.adoc @@ -1,7 +1,7 @@ [[jkube-healthcheck-quarkus]] ==== jkube-healthcheck-quarkus -This enricher adds kubernetes readiness and liveness probes with Quarkus. This requires the following dependency +This enricher adds kubernetes _readiness_, _liveness_ and _startup_ probes with Quarkus. This requires the following dependency to be added to your Quarkus project: [source,xml,indent=0,subs="verbatim,quotes,attributes"] @@ -18,7 +18,9 @@ configuration file. JKube uses the following properties to resolve the health ch * `quarkus.http.root-path`: Quarkus Application root path. * `quarkus.http.non-application-root-path`: This property got introduced in recent versions of Quarkus(2.x) for non application endpoints. * `quarkus.smallrye-health.root-path`: The location of the all-encompassing health endpoint. +* `quarkus.smallrye-health.readiness-path`: The location of the readiness endpoint. * `quarkus.smallrye-health.liveness-path`: The location of the liveness endpoint. +* `quarkus.smallrye-health.startup-path`: The location of the startup endpoint. **Note**: Please note that behavior of these properties seem to have changed since Quarkus 1.11.x versions (e.g for health and liveness paths leading slashes are now being considered). `{plugin}` would also check quarkus version along with value of these properties in order to resolve effective health endpoints. @@ -31,7 +33,11 @@ The enricher will use the following settings by default: * `failureThreshold` : `3` * `successThreshold` : `1` * `livenessInitialDelay` : `10` -* `readinessIntialDelay` : `5` +* `readinessInitialDelay` : `5` +* `startupInitialDelay` : `5` +* `livenessPath` : `q/health/live` +* `readinessPath` : `q/health/ready` +* `startupPath` : `q/health/started` These values can be configured by the enricher in the `{plugin}` configuration as shown below: [source,xml,indent=0,subs="verbatim,quotes,attributes"] diff --git a/jkube-kit/jkube-kit-quarkus/src/main/java/org/eclipse/jkube/quarkus/QuarkusUtils.java b/jkube-kit/jkube-kit-quarkus/src/main/java/org/eclipse/jkube/quarkus/QuarkusUtils.java index eb97e59ba5..30724bfa51 100644 --- a/jkube-kit/jkube-kit-quarkus/src/main/java/org/eclipse/jkube/quarkus/QuarkusUtils.java +++ b/jkube-kit/jkube-kit-quarkus/src/main/java/org/eclipse/jkube/quarkus/QuarkusUtils.java @@ -45,6 +45,7 @@ public class QuarkusUtils { private static final String QUARKUS_SMALLRYE_HEALTH_ROOT_PATH = "quarkus.smallrye-health.root-path"; private static final String QUARKUS_SMALLRYE_HEALTH_READINESS_PATH = "quarkus.smallrye-health.readiness-path"; private static final String QUARKUS_SMALLRYE_HEALTH_LIVENESS_PATH = "quarkus.smallrye-health.liveness-path"; + private static final String QUARKUS_SMALLRYE_HEALTH_STARTUP_PATH = "quarkus.smallrye-health.startup-path"; private static final String QUARKUS_MAVEN_PLUGIN_ARTIFACTID = "quarkus-maven-plugin"; private static final int QUARKUS_MAJOR_VERSION_SINCE_PATH_RESOLUTION_CHANGE = 1; private static final int QUARKUS_MINOR_VERSION_SINCE_PATH_RESOLUTION_CHANGE = 11; @@ -56,6 +57,7 @@ public class QuarkusUtils { private static final String DEFAULT_HEALTH_ROOT_PATH = "health"; private static final String DEFAULT_READINESS_SUBPATH = "ready"; private static final String DEFAULT_LIVENESS_SUBPATH = "live"; + private static final String DEFAULT_STARTUP_SUBPATH = "started"; private QuarkusUtils() {} @@ -143,6 +145,17 @@ public static String resolveQuarkusLivenessPath(JavaProject javaProject) { .getProperty(QUARKUS_SMALLRYE_HEALTH_LIVENESS_PATH, DEFAULT_LIVENESS_SUBPATH); } + /** + * Get Quarkus SmallRye Health startup path from Quarkus Configuration + * + * @param javaProject {@link JavaProject} project for which health startup path is required + * @return a string containing the startup path + */ + public static String resolveQuarkusStartupPath(JavaProject javaProject) { + return getQuarkusConfiguration(javaProject) + .getProperty(QUARKUS_SMALLRYE_HEALTH_STARTUP_PATH, DEFAULT_STARTUP_SUBPATH); + } + public static boolean hasQuarkusPlugin(JavaProject javaProject) { return JKubeProjectUtil.hasPlugin(javaProject, QUARKUS_GROUP_ID, QUARKUS_MAVEN_PLUGIN_ARTIFACTID) || JKubeProjectUtil.hasPlugin(javaProject, QUARKUS_PLATFORM_GROUP_ID, QUARKUS_MAVEN_PLUGIN_ARTIFACTID) || diff --git a/jkube-kit/jkube-kit-quarkus/src/main/java/org/eclipse/jkube/quarkus/enricher/QuarkusHealthCheckEnricher.java b/jkube-kit/jkube-kit-quarkus/src/main/java/org/eclipse/jkube/quarkus/enricher/QuarkusHealthCheckEnricher.java index 5a23c151d5..deb731766a 100644 --- a/jkube-kit/jkube-kit-quarkus/src/main/java/org/eclipse/jkube/quarkus/enricher/QuarkusHealthCheckEnricher.java +++ b/jkube-kit/jkube-kit-quarkus/src/main/java/org/eclipse/jkube/quarkus/enricher/QuarkusHealthCheckEnricher.java @@ -49,7 +49,8 @@ private enum Config implements Configs.Config { FAILURE_THRESHOLD("failureThreshold", "3"), SUCCESS_THRESHOLD("successThreshold", "1"), LIVENESS_INITIAL_DELAY("livenessInitialDelay", null), - READINESS_INTIAL_DELAY("readinessIntialDelay", null), + READINESS_INITIAL_DELAY("readinessInitialDelay", null), + STARTUP_INITIAL_DELAY("startupInitialDelay", null), HEALTH_PATH("path", null); @Getter @@ -60,7 +61,7 @@ private enum Config implements Configs.Config { @Override protected Probe getReadinessProbe() { - return discoverQuarkusHealthCheck(asInteger(getConfig(Config.READINESS_INTIAL_DELAY, "5")), + return discoverQuarkusHealthCheck(asInteger(getConfig(Config.READINESS_INITIAL_DELAY, "5")), QuarkusUtils::resolveQuarkusReadinessPath); } @@ -69,6 +70,12 @@ protected Probe getLivenessProbe() { return discoverQuarkusHealthCheck(asInteger(getConfig(Config.LIVENESS_INITIAL_DELAY, "10")), QuarkusUtils::resolveQuarkusLivenessPath); } + + @Override + protected Probe getStartupProbe() { + return discoverQuarkusHealthCheck(asInteger(getConfig(Config.STARTUP_INITIAL_DELAY, "5")), + QuarkusUtils::resolveQuarkusStartupPath); + } private Probe discoverQuarkusHealthCheck(int initialDelay, Function pathResolver) { if (!getContext().hasDependency(QUARKUS_GROUP_ID, "quarkus-smallrye-health")) { diff --git a/jkube-kit/jkube-kit-quarkus/src/test/java/org/eclipse/jkube/quarkus/QuarkusModeTest.java b/jkube-kit/jkube-kit-quarkus/src/test/java/org/eclipse/jkube/quarkus/QuarkusModeTest.java index 6722af62fd..09ae4f1172 100644 --- a/jkube-kit/jkube-kit-quarkus/src/test/java/org/eclipse/jkube/quarkus/QuarkusModeTest.java +++ b/jkube-kit/jkube-kit-quarkus/src/test/java/org/eclipse/jkube/quarkus/QuarkusModeTest.java @@ -31,7 +31,7 @@ import static org.assertj.core.api.Assertions.assertThat; -@SuppressWarnings("ResultOfMethodCallIgnored") +@SuppressWarnings({"ResultOfMethodCallIgnored", "unused"}) public class QuarkusModeTest { @Rule diff --git a/jkube-kit/jkube-kit-quarkus/src/test/java/org/eclipse/jkube/quarkus/QuarkusUtilsTest.java b/jkube-kit/jkube-kit-quarkus/src/test/java/org/eclipse/jkube/quarkus/QuarkusUtilsTest.java index 24c5a61e47..291735e47e 100644 --- a/jkube-kit/jkube-kit-quarkus/src/test/java/org/eclipse/jkube/quarkus/QuarkusUtilsTest.java +++ b/jkube-kit/jkube-kit-quarkus/src/test/java/org/eclipse/jkube/quarkus/QuarkusUtilsTest.java @@ -35,6 +35,7 @@ import static org.eclipse.jkube.quarkus.QuarkusUtils.getQuarkusConfiguration; import static org.eclipse.jkube.quarkus.QuarkusUtils.resolveCompleteQuarkusHealthRootPath; import static org.eclipse.jkube.quarkus.QuarkusUtils.resolveQuarkusLivenessPath; +import static org.eclipse.jkube.quarkus.QuarkusUtils.resolveQuarkusStartupPath; public class QuarkusUtilsTest { @@ -282,6 +283,19 @@ public void resolveQuarkusLivenessPath_withLivenessPathSet_shouldReturnValidPath assertThat(resolvedHealthPath).isNotEmpty().isEqualTo("liveness"); } + @Test + public void resolveQuarkusStartupPath_withStartupPathSet_shouldReturnValidPath() { + // Given + Properties properties = new Properties(); + properties.setProperty("quarkus.smallrye-health.startup-path", "startup"); + javaProject.setProperties(properties); + // When + String resolvedStartupPath = resolveQuarkusStartupPath(javaProject); + // Then + assertThat(resolvedStartupPath).isNotEmpty() + .isEqualTo("startup"); + } + @Test public void concatPath_withEmptyRootAndPrefixed() { assertThat(concatPath("/", "/liveness")).isEqualTo("/liveness"); diff --git a/jkube-kit/jkube-kit-quarkus/src/test/java/org/eclipse/jkube/quarkus/enricher/QuarkusHealthCheckEnricherTest.java b/jkube-kit/jkube-kit-quarkus/src/test/java/org/eclipse/jkube/quarkus/enricher/QuarkusHealthCheckEnricherTest.java index 08b55c8a6d..8f84642c2f 100644 --- a/jkube-kit/jkube-kit-quarkus/src/test/java/org/eclipse/jkube/quarkus/enricher/QuarkusHealthCheckEnricherTest.java +++ b/jkube-kit/jkube-kit-quarkus/src/test/java/org/eclipse/jkube/quarkus/enricher/QuarkusHealthCheckEnricherTest.java @@ -40,6 +40,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.tuple; +@SuppressWarnings({"ResultOfMethodCallIgnored", "unused"}) public class QuarkusHealthCheckEnricherTest { @Mocked @@ -52,7 +53,6 @@ public class QuarkusHealthCheckEnricherTest { private ProcessorConfig processorConfig; private KubernetesListBuilder klb; - @SuppressWarnings("ResultOfMethodCallIgnored") @Before public void setUp() { properties = new Properties(); @@ -90,7 +90,7 @@ public void create_withCustomPath_shouldReturnCustomPath() { // When new QuarkusHealthCheckEnricher(context).create(PlatformMode.kubernetes, klb); // Then - assertLivenessReadinessProbes(klb, tuple("HTTP", "/my-custom-path/live", "HTTP", "/my-custom-path/ready")); + assertLivenessReadinessStartupProbes(klb, tuple("HTTP", "/my-custom-path/live", "HTTP", "/my-custom-path/ready", "HTTP", "/my-custom-path/started")); } @Test @@ -101,7 +101,7 @@ public void create_withDefaultsAndQuarkus1_shouldReturnDefaults() { // When new QuarkusHealthCheckEnricher(context).create(PlatformMode.kubernetes, klb); // Then - assertLivenessReadinessProbes(klb, tuple("HTTP", "/health/live", "HTTP", "/health/ready")); + assertLivenessReadinessStartupProbes(klb, tuple("HTTP", "/health/live", "HTTP", "/health/ready", "HTTP", "/health/started")); } @Test @@ -112,7 +112,7 @@ public void create_withDefaultsAndQuarkus2_shouldReturnPrefixedDefaults() { // When new QuarkusHealthCheckEnricher(context).create(PlatformMode.kubernetes, klb); // Then - assertLivenessReadinessProbes(klb, tuple("HTTP", "/q/health/live", "HTTP", "/q/health/ready")); + assertLivenessReadinessStartupProbes(klb, tuple("HTTP", "/q/health/live", "HTTP", "/q/health/ready", "HTTP", "/q/health/started")); } @Test @@ -125,10 +125,11 @@ public void create_withQuarkus2AndApplicationProperties_shouldReturnCustomizedPa properties.put("quarkus.smallrye-health.root-path", "health"); properties.put("quarkus.smallrye-health.readiness-path", "im-ready"); properties.put("quarkus.smallrye-health.liveness-path", "im-alive"); + properties.put("quarkus.smallrye-health.startup-path", "im-started"); // When new QuarkusHealthCheckEnricher(context).create(PlatformMode.kubernetes, klb); // Then - assertLivenessReadinessProbes(klb, tuple("HTTP", "/not-app/health/im-alive", "HTTP", "/not-app/health/im-ready")); + assertLivenessReadinessStartupProbes(klb, tuple("HTTP", "/not-app/health/im-alive", "HTTP", "/not-app/health/im-ready", "HTTP", "/not-app/health/im-started")); } @Test @@ -143,7 +144,7 @@ public void create_withQuarkus2AndCustomRootProperty_shouldReturnCustomizedPaths // When new QuarkusHealthCheckEnricher(context).create(PlatformMode.kubernetes, klb); // Then - assertLivenessReadinessProbes(klb, tuple("HTTP", "/root/q/health/liveness", "HTTP", "/root/q/health/ready")); + assertLivenessReadinessStartupProbes(klb, tuple("HTTP", "/root/q/health/liveness", "HTTP", "/root/q/health/ready", "HTTP", "/root/q/health/started")); } @Test @@ -158,7 +159,7 @@ public void create_withQuarkus2AndAbsoluteNonApplicationRootPathProperty_shouldR // When new QuarkusHealthCheckEnricher(context).create(PlatformMode.kubernetes, klb); // Then - assertLivenessReadinessProbes(klb, tuple("HTTP", "/absolute/health/liveness", "HTTP", "/absolute/health/ready")); + assertLivenessReadinessStartupProbes(klb, tuple("HTTP", "/absolute/health/liveness", "HTTP", "/absolute/health/ready", "HTTP", "/absolute/health/started")); } @Test @@ -170,7 +171,7 @@ public void create_withQuarkus3AndAbsoluteLivenessPathProperty_shouldReturnAbsol // When new QuarkusHealthCheckEnricher(context).create(PlatformMode.kubernetes, klb); // Then - assertLivenessReadinessProbes(klb, tuple("HTTP", "/absolute/liveness", "HTTP", "/q/health/ready")); + assertLivenessReadinessStartupProbes(klb, tuple("HTTP", "/absolute/liveness", "HTTP", "/q/health/ready", "HTTP", "/q/health/started")); } @Test @@ -182,7 +183,7 @@ public void create_withQuarkus3AndAbsoluteReadinessPathProperty_shouldReturnAbso // When new QuarkusHealthCheckEnricher(context).create(PlatformMode.kubernetes, klb); // Then - assertLivenessReadinessProbes(klb, tuple("HTTP", "/q/health/live", "HTTP", "/absolute/readiness")); + assertLivenessReadinessStartupProbes(klb, tuple("HTTP", "/q/health/live", "HTTP", "/absolute/readiness", "HTTP", "/q/health/started")); } @Test @@ -194,7 +195,7 @@ public void create_withQuarkus1_0AndAbsoluteLivenessPathProperty_shouldIgnoreLea // When new QuarkusHealthCheckEnricher(context).create(PlatformMode.kubernetes, klb); // Then - assertLivenessReadinessProbes(klb, tuple("HTTP", "/health/absolute/liveness", "HTTP", "/health/ready")); + assertLivenessReadinessStartupProbes(klb, tuple("HTTP", "/health/absolute/liveness", "HTTP", "/health/ready", "HTTP", "/health/started")); } @Test @@ -206,7 +207,7 @@ public void create_withQuarkus1_0AndAbsoluteReadinessPathProperty_shouldIgnoreLe // When new QuarkusHealthCheckEnricher(context).create(PlatformMode.kubernetes, klb); // Then - assertLivenessReadinessProbes(klb, tuple("HTTP", "/health/live", "HTTP", "/health/absolute/readiness")); + assertLivenessReadinessStartupProbes(klb, tuple("HTTP", "/health/live", "HTTP", "/health/absolute/readiness", "HTTP", "/health/started")); } @Test @@ -220,7 +221,7 @@ public void create_withQuarkus2AndAbsoluteHealthPathProperty_shouldReturnCustomi // When new QuarkusHealthCheckEnricher(context).create(PlatformMode.kubernetes, klb); // Then - assertLivenessReadinessProbes(klb, tuple("HTTP", "/absolute/health/live", "HTTP", "/absolute/health/ready")); + assertLivenessReadinessStartupProbes(klb, tuple("HTTP", "/absolute/health/live", "HTTP", "/absolute/health/ready", "HTTP", "/absolute/health/started")); } @Test @@ -233,7 +234,31 @@ public void create_withQuarkus1_0AndAbsoluteHealthPathProperty_shouldIgnoreLeadi // When new QuarkusHealthCheckEnricher(context).create(PlatformMode.kubernetes, klb); // Then - assertLivenessReadinessProbes(klb, tuple("HTTP", "/not-ignored/absolute/live", "HTTP", "/not-ignored/absolute/ready")); + assertLivenessReadinessStartupProbes(klb, tuple("HTTP", "/not-ignored/absolute/live", "HTTP", "/not-ignored/absolute/ready", "HTTP", "/not-ignored/absolute/started")); + } + + @Test + public void create_withQuarkus1_0AndAbsoluteStartupPathProperty_shouldIgnoreLeadingSlash() { + // Given + withSmallryeDependency(); + withQuarkus("1.0.0.Final"); + properties.put("quarkus.smallrye-health.startup-path", "/absolute/startup"); + // When + new QuarkusHealthCheckEnricher(context).create(PlatformMode.kubernetes, klb); + // Then + assertLivenessReadinessStartupProbes(klb, tuple("HTTP", "/health/live", "HTTP", "/health/ready", "HTTP", "/health/absolute/startup")); + } + + @Test + public void create_withQuarkus3_AndAbsoluteStartupPathProperty_shouldReturnAbsoluteCustomizedPaths() { + // Given + withSmallryeDependency(); + withQuarkus("3.333.3.Final"); + properties.put("quarkus.smallrye-health.startup-path", "/absolute/startup"); + // When + new QuarkusHealthCheckEnricher(context).create(PlatformMode.kubernetes, klb); + // Then + assertLivenessReadinessStartupProbes(klb, tuple("HTTP", "/q/health/live", "HTTP", "/q/health/ready", "HTTP", "/absolute/startup")); } @Test @@ -243,8 +268,8 @@ public void create_withNoSmallrye_shouldNotAddProbes() { // Then assertContainers(klb) .extracting( - "livenessProbe", "readinessProbe") - .containsExactly(tuple(null, null)); + "livenessProbe", "readinessProbe", "startupProbe") + .containsExactly(tuple(null, null, null)); } private AbstractListAssert, Container, ObjectAssert> assertContainers( @@ -257,15 +282,15 @@ private AbstractListAssert, Container, ObjectAssert .flatExtracting(PodSpec::getContainers); } - private void assertLivenessReadinessProbes(KubernetesListBuilder kubernetesListBuilder, Tuple... values) { + private void assertLivenessReadinessStartupProbes(KubernetesListBuilder kubernetesListBuilder, Tuple... values) { assertContainers(kubernetesListBuilder) .extracting( "livenessProbe.httpGet.scheme", "livenessProbe.httpGet.path", - "readinessProbe.httpGet.scheme", "readinessProbe.httpGet.path") + "readinessProbe.httpGet.scheme", "readinessProbe.httpGet.path", + "startupProbe.httpGet.scheme", "startupProbe.httpGet.path") .containsExactly(values); } - @SuppressWarnings("ResultOfMethodCallIgnored") private void withQuarkus(String version) { // @formatter:off new Expectations() {{