Skip to content

Commit 18ccb62

Browse files
committed
fix(health): report service configured/available separately
1 parent e08b97a commit 18ccb62

File tree

2 files changed

+136
-0
lines changed

2 files changed

+136
-0
lines changed

src/main/java/io/cryostat/net/web/http/generic/HealthGetHandler.java

+6
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,16 @@ public void handle(RoutingContext ctx) {
133133
Map.of(
134134
"cryostatVersion",
135135
appVersion.getVersionString(),
136+
"dashboardConfigured",
137+
env.hasEnv(Variables.GRAFANA_DASHBOARD_ENV),
136138
"dashboardAvailable",
137139
dashboardAvailable.join(),
140+
"datasourceConfigured",
141+
env.hasEnv(Variables.GRAFANA_DATASOURCE_ENV),
138142
"datasourceAvailable",
139143
datasourceAvailable.join(),
144+
"reportsConfigured",
145+
env.hasEnv(Variables.REPORT_GENERATOR_ENV),
140146
"reportsAvailable",
141147
reportsAvailable.join())));
142148
}

src/test/java/io/cryostat/net/web/http/generic/HealthGetHandlerTest.java

+130
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,11 @@ void shouldHandleHealthRequest() {
128128
Matchers.equalTo(
129129
Map.of(
130130
"cryostatVersion", "v1.2.3",
131+
"dashboardConfigured", false,
131132
"dashboardAvailable", false,
133+
"datasourceConfigured", false,
132134
"datasourceAvailable", false,
135+
"reportsConfigured", false,
133136
"reportsAvailable", true)));
134137
}
135138

@@ -184,8 +187,11 @@ public Void answer(InvocationOnMock args) throws Throwable {
184187
Matchers.equalTo(
185188
Map.of(
186189
"cryostatVersion", "v1.2.3",
190+
"dashboardConfigured", false,
187191
"dashboardAvailable", false,
192+
"datasourceConfigured", true,
188193
"datasourceAvailable", true,
194+
"reportsConfigured", false,
189195
"reportsAvailable", true)));
190196
}
191197

@@ -240,8 +246,129 @@ public Void answer(InvocationOnMock args) throws Throwable {
240246
Matchers.equalTo(
241247
Map.of(
242248
"cryostatVersion", "v1.2.3",
249+
"dashboardConfigured", true,
243250
"dashboardAvailable", true,
251+
"datasourceConfigured", false,
244252
"datasourceAvailable", false,
253+
"reportsConfigured", false,
254+
"reportsAvailable", true)));
255+
}
256+
257+
@Test
258+
void shouldHandleHealthRequestWithConfiguredButUnhealthyService() {
259+
RoutingContext ctx = mock(RoutingContext.class);
260+
HttpServerResponse rep = mock(HttpServerResponse.class);
261+
when(ctx.response()).thenReturn(rep);
262+
when(rep.putHeader(Mockito.any(CharSequence.class), Mockito.anyString())).thenReturn(rep);
263+
264+
when(appVersion.getVersionString()).thenReturn("v1.2.3");
265+
266+
String url = "http://hostname:1/";
267+
when(env.hasEnv("GRAFANA_DASHBOARD_URL")).thenReturn(true);
268+
when(env.getEnv("GRAFANA_DASHBOARD_URL")).thenReturn(url);
269+
when(env.hasEnv("GRAFANA_DATASOURCE_URL")).thenReturn(false);
270+
271+
HttpRequest<Buffer> req = Mockito.mock(HttpRequest.class);
272+
HttpResponse<Buffer> resp = Mockito.mock(HttpResponse.class);
273+
Mockito.when(webClient.get(Mockito.anyString(), Mockito.anyString())).thenReturn(req);
274+
Mockito.when(req.port(Mockito.anyInt())).thenReturn(req);
275+
Mockito.when(req.ssl(Mockito.anyBoolean())).thenReturn(req);
276+
Mockito.when(req.timeout(Mockito.anyLong())).thenReturn(req);
277+
Mockito.doAnswer(
278+
new Answer<Void>() {
279+
@Override
280+
public Void answer(InvocationOnMock args) throws Throwable {
281+
AsyncResult<HttpResponse<Buffer>> asyncResult =
282+
Mockito.mock(AsyncResult.class);
283+
Mockito.when(asyncResult.result()).thenReturn(resp);
284+
Mockito.when(resp.statusCode()).thenReturn(500);
285+
((Handler<AsyncResult<HttpResponse<Buffer>>>) args.getArgument(0))
286+
.handle(asyncResult);
287+
return null;
288+
}
289+
})
290+
.when(req)
291+
.send(Mockito.any());
292+
293+
handler.handle(ctx);
294+
295+
verify(rep).putHeader(HttpHeaders.CONTENT_TYPE, HttpMimeType.JSON.mime());
296+
ArgumentCaptor<String> responseCaptor = ArgumentCaptor.forClass(String.class);
297+
verify(rep).end(responseCaptor.capture());
298+
299+
Map<String, Object> responseMap =
300+
gson.fromJson(
301+
responseCaptor.getValue(),
302+
new TypeToken<Map<String, Object>>() {}.getType());
303+
MatcherAssert.assertThat(
304+
responseMap,
305+
Matchers.equalTo(
306+
Map.of(
307+
"cryostatVersion", "v1.2.3",
308+
"dashboardConfigured", true,
309+
"dashboardAvailable", false,
310+
"datasourceConfigured", false,
311+
"datasourceAvailable", false,
312+
"reportsConfigured", false,
313+
"reportsAvailable", true)));
314+
}
315+
316+
@Test
317+
void shouldHandleHealthRequestWithConfiguredButUnreachableService() {
318+
RoutingContext ctx = mock(RoutingContext.class);
319+
HttpServerResponse rep = mock(HttpServerResponse.class);
320+
when(ctx.response()).thenReturn(rep);
321+
when(rep.putHeader(Mockito.any(CharSequence.class), Mockito.anyString())).thenReturn(rep);
322+
323+
when(appVersion.getVersionString()).thenReturn("v1.2.3");
324+
325+
String url = "http://hostname:1/";
326+
when(env.hasEnv("GRAFANA_DASHBOARD_URL")).thenReturn(true);
327+
when(env.getEnv("GRAFANA_DASHBOARD_URL")).thenReturn(url);
328+
when(env.hasEnv("GRAFANA_DATASOURCE_URL")).thenReturn(false);
329+
330+
HttpRequest<Buffer> req = Mockito.mock(HttpRequest.class);
331+
Mockito.when(webClient.get(Mockito.anyString(), Mockito.anyString())).thenReturn(req);
332+
Mockito.when(req.port(Mockito.anyInt())).thenReturn(req);
333+
Mockito.when(req.ssl(Mockito.anyBoolean())).thenReturn(req);
334+
Mockito.when(req.timeout(Mockito.anyLong())).thenReturn(req);
335+
Mockito.doAnswer(
336+
new Answer<Void>() {
337+
@Override
338+
public Void answer(InvocationOnMock args) throws Throwable {
339+
AsyncResult<HttpResponse<Buffer>> asyncResult =
340+
Mockito.mock(AsyncResult.class);
341+
Mockito.when(asyncResult.failed()).thenReturn(true);
342+
Mockito.when(asyncResult.cause())
343+
.thenReturn(new Exception("test failure: unreachable"));
344+
((Handler<AsyncResult<HttpResponse<Buffer>>>) args.getArgument(0))
345+
.handle(asyncResult);
346+
return null;
347+
}
348+
})
349+
.when(req)
350+
.send(Mockito.any());
351+
352+
handler.handle(ctx);
353+
354+
verify(rep).putHeader(HttpHeaders.CONTENT_TYPE, HttpMimeType.JSON.mime());
355+
ArgumentCaptor<String> responseCaptor = ArgumentCaptor.forClass(String.class);
356+
verify(rep).end(responseCaptor.capture());
357+
358+
Map<String, Object> responseMap =
359+
gson.fromJson(
360+
responseCaptor.getValue(),
361+
new TypeToken<Map<String, Object>>() {}.getType());
362+
MatcherAssert.assertThat(
363+
responseMap,
364+
Matchers.equalTo(
365+
Map.of(
366+
"cryostatVersion", "v1.2.3",
367+
"dashboardConfigured", true,
368+
"dashboardAvailable", false,
369+
"datasourceConfigured", false,
370+
"datasourceAvailable", false,
371+
"reportsConfigured", false,
245372
"reportsAvailable", true)));
246373
}
247374

@@ -295,8 +422,11 @@ public Void answer(InvocationOnMock args) throws Throwable {
295422
Matchers.equalTo(
296423
Map.of(
297424
"cryostatVersion", "v1.2.3",
425+
"dashboardConfigured", true,
298426
"dashboardAvailable", true,
427+
"datasourceConfigured", false,
299428
"datasourceAvailable", false,
429+
"reportsConfigured", false,
300430
"reportsAvailable", true)));
301431
}
302432
}

0 commit comments

Comments
 (0)