Skip to content

Commit

Permalink
feat(spring-boot): added support WebFlux SpringBoot projects when gen…
Browse files Browse the repository at this point in the history
…erating 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]>
  • Loading branch information
rohanKanojia authored Aug 29, 2024
1 parent a84777f commit 3d2eead
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 3 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Usage:
./scripts/extract-changelog-for-version.sh 1.3.37 5
```
### 1.18-SNAPSHOT
* Fix #1125: Support WebFlux SpringBoot projects when it comes to generate probes for actuators

### 1.17.0 (2024-08-13)
* Fix #494: Support for Micronaut Framework Native Images
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public class SpringBootConfiguration {
private String managementContextPath;
private String actuatorBasePath;
private String actuatorDefaultBasePath;
private String webFluxBasePath;
private boolean managementHealthProbesEnabled;

public static SpringBootConfiguration from(JavaProject project) {
Expand Down Expand Up @@ -63,7 +64,8 @@ public static SpringBootConfiguration from(JavaProject project) {
.serverContextPath(properties.getProperty("server.context-path"))
.managementContextPath(properties.getProperty("management.context-path"))
.actuatorBasePath("")
.actuatorDefaultBasePath("");
.actuatorDefaultBasePath("")
.webFluxBasePath(properties.getProperty("spring.webflux.base-path"));
if (majorVersion > 1) {
configBuilder
.managementPort(Optional.ofNullable(properties.getProperty("management.server.port")).map(Integer::parseInt).orElse(null))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public class SpringBootUtil {
public static final String DEV_TOOLS_REMOTE_SECRET = "spring.devtools.remote.secret";
public static final String DEV_TOOLS_REMOTE_SECRET_ENV = "SPRING_DEVTOOLS_REMOTE_SECRET";

private static final String SPRING_WEB_FLUX_ARTIFACT_ID = "spring-boot-starter-webflux";
private static final String PLACEHOLDER_PREFIX = "${";
private static final String PLACEHOLDER_SUFFIX = "}";
private static final String VALUE_SEPARATOR = ":";
Expand Down Expand Up @@ -149,5 +150,9 @@ public static File findNativeArtifactFile(JavaProject project) {
}
return null;
}

public static boolean hasSpringWebFluxDependency(JavaProject javaProject) {
return JKubeProjectUtil.hasDependency(javaProject, SPRING_BOOT_GROUP_ID, SPRING_WEB_FLUX_ARTIFACT_ID);
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,29 @@ void findNativeArtifactFile_whenNativeExecutableInStandardGradleNativeDirectory_
assertThat(nativeArtifactFound).hasName("sample");
}

@Test
void hasSpringWebFluxDependency_whenWebFluxDependencyPresent_thenReturnTrue() {
// Given
JavaProject javaProject = JavaProject.builder().dependency(Dependency.builder()
.groupId("org.springframework.boot")
.artifactId("spring-boot-starter-webflux")
.build())
.build();
// When + Then
assertThat(SpringBootUtil.hasSpringWebFluxDependency(javaProject)).isTrue();
}

@Test
void hasSpringWebFluxDependency_whenNoWebFluxDependencyPresent_thenReturnFalse() {
// Given
JavaProject javaProject = JavaProject.builder().dependency(Dependency.builder()
.groupId("org.springframework.boot")
.artifactId("spring-boot-starter-web")
.build()).build();
// When + Then
assertThat(SpringBootUtil.hasSpringWebFluxDependency(javaProject)).isFalse();
}

static URLClassLoader createClassLoader(File temporaryFolder, String resource) throws IOException {
File applicationProp = new File(Objects.requireNonNull(SpringBootUtilTest.class.getResource(resource)).getPath());
File classesInTarget = new File(new File(temporaryFolder, "target"), "classes");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ ifeval::["{plugin-type}" == "gradle"]
include::gradle/_actuator_dependency.adoc[]
endif::[]

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.

The enricher will try to discover the settings from the `application.properties` / `application.yaml` Spring Boot configuration file.

`/actuator/health` is the default endpoint for the liveness and readiness probes.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
import org.eclipse.jkube.kit.enricher.specific.AbstractHealthCheckEnricher;
import org.apache.commons.lang3.StringUtils;

import static org.eclipse.jkube.kit.common.util.SpringBootUtil.hasSpringWebFluxDependency;

/**
* Enriches spring-boot containers with health checks if the actuator module is present.
*/
Expand Down Expand Up @@ -112,8 +114,13 @@ protected Probe buildProbe(Integer initialDelay, Integer period, Integer timeout
springBootConfiguration.getManagementContextPath() : "";
} else {
scheme = StringUtils.isNotBlank(springBootConfiguration.getServerKeystore()) ? SCHEME_HTTPS : SCHEME_HTTP;
prefix = StringUtils.isNotBlank(springBootConfiguration.getServerContextPath()) ?
springBootConfiguration.getServerContextPath() : "";
if (hasSpringWebFluxDependency(getContext().getProject()) && StringUtils.isNotBlank(springBootConfiguration.getWebFluxBasePath())) {
prefix = springBootConfiguration.getWebFluxBasePath();
} else if (StringUtils.isNotBlank(springBootConfiguration.getServerContextPath())) {
prefix = springBootConfiguration.getServerContextPath();
} else {
prefix = "";
}
prefix += StringUtils.isNotBlank(springBootConfiguration.getServletPath()) ?
springBootConfiguration.getServletPath() : "";
prefix += StringUtils.isNotBlank(springBootConfiguration.getManagementContextPath()) ?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@
import org.eclipse.jkube.kit.config.resource.ProcessorConfig;
import org.eclipse.jkube.kit.enricher.api.JKubeEnricherContext;
import org.eclipse.jkube.kit.common.util.ProjectClassLoaders;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

Expand Down Expand Up @@ -701,6 +704,79 @@ void testLivenessAndReadinessProbesForDefaultPath_whenManagementHealthProbesEnab
assertHTTPGetPathAndPort(readinessProbe,getActuatorDefaultBasePath() + "/health",8080);
}

@Nested
@DisplayName("Spring Web Flux Dependency present")
class SpringWebFlux {
@BeforeEach
void setup() {
context.getProject().setDependencies(Collections.singletonList(Dependency.builder()
.groupId("org.springframework.boot")
.artifactId("spring-boot-starter-webflux")
.version(getSpringBootVersion())
.build()));
when(context.getProjectClassLoaders().isClassInCompileClasspath(true, REQUIRED_CLASSES))
.thenReturn(true);
}

@Nested
@DisplayName("spring.webflux.base-path property configured")
class SpringWebFluxBasePathConfigured {
@BeforeEach
void setUp() {
props.put("spring.webflux.base-path", "/webflux");
}

@AfterEach
void tearDown() {
props.clear();
}

@Test
@DisplayName("when management server sharing main server port, then liveness, readiness probes use web flux base path in endpoints")
void whenProbesGenerated_thenProbesAddWebFluxBasePath() {
// Given
props.put("management.port", "8383");
props.put("management.server.port", "8383");
writeProps();
SpringBootHealthCheckEnricher enricher = new SpringBootHealthCheckEnricher(context);
// When
Probe livenessProbe = enricher.getLivenessProbe();
Probe readinessProbe = enricher.getReadinessProbe();
// Then
assertHTTPGetPathAndPort(livenessProbe, getActuatorDefaultBasePath() + "/health",8383);
assertHTTPGetPathAndPort(readinessProbe,getActuatorDefaultBasePath() + "/health",8383);
}

@Test
@DisplayName("when a separate management server port configured, then spring.webflux.base-path is ignored")
void whenManagementServerRunningOnDifferentPort_thenProbesDoNotUseWebFluxBasePath() {
// Given
writeProps();
SpringBootHealthCheckEnricher enricher = new SpringBootHealthCheckEnricher(context);
// When
Probe livenessProbe = enricher.getLivenessProbe();
Probe readinessProbe = enricher.getReadinessProbe();
// Then
assertHTTPGetPathAndPort(livenessProbe,"/webflux" + getActuatorDefaultBasePath() + "/health",8080);
assertHTTPGetPathAndPort(readinessProbe,"/webflux" + getActuatorDefaultBasePath() + "/health",8080);
}
}

@Test
@DisplayName("when no explicit base path configured, then use default base path")
void whenManagementServerRunningOnDifferentPort_thenProbesDoNotUseWebFluxBasePath() {
// Given
writeProps();
SpringBootHealthCheckEnricher enricher = new SpringBootHealthCheckEnricher(context);
// When
Probe livenessProbe = enricher.getLivenessProbe();
Probe readinessProbe = enricher.getReadinessProbe();
// Then
assertHTTPGetPathAndPort(livenessProbe,getActuatorDefaultBasePath() + "/health",8080);
assertHTTPGetPathAndPort(readinessProbe,getActuatorDefaultBasePath() + "/health",8080);
}
}

private void assertHTTPGetPathAndPort(Probe probe, String path, int port) {
assertThat(probe).isNotNull()
.extracting(Probe::getHttpGet).isNotNull()
Expand Down

0 comments on commit 3d2eead

Please sign in to comment.