Skip to content

Commit dbd1c9c

Browse files
committed
Add Spring Boot Actuator for health endpoint
Resolves embabel#33 - Add spring-boot-starter-actuator dependency - Expose /actuator/health and /actuator/info endpoints - Configure security to permit actuator endpoints without auth - Add ActuatorSecurityTest with 4 regression tests - Configure health details to show only when authorized This provides a proper health check endpoint for CI/CD workflows, monitoring, and orchestration, replacing the workaround of using /api/v1/data/stats. All tests pass
1 parent d072b0a commit dbd1c9c

File tree

4 files changed

+113
-1
lines changed

4 files changed

+113
-1
lines changed

pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,12 @@
131131
<artifactId>spring-boot-starter-security</artifactId>
132132
</dependency>
133133

134+
<!-- Actuator for health checks and monitoring -->
135+
<dependency>
136+
<groupId>org.springframework.boot</groupId>
137+
<artifactId>spring-boot-starter-actuator</artifactId>
138+
</dependency>
139+
134140
<!-- Transaction Management -->
135141
<dependency>
136142
<groupId>org.springframework</groupId>

src/main/kotlin/com/embabel/guide/chat/security/SecurityConfig.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ class SecurityConfig(
4747
web.ignoring().requestMatchers(*mcpMatchers)
4848
}
4949

50+
val actuatorPatterns = arrayOf(
51+
"/actuator",
52+
"/actuator/**",
53+
)
54+
5055
val permittedPatterns = arrayOf(
5156
"/ws/**",
5257
"/app/**",
@@ -55,7 +60,7 @@ class SecurityConfig(
5560
"/",
5661
"/index.html",
5762
"/static/**",
58-
) + mcpPatterns
63+
) + mcpPatterns + actuatorPatterns
5964

6065
@Bean
6166
@Order(0)

src/main/resources/application.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
11
server:
22
port: 1337
33

4+
# Actuator endpoints for health checks and monitoring
5+
management:
6+
endpoints:
7+
web:
8+
exposure:
9+
include: health,info
10+
base-path: /actuator
11+
endpoint:
12+
health:
13+
show-details: when-authorized
14+
415
guide:
516

617
# If true, the Guide chatbot will load content from the projects path on startup
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* Copyright 2024-2025 Embabel Software, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.embabel.guide.chat.security
17+
18+
import com.embabel.guide.Neo4jPropertiesInitializer
19+
import org.junit.jupiter.api.Test
20+
import org.springframework.beans.factory.annotation.Autowired
21+
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
22+
import org.springframework.boot.test.context.SpringBootTest
23+
import org.springframework.test.context.ActiveProfiles
24+
import org.springframework.test.context.ContextConfiguration
25+
import org.springframework.test.web.servlet.MockMvc
26+
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
27+
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath
28+
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
29+
30+
/**
31+
* Tests that Actuator health endpoints are accessible without authentication.
32+
*
33+
* These tests ensure that health check endpoints remain publicly accessible
34+
* for monitoring, orchestration, and CI/CD workflows.
35+
*/
36+
@SpringBootTest
37+
@ActiveProfiles("test")
38+
@AutoConfigureMockMvc
39+
@ContextConfiguration(initializers = [Neo4jPropertiesInitializer::class])
40+
class ActuatorSecurityTest {
41+
42+
@Autowired
43+
private lateinit var mockMvc: MockMvc
44+
45+
@Test
46+
fun `actuator health endpoint should be accessible without authentication`() {
47+
val result = mockMvc.perform(get("/actuator/health")).andReturn()
48+
val httpStatus = result.response.status
49+
// Health endpoint should not be blocked by security (401/403)
50+
// It may return 503 if some health indicators are DOWN, but security should not block it
51+
assert(httpStatus != 401 && httpStatus != 403) {
52+
"Actuator health endpoint should not return 401 or 403, got $httpStatus"
53+
}
54+
}
55+
56+
@Test
57+
fun `actuator health endpoint should return health status`() {
58+
val result = mockMvc.perform(get("/actuator/health")).andReturn()
59+
val httpStatus = result.response.status
60+
// Should be 200 (UP) or 503 (DOWN), but accessible
61+
assert(httpStatus == 200 || httpStatus == 503) {
62+
"Actuator health endpoint should return 200 or 503, got $httpStatus"
63+
}
64+
// Response should contain status field
65+
val body = result.response.contentAsString
66+
assert(body.contains("status")) {
67+
"Health response should contain status field"
68+
}
69+
}
70+
71+
@Test
72+
fun `actuator info endpoint should be accessible without authentication`() {
73+
val result = mockMvc.perform(get("/actuator/info")).andReturn()
74+
val httpStatus = result.response.status
75+
// Info endpoint might be empty (404) or return data (200), but should not be blocked by security (401/403)
76+
assert(httpStatus != 401 && httpStatus != 403) {
77+
"Actuator info endpoint should not return 401 or 403, got $httpStatus"
78+
}
79+
}
80+
81+
@Test
82+
fun `actuator base path should be accessible`() {
83+
val result = mockMvc.perform(get("/actuator")).andReturn()
84+
val httpStatus = result.response.status
85+
// Should not be blocked by security
86+
assert(httpStatus != 401 && httpStatus != 403) {
87+
"Actuator base path should not return 401 or 403, got $httpStatus"
88+
}
89+
}
90+
}

0 commit comments

Comments
 (0)