Skip to content

Commit

Permalink
feat (jkube-kit/quarkus): Add startup probe in QuarkusHealthCheckEnri…
Browse files Browse the repository at this point in the history
…cher (#1468)

Signed-off-by: Anurag Singh Rajawat <anuragsinghrajawat22@gmail.com>
  • Loading branch information
anurag-rajawat authored and manusa committed Jul 6, 2022
1 parent 698fb4c commit be63f1f
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 24 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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"]
Expand All @@ -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.

Expand All @@ -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"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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() {}

Expand Down Expand Up @@ -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) ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
}

Expand All @@ -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<JavaProject, String> pathResolver) {
if (!getContext().hasDependency(QUARKUS_GROUP_ID, "quarkus-smallrye-health")) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@

import static org.assertj.core.api.Assertions.assertThat;

@SuppressWarnings("ResultOfMethodCallIgnored")
@SuppressWarnings({"ResultOfMethodCallIgnored", "unused"})
public class QuarkusModeTest {

@Rule
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down Expand Up @@ -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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -52,7 +53,6 @@ public class QuarkusHealthCheckEnricherTest {
private ProcessorConfig processorConfig;
private KubernetesListBuilder klb;

@SuppressWarnings("ResultOfMethodCallIgnored")
@Before
public void setUp() {
properties = new Properties();
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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<?, List<? extends Container>, Container, ObjectAssert<Container>> assertContainers(
Expand All @@ -257,15 +282,15 @@ private AbstractListAssert<?, List<? extends Container>, 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() {{
Expand Down

0 comments on commit be63f1f

Please sign in to comment.