Skip to content

Commit

Permalink
feat(health): add cryostatVersion
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewazores committed Aug 19, 2021
1 parent 5b36816 commit 5be5cf0
Show file tree
Hide file tree
Showing 5 changed files with 222 additions and 18 deletions.
77 changes: 77 additions & 0 deletions src/main/java/io/cryostat/ApplicationVersion.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright The Cryostat Authors
*
* The Universal Permissive License (UPL), Version 1.0
*
* Subject to the condition set forth below, permission is hereby granted to any
* person obtaining a copy of this software, associated documentation and/or data
* (collectively the "Software"), free of charge and under any and all copyright
* rights in the Software, and any and all patent rights owned or freely
* licensable by each licensor hereunder covering either (i) the unmodified
* Software as contributed to or provided by such licensor, or (ii) the Larger
* Works (as defined below), to deal in both
*
* (a) the Software, and
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
* one is included with the Software (each a "Larger Work" to which the Software
* is contributed by such licensors),
*
* without restriction, including without limitation the rights to copy, create
* derivative works of, display, perform, and distribute the Software and make,
* use, sell, offer for sale, import, export, have made, and have sold the
* Software and the Larger Work(s), and to sublicense the foregoing rights on
* either these or other terms.
*
* This license is subject to the following condition:
* The above copyright notice and either this complete permission notice or at
* a minimum a reference to the UPL must be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package io.cryostat;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;

import io.cryostat.core.log.Logger;

public class ApplicationVersion {

private volatile String version;
private final Logger logger;

ApplicationVersion(Logger logger) {
this.logger = logger;
}

public synchronized String getVersionString() {
if (version == null) {
try (BufferedReader br =
new BufferedReader(
new InputStreamReader(
getClass().getResourceAsStream("/io/cryostat/version"),
StandardCharsets.UTF_8))) {
version =
br.lines()
.findFirst()
.orElseThrow(
() ->
new IllegalStateException(
"Resource file /io/cryostat/version is empty"))
.trim();
} catch (Exception e) {
logger.error(e);
version = "unknown";
}
}
return version;
}
}
6 changes: 6 additions & 0 deletions src/main/java/io/cryostat/MainModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ public abstract class MainModule {
public static final String RECORDINGS_PATH = "RECORDINGS_PATH";
public static final String CONF_DIR = "CONF_DIR";

@Provides
@Singleton
static ApplicationVersion provideApplicationVersion(Logger logger) {
return new ApplicationVersion(logger);
}

@Provides
@Singleton
static Logger provideLogger() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@

import javax.inject.Inject;

import io.cryostat.ApplicationVersion;
import io.cryostat.core.log.Logger;
import io.cryostat.core.sys.Environment;
import io.cryostat.net.security.ResourceAction;
Expand All @@ -66,13 +67,20 @@ class HealthGetHandler implements RequestHandler {
static final String GRAFANA_DATASOURCE_ENV = "GRAFANA_DATASOURCE_URL";
static final String GRAFANA_DASHBOARD_ENV = "GRAFANA_DASHBOARD_URL";

private final ApplicationVersion appVersion;
private final WebClient webClient;
private final Environment env;
private final Gson gson;
private final Logger logger;

@Inject
HealthGetHandler(WebClient webClient, Environment env, Gson gson, Logger logger) {
HealthGetHandler(
ApplicationVersion appVersion,
WebClient webClient,
Environment env,
Gson gson,
Logger logger) {
this.appVersion = appVersion;
this.webClient = webClient;
this.env = env;
this.gson = gson;
Expand Down Expand Up @@ -117,6 +125,8 @@ public void handle(RoutingContext ctx) {
.end(
gson.toJson(
Map.of(
"cryostatVersion",
appVersion.getVersionString(),
"dashboardAvailable",
dashboardAvailable.join(),
"datasourceAvailable",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@

import java.util.Map;

import io.cryostat.ApplicationVersion;
import io.cryostat.MainModule;
import io.cryostat.core.log.Logger;
import io.cryostat.core.sys.Environment;
Expand Down Expand Up @@ -77,14 +78,15 @@
class HealthGetHandlerTest {

HealthGetHandler handler;
@Mock ApplicationVersion appVersion;
@Mock WebClient webClient;
@Mock Environment env;
@Mock Logger logger;
Gson gson = MainModule.provideGson(logger);

@BeforeEach
void setup() {
this.handler = new HealthGetHandler(webClient, env, gson, logger);
this.handler = new HealthGetHandler(appVersion, webClient, env, gson, logger);
}

@Test
Expand All @@ -109,18 +111,25 @@ void shouldHandleHealthRequest() {
when(ctx.response()).thenReturn(rep);
when(rep.putHeader(Mockito.any(CharSequence.class), Mockito.anyString())).thenReturn(rep);

when(appVersion.getVersionString()).thenReturn("v1.2.3");

handler.handle(ctx);

verify(rep).putHeader(HttpHeaders.CONTENT_TYPE, HttpMimeType.JSON.mime());
ArgumentCaptor<String> responseCaptor = ArgumentCaptor.forClass(String.class);
verify(rep).end(responseCaptor.capture());

Map<String, Boolean> responseMap =
Map<String, Object> responseMap =
gson.fromJson(
responseCaptor.getValue(),
new TypeToken<Map<String, Boolean>>() {}.getType());
Assertions.assertEquals(responseMap.get("dashboardAvailable"), false);
Assertions.assertEquals(responseMap.get("datasourceAvailable"), false);
new TypeToken<Map<String, Object>>() {}.getType());
MatcherAssert.assertThat(
responseMap,
Matchers.equalTo(
Map.of(
"cryostatVersion", "v1.2.3",
"dashboardAvailable", false,
"datasourceAvailable", false)));
}

@Test
Expand All @@ -130,6 +139,8 @@ void shouldHandleHealthRequestWithDatasourceUrl() {
when(ctx.response()).thenReturn(rep);
when(rep.putHeader(Mockito.any(CharSequence.class), Mockito.anyString())).thenReturn(rep);

when(appVersion.getVersionString()).thenReturn("v1.2.3");

String url = "http://hostname:1/";
when(env.hasEnv("GRAFANA_DATASOURCE_URL")).thenReturn(true);
when(env.getEnv("GRAFANA_DATASOURCE_URL")).thenReturn(url);
Expand Down Expand Up @@ -163,12 +174,17 @@ public Void answer(InvocationOnMock args) throws Throwable {
ArgumentCaptor<String> responseCaptor = ArgumentCaptor.forClass(String.class);
verify(rep).end(responseCaptor.capture());

Map<String, Boolean> responseMap =
Map<String, Object> responseMap =
gson.fromJson(
responseCaptor.getValue(),
new TypeToken<Map<String, Boolean>>() {}.getType());
Assertions.assertEquals(responseMap.get("dashboardAvailable"), false);
Assertions.assertEquals(responseMap.get("datasourceAvailable"), true);
new TypeToken<Map<String, Object>>() {}.getType());
MatcherAssert.assertThat(
responseMap,
Matchers.equalTo(
Map.of(
"cryostatVersion", "v1.2.3",
"dashboardAvailable", false,
"datasourceAvailable", true)));
}

@Test
Expand All @@ -178,6 +194,8 @@ void shouldHandleHealthRequestWithDashboardUrl() {
when(ctx.response()).thenReturn(rep);
when(rep.putHeader(Mockito.any(CharSequence.class), Mockito.anyString())).thenReturn(rep);

when(appVersion.getVersionString()).thenReturn("v1.2.3");

String url = "http://hostname:1/";
when(env.hasEnv("GRAFANA_DASHBOARD_URL")).thenReturn(true);
when(env.getEnv("GRAFANA_DASHBOARD_URL")).thenReturn(url);
Expand Down Expand Up @@ -211,12 +229,17 @@ public Void answer(InvocationOnMock args) throws Throwable {
ArgumentCaptor<String> responseCaptor = ArgumentCaptor.forClass(String.class);
verify(rep).end(responseCaptor.capture());

Map<String, Boolean> responseMap =
Map<String, Object> responseMap =
gson.fromJson(
responseCaptor.getValue(),
new TypeToken<Map<String, Boolean>>() {}.getType());
Assertions.assertEquals(responseMap.get("dashboardAvailable"), true);
Assertions.assertEquals(responseMap.get("datasourceAvailable"), false);
new TypeToken<Map<String, Object>>() {}.getType());
MatcherAssert.assertThat(
responseMap,
Matchers.equalTo(
Map.of(
"cryostatVersion", "v1.2.3",
"dashboardAvailable", true,
"datasourceAvailable", false)));
}

@Test
Expand All @@ -226,6 +249,8 @@ void shouldHandleHealthRequestWithDashboardUrlWithoutExplicitPort() {
when(ctx.response()).thenReturn(rep);
when(rep.putHeader(Mockito.any(CharSequence.class), Mockito.anyString())).thenReturn(rep);

when(appVersion.getVersionString()).thenReturn("v1.2.3");

String url = "https://hostname/";
when(env.hasEnv("GRAFANA_DASHBOARD_URL")).thenReturn(true);
when(env.getEnv("GRAFANA_DASHBOARD_URL")).thenReturn(url);
Expand Down Expand Up @@ -258,11 +283,16 @@ public Void answer(InvocationOnMock args) throws Throwable {
ArgumentCaptor<String> responseCaptor = ArgumentCaptor.forClass(String.class);
verify(rep).end(responseCaptor.capture());

Map<String, Boolean> responseMap =
Map<String, Object> responseMap =
gson.fromJson(
responseCaptor.getValue(),
new TypeToken<Map<String, Boolean>>() {}.getType());
Assertions.assertEquals(responseMap.get("dashboardAvailable"), true);
Assertions.assertEquals(responseMap.get("datasourceAvailable"), false);
new TypeToken<Map<String, Object>>() {}.getType());
MatcherAssert.assertThat(
responseMap,
Matchers.equalTo(
Map.of(
"cryostatVersion", "v1.2.3",
"dashboardAvailable", true,
"datasourceAvailable", false)));
}
}
81 changes: 81 additions & 0 deletions src/test/java/itest/HealthIT.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Copyright The Cryostat Authors
*
* The Universal Permissive License (UPL), Version 1.0
*
* Subject to the condition set forth below, permission is hereby granted to any
* person obtaining a copy of this software, associated documentation and/or data
* (collectively the "Software"), free of charge and under any and all copyright
* rights in the Software, and any and all patent rights owned or freely
* licensable by each licensor hereunder covering either (i) the unmodified
* Software as contributed to or provided by such licensor, or (ii) the Larger
* Works (as defined below), to deal in both
*
* (a) the Software, and
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
* one is included with the Software (each a "Larger Work" to which the Software
* is contributed by such licensors),
*
* without restriction, including without limitation the rights to copy, create
* derivative works of, display, perform, and distribute the Software and make,
* use, sell, offer for sale, import, export, have made, and have sold the
* Software and the Larger Work(s), and to sublicense the foregoing rights on
* either these or other terms.
*
* This license is subject to the following condition:
* The above copyright notice and either this complete permission notice or at
* a minimum a reference to the UPL must be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package itest;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

import io.vertx.core.buffer.Buffer;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.client.HttpRequest;
import itest.bases.StandardSelfTest;
import org.hamcrest.MatcherAssert;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

public class HealthIT extends StandardSelfTest {

HttpRequest<Buffer> req;

@BeforeEach
void createRequest() {
req = webClient.get("/health");
}

@Test
void shouldIncludeApplicationVersion() throws Exception {
CompletableFuture<JsonObject> future = new CompletableFuture<>();
req.send(
ar -> {
if (ar.failed()) {
future.completeExceptionally(ar.cause());
return;
}
future.complete(ar.result().bodyAsJsonObject());
});
JsonObject response = future.get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS);

Assertions.assertTrue(response.containsKey("cryostatVersion"));
MatcherAssert.assertThat(
response.getString("cryostatVersion"), Matchers.not(Matchers.emptyOrNullString()));
MatcherAssert.assertThat(
response.getString("cryostatVersion"), Matchers.not(Matchers.equalTo("unknown")));
}
}

0 comments on commit 5be5cf0

Please sign in to comment.