Skip to content

Commit 3d2eead

Browse files
authored
feat(spring-boot): added support WebFlux SpringBoot projects when generating liveness/readiness probes
SpringBootHealthCheckEnricher now considers WebFlux dependency while adding liveness and readiness probes. If user is depending on webflux dependency and is using `spring.webflux.base-path` property, it would automatically be picked up in probe endpoints Signed-off-by: Rohan Kumar <[email protected]>
1 parent a84777f commit 3d2eead

File tree

7 files changed

+119
-3
lines changed

7 files changed

+119
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Usage:
2121
./scripts/extract-changelog-for-version.sh 1.3.37 5
2222
```
2323
### 1.18-SNAPSHOT
24+
* Fix #1125: Support WebFlux SpringBoot projects when it comes to generate probes for actuators
2425

2526
### 1.17.0 (2024-08-13)
2627
* Fix #494: Support for Micronaut Framework Native Images

jkube-kit/common/src/main/java/org/eclipse/jkube/kit/common/util/SpringBootConfiguration.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public class SpringBootConfiguration {
3535
private String managementContextPath;
3636
private String actuatorBasePath;
3737
private String actuatorDefaultBasePath;
38+
private String webFluxBasePath;
3839
private boolean managementHealthProbesEnabled;
3940

4041
public static SpringBootConfiguration from(JavaProject project) {
@@ -63,7 +64,8 @@ public static SpringBootConfiguration from(JavaProject project) {
6364
.serverContextPath(properties.getProperty("server.context-path"))
6465
.managementContextPath(properties.getProperty("management.context-path"))
6566
.actuatorBasePath("")
66-
.actuatorDefaultBasePath("");
67+
.actuatorDefaultBasePath("")
68+
.webFluxBasePath(properties.getProperty("spring.webflux.base-path"));
6769
if (majorVersion > 1) {
6870
configBuilder
6971
.managementPort(Optional.ofNullable(properties.getProperty("management.server.port")).map(Integer::parseInt).orElse(null))

jkube-kit/common/src/main/java/org/eclipse/jkube/kit/common/util/SpringBootUtil.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public class SpringBootUtil {
3939
public static final String DEV_TOOLS_REMOTE_SECRET = "spring.devtools.remote.secret";
4040
public static final String DEV_TOOLS_REMOTE_SECRET_ENV = "SPRING_DEVTOOLS_REMOTE_SECRET";
4141

42+
private static final String SPRING_WEB_FLUX_ARTIFACT_ID = "spring-boot-starter-webflux";
4243
private static final String PLACEHOLDER_PREFIX = "${";
4344
private static final String PLACEHOLDER_SUFFIX = "}";
4445
private static final String VALUE_SEPARATOR = ":";
@@ -149,5 +150,9 @@ public static File findNativeArtifactFile(JavaProject project) {
149150
}
150151
return null;
151152
}
153+
154+
public static boolean hasSpringWebFluxDependency(JavaProject javaProject) {
155+
return JKubeProjectUtil.hasDependency(javaProject, SPRING_BOOT_GROUP_ID, SPRING_WEB_FLUX_ARTIFACT_ID);
156+
}
152157
}
153158

jkube-kit/common/src/test/java/org/eclipse/jkube/kit/common/util/SpringBootUtilTest.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,29 @@ void findNativeArtifactFile_whenNativeExecutableInStandardGradleNativeDirectory_
389389
assertThat(nativeArtifactFound).hasName("sample");
390390
}
391391

392+
@Test
393+
void hasSpringWebFluxDependency_whenWebFluxDependencyPresent_thenReturnTrue() {
394+
// Given
395+
JavaProject javaProject = JavaProject.builder().dependency(Dependency.builder()
396+
.groupId("org.springframework.boot")
397+
.artifactId("spring-boot-starter-webflux")
398+
.build())
399+
.build();
400+
// When + Then
401+
assertThat(SpringBootUtil.hasSpringWebFluxDependency(javaProject)).isTrue();
402+
}
403+
404+
@Test
405+
void hasSpringWebFluxDependency_whenNoWebFluxDependencyPresent_thenReturnFalse() {
406+
// Given
407+
JavaProject javaProject = JavaProject.builder().dependency(Dependency.builder()
408+
.groupId("org.springframework.boot")
409+
.artifactId("spring-boot-starter-web")
410+
.build()).build();
411+
// When + Then
412+
assertThat(SpringBootUtil.hasSpringWebFluxDependency(javaProject)).isFalse();
413+
}
414+
392415
static URLClassLoader createClassLoader(File temporaryFolder, String resource) throws IOException {
393416
File applicationProp = new File(Objects.requireNonNull(SpringBootUtilTest.class.getResource(resource)).getPath());
394417
File classesInTarget = new File(new File(temporaryFolder, "target"), "classes");

jkube-kit/doc/src/main/asciidoc/inc/enricher/spring-boot-healthcheck/_jkube_healthcheck_spring_boot.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ ifeval::["{plugin-type}" == "gradle"]
1212
include::gradle/_actuator_dependency.adoc[]
1313
endif::[]
1414

15+
If you're using Spring Boot WebFlux, this enricher would automatically read `spring.webflux.base-path` property to infer base path for health check endpoints.
16+
1517
The enricher will try to discover the settings from the `application.properties` / `application.yaml` Spring Boot configuration file.
1618

1719
`/actuator/health` is the default endpoint for the liveness and readiness probes.

jkube-kit/jkube-kit-spring-boot/src/main/java/org/eclipse/jkube/springboot/enricher/SpringBootHealthCheckEnricher.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
import org.eclipse.jkube.kit.enricher.specific.AbstractHealthCheckEnricher;
2525
import org.apache.commons.lang3.StringUtils;
2626

27+
import static org.eclipse.jkube.kit.common.util.SpringBootUtil.hasSpringWebFluxDependency;
28+
2729
/**
2830
* Enriches spring-boot containers with health checks if the actuator module is present.
2931
*/
@@ -112,8 +114,13 @@ protected Probe buildProbe(Integer initialDelay, Integer period, Integer timeout
112114
springBootConfiguration.getManagementContextPath() : "";
113115
} else {
114116
scheme = StringUtils.isNotBlank(springBootConfiguration.getServerKeystore()) ? SCHEME_HTTPS : SCHEME_HTTP;
115-
prefix = StringUtils.isNotBlank(springBootConfiguration.getServerContextPath()) ?
116-
springBootConfiguration.getServerContextPath() : "";
117+
if (hasSpringWebFluxDependency(getContext().getProject()) && StringUtils.isNotBlank(springBootConfiguration.getWebFluxBasePath())) {
118+
prefix = springBootConfiguration.getWebFluxBasePath();
119+
} else if (StringUtils.isNotBlank(springBootConfiguration.getServerContextPath())) {
120+
prefix = springBootConfiguration.getServerContextPath();
121+
} else {
122+
prefix = "";
123+
}
117124
prefix += StringUtils.isNotBlank(springBootConfiguration.getServletPath()) ?
118125
springBootConfiguration.getServletPath() : "";
119126
prefix += StringUtils.isNotBlank(springBootConfiguration.getManagementContextPath()) ?

jkube-kit/jkube-kit-spring-boot/src/test/java/org/eclipse/jkube/springboot/enricher/AbstractSpringBootHealthCheckEnricherTestSupport.java

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@
3030
import org.eclipse.jkube.kit.config.resource.ProcessorConfig;
3131
import org.eclipse.jkube.kit.enricher.api.JKubeEnricherContext;
3232
import org.eclipse.jkube.kit.common.util.ProjectClassLoaders;
33+
import org.junit.jupiter.api.AfterEach;
3334
import org.junit.jupiter.api.BeforeEach;
35+
import org.junit.jupiter.api.DisplayName;
36+
import org.junit.jupiter.api.Nested;
3437
import org.junit.jupiter.api.Test;
3538
import org.junit.jupiter.api.io.TempDir;
3639

@@ -701,6 +704,79 @@ void testLivenessAndReadinessProbesForDefaultPath_whenManagementHealthProbesEnab
701704
assertHTTPGetPathAndPort(readinessProbe,getActuatorDefaultBasePath() + "/health",8080);
702705
}
703706

707+
@Nested
708+
@DisplayName("Spring Web Flux Dependency present")
709+
class SpringWebFlux {
710+
@BeforeEach
711+
void setup() {
712+
context.getProject().setDependencies(Collections.singletonList(Dependency.builder()
713+
.groupId("org.springframework.boot")
714+
.artifactId("spring-boot-starter-webflux")
715+
.version(getSpringBootVersion())
716+
.build()));
717+
when(context.getProjectClassLoaders().isClassInCompileClasspath(true, REQUIRED_CLASSES))
718+
.thenReturn(true);
719+
}
720+
721+
@Nested
722+
@DisplayName("spring.webflux.base-path property configured")
723+
class SpringWebFluxBasePathConfigured {
724+
@BeforeEach
725+
void setUp() {
726+
props.put("spring.webflux.base-path", "/webflux");
727+
}
728+
729+
@AfterEach
730+
void tearDown() {
731+
props.clear();
732+
}
733+
734+
@Test
735+
@DisplayName("when management server sharing main server port, then liveness, readiness probes use web flux base path in endpoints")
736+
void whenProbesGenerated_thenProbesAddWebFluxBasePath() {
737+
// Given
738+
props.put("management.port", "8383");
739+
props.put("management.server.port", "8383");
740+
writeProps();
741+
SpringBootHealthCheckEnricher enricher = new SpringBootHealthCheckEnricher(context);
742+
// When
743+
Probe livenessProbe = enricher.getLivenessProbe();
744+
Probe readinessProbe = enricher.getReadinessProbe();
745+
// Then
746+
assertHTTPGetPathAndPort(livenessProbe, getActuatorDefaultBasePath() + "/health",8383);
747+
assertHTTPGetPathAndPort(readinessProbe,getActuatorDefaultBasePath() + "/health",8383);
748+
}
749+
750+
@Test
751+
@DisplayName("when a separate management server port configured, then spring.webflux.base-path is ignored")
752+
void whenManagementServerRunningOnDifferentPort_thenProbesDoNotUseWebFluxBasePath() {
753+
// Given
754+
writeProps();
755+
SpringBootHealthCheckEnricher enricher = new SpringBootHealthCheckEnricher(context);
756+
// When
757+
Probe livenessProbe = enricher.getLivenessProbe();
758+
Probe readinessProbe = enricher.getReadinessProbe();
759+
// Then
760+
assertHTTPGetPathAndPort(livenessProbe,"/webflux" + getActuatorDefaultBasePath() + "/health",8080);
761+
assertHTTPGetPathAndPort(readinessProbe,"/webflux" + getActuatorDefaultBasePath() + "/health",8080);
762+
}
763+
}
764+
765+
@Test
766+
@DisplayName("when no explicit base path configured, then use default base path")
767+
void whenManagementServerRunningOnDifferentPort_thenProbesDoNotUseWebFluxBasePath() {
768+
// Given
769+
writeProps();
770+
SpringBootHealthCheckEnricher enricher = new SpringBootHealthCheckEnricher(context);
771+
// When
772+
Probe livenessProbe = enricher.getLivenessProbe();
773+
Probe readinessProbe = enricher.getReadinessProbe();
774+
// Then
775+
assertHTTPGetPathAndPort(livenessProbe,getActuatorDefaultBasePath() + "/health",8080);
776+
assertHTTPGetPathAndPort(readinessProbe,getActuatorDefaultBasePath() + "/health",8080);
777+
}
778+
}
779+
704780
private void assertHTTPGetPathAndPort(Probe probe, String path, int port) {
705781
assertThat(probe).isNotNull()
706782
.extracting(Probe::getHttpGet).isNotNull()

0 commit comments

Comments
 (0)