From 3d6feef167f8e8eb716312599132c41dd89457cb Mon Sep 17 00:00:00 2001 From: Piotr Jaszczyk Date: Mon, 3 Jun 2019 14:20:40 +0200 Subject: Monitoring API - write framework adapters Change-Id: Iaba9c4ef6022d01c1f572055316700a3a4dfa8f2 Issue-ID: DCAEGEN2-1589 Signed-off-by: Piotr Jaszczyk --- .gitignore | 5 +- pom.xml | 20 ++-- rest-services/aai-client/pom.xml | 5 +- rest-services/cbs-client/pom.xml | 4 + rest-services/dmaap-client/pom.xml | 4 + rest-services/http-client/pom.xml | 4 + .../services/sdk/security/ssl/PasswordTest.java | 1 - standardization/moher-api/healthstate/pom.xml | 19 ++++ .../moher/health/api/AliveMessage.java | 29 +++++ .../standardization/moher/health/api/Health.java | 51 +++++++++ .../moher/health/api/HealthProvider.java | 38 +++++++ .../moher/metrics/impl/MetricsImpl.java | 4 +- standardization/moher-api/server-adapters/pom.xml | 2 +- .../server-adapters/reactor-netty/pom.xml | 30 +++++ .../moher/adapters/reactornetty/HealthRoutes.java | 80 ++++++++++++++ .../moher/adapters/reactornetty/MetricsRoutes.java | 49 +++++++++ .../adapters/reactornetty/HealthRoutesIT.java | 122 +++++++++++++++++++++ .../adapters/reactornetty/MetricsRoutesIT.java | 71 ++++++++++++ .../server-adapters/spring-webflux/pom.xml | 81 ++++++++++++++ .../adapters/springwebflux/HealthController.java | 72 ++++++++++++ .../adapters/springwebflux/MetricsController.java | 43 ++++++++ .../adapters/springwebflux/HealthControllerIT.java | 81 ++++++++++++++ .../springwebflux/MetricsControllerIT.java | 48 ++++++++ .../moher-api/server-adapters/spring/pom.xml | 35 ------ 24 files changed, 846 insertions(+), 52 deletions(-) create mode 100644 standardization/moher-api/healthstate/src/main/java/org/onap/dcaegen2/services/sdk/standardization/moher/health/api/AliveMessage.java create mode 100644 standardization/moher-api/healthstate/src/main/java/org/onap/dcaegen2/services/sdk/standardization/moher/health/api/Health.java create mode 100644 standardization/moher-api/healthstate/src/main/java/org/onap/dcaegen2/services/sdk/standardization/moher/health/api/HealthProvider.java create mode 100644 standardization/moher-api/server-adapters/reactor-netty/src/main/java/org/onap/dcaegen2/services/sdk/standardization/moher/adapters/reactornetty/HealthRoutes.java create mode 100644 standardization/moher-api/server-adapters/reactor-netty/src/main/java/org/onap/dcaegen2/services/sdk/standardization/moher/adapters/reactornetty/MetricsRoutes.java create mode 100644 standardization/moher-api/server-adapters/reactor-netty/src/test/java/org/onap/dcaegen2/services/sdk/standardization/moher/adapters/reactornetty/HealthRoutesIT.java create mode 100644 standardization/moher-api/server-adapters/reactor-netty/src/test/java/org/onap/dcaegen2/services/sdk/standardization/moher/adapters/reactornetty/MetricsRoutesIT.java create mode 100644 standardization/moher-api/server-adapters/spring-webflux/pom.xml create mode 100644 standardization/moher-api/server-adapters/spring-webflux/src/main/java/org/onap/dcaegen2/services/sdk/standardization/moher/adapters/springwebflux/HealthController.java create mode 100644 standardization/moher-api/server-adapters/spring-webflux/src/main/java/org/onap/dcaegen2/services/sdk/standardization/moher/adapters/springwebflux/MetricsController.java create mode 100644 standardization/moher-api/server-adapters/spring-webflux/src/test/java/org/onap/dcaegen2/services/sdk/standardization/moher/adapters/springwebflux/HealthControllerIT.java create mode 100644 standardization/moher-api/server-adapters/spring-webflux/src/test/java/org/onap/dcaegen2/services/sdk/standardization/moher/adapters/springwebflux/MetricsControllerIT.java delete mode 100644 standardization/moher-api/server-adapters/spring/pom.xml diff --git a/.gitignore b/.gitignore index da642ecd..837391e5 100644 --- a/.gitignore +++ b/.gitignore @@ -173,6 +173,9 @@ fabric.properties # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* +# VSCode +.vscode/ + ### Maven ### # Maven target @@ -185,4 +188,4 @@ release.properties dependency-reduced-pom.xml buildNumber.properties .mvn/timing.properties -.mvn/wrapper/maven-wrapper.jar \ No newline at end of file +.mvn/wrapper/maven-wrapper.jar diff --git a/pom.xml b/pom.xml index ea0a379e..fc214bd7 100644 --- a/pom.xml +++ b/pom.xml @@ -40,8 +40,8 @@ 1.6 16.0.3 3.6.0.2 - 1.1.4 1.11.2 + 2.1.5.RELEASE @@ -165,6 +165,7 @@ org.immutables value ${immutables.version} + provided org.immutables @@ -196,6 +197,13 @@ slf4j-api ${slf4j.version} + + spring-boot-dependencies + org.springframework.boot + ${spring.boot.version} + pom + import + org.jetbrains annotations @@ -220,16 +228,6 @@ 1.7.26 runtime - - io.micrometer - micrometer-registry-prometheus - ${micrometer.version} - - - org.springframework.security - spring-security-crypto - 5.0.12.RELEASE - org.mockito diff --git a/rest-services/aai-client/pom.xml b/rest-services/aai-client/pom.xml index 7c63f4b0..6a695b0f 100644 --- a/rest-services/aai-client/pom.xml +++ b/rest-services/aai-client/pom.xml @@ -28,7 +28,10 @@ ssl ${project.version} - + + org.immutables + value + org.apache.commons commons-text diff --git a/rest-services/cbs-client/pom.xml b/rest-services/cbs-client/pom.xml index 7d044dce..538950fa 100644 --- a/rest-services/cbs-client/pom.xml +++ b/rest-services/cbs-client/pom.xml @@ -28,6 +28,10 @@ model ${project.version} + + org.immutables + value + org.mockito diff --git a/rest-services/dmaap-client/pom.xml b/rest-services/dmaap-client/pom.xml index 86a4ab07..a8ee7391 100644 --- a/rest-services/dmaap-client/pom.xml +++ b/rest-services/dmaap-client/pom.xml @@ -46,6 +46,10 @@ ch.qos.logback logback-classic + + org.immutables + value + org.junit.jupiter diff --git a/rest-services/http-client/pom.xml b/rest-services/http-client/pom.xml index 1d321058..ba6b2fd0 100644 --- a/rest-services/http-client/pom.xml +++ b/rest-services/http-client/pom.xml @@ -57,6 +57,10 @@ io.vavr vavr + + org.immutables + value + org.jetbrains annotations diff --git a/security/ssl/src/test/java/org/onap/dcaegen2/services/sdk/security/ssl/PasswordTest.java b/security/ssl/src/test/java/org/onap/dcaegen2/services/sdk/security/ssl/PasswordTest.java index 41143f61..03d4ac25 100644 --- a/security/ssl/src/test/java/org/onap/dcaegen2/services/sdk/security/ssl/PasswordTest.java +++ b/security/ssl/src/test/java/org/onap/dcaegen2/services/sdk/security/ssl/PasswordTest.java @@ -24,7 +24,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import io.vavr.collection.Array; -import java.security.GeneralSecurityException; import java.util.Arrays; import org.junit.jupiter.api.Test; import org.onap.dcaegen2.services.sdk.security.ssl.exceptions.PasswordEvictedException; diff --git a/standardization/moher-api/healthstate/pom.xml b/standardization/moher-api/healthstate/pom.xml index d6cf46be..a4036288 100644 --- a/standardization/moher-api/healthstate/pom.xml +++ b/standardization/moher-api/healthstate/pom.xml @@ -33,4 +33,23 @@ dcaegen2-sdk-moher-healthstate + + + io.vavr + vavr + + + + org.immutables + value + + + org.immutables + gson + + + io.projectreactor + reactor-core + + \ No newline at end of file diff --git a/standardization/moher-api/healthstate/src/main/java/org/onap/dcaegen2/services/sdk/standardization/moher/health/api/AliveMessage.java b/standardization/moher-api/healthstate/src/main/java/org/onap/dcaegen2/services/sdk/standardization/moher/health/api/AliveMessage.java new file mode 100644 index 00000000..c9a8c226 --- /dev/null +++ b/standardization/moher-api/healthstate/src/main/java/org/onap/dcaegen2/services/sdk/standardization/moher/health/api/AliveMessage.java @@ -0,0 +1,29 @@ +/* + * ============LICENSE_START==================================== + * DCAEGEN2-SERVICES-SDK + * ========================================================= + * Copyright (C) 2019 Nokia. All rights reserved. + * ========================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END===================================== + */ + +package org.onap.dcaegen2.services.sdk.standardization.moher.health.api; + +public final class AliveMessage { + + public static final String ALIVE_MESSAGE_JSON = "{\"alive\":true}"; + + private AliveMessage() { + } +} diff --git a/standardization/moher-api/healthstate/src/main/java/org/onap/dcaegen2/services/sdk/standardization/moher/health/api/Health.java b/standardization/moher-api/healthstate/src/main/java/org/onap/dcaegen2/services/sdk/standardization/moher/health/api/Health.java new file mode 100644 index 00000000..65b11ad1 --- /dev/null +++ b/standardization/moher-api/healthstate/src/main/java/org/onap/dcaegen2/services/sdk/standardization/moher/health/api/Health.java @@ -0,0 +1,51 @@ +/* + * ============LICENSE_START==================================== + * DCAEGEN2-SERVICES-SDK + * ========================================================= + * Copyright (C) 2019 Nokia. All rights reserved. + * ========================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END===================================== + */ + +package org.onap.dcaegen2.services.sdk.standardization.moher.health.api; + +import org.immutables.gson.Gson; +import org.immutables.value.Value; + +@Value.Immutable +@Gson.TypeAdapters +public interface Health { + + @Value.Default + default boolean healthy() { + return true; + } + + @Value.Default + default String description() { + return "Service is working correctly"; + } + + static Health createHealthy() { + return ImmutableHealth.builder().build(); + } + + static Health createHealthy(String description) { + return ImmutableHealth.builder().description(description).build(); + } + + static Health createUnhealthy(String description) { + return ImmutableHealth.builder().healthy(false).description(description).build(); + } +} diff --git a/standardization/moher-api/healthstate/src/main/java/org/onap/dcaegen2/services/sdk/standardization/moher/health/api/HealthProvider.java b/standardization/moher-api/healthstate/src/main/java/org/onap/dcaegen2/services/sdk/standardization/moher/health/api/HealthProvider.java new file mode 100644 index 00000000..b6d770a4 --- /dev/null +++ b/standardization/moher-api/healthstate/src/main/java/org/onap/dcaegen2/services/sdk/standardization/moher/health/api/HealthProvider.java @@ -0,0 +1,38 @@ +/* + * ============LICENSE_START==================================== + * DCAEGEN2-SERVICES-SDK + * ========================================================= + * Copyright (C) 2019 Nokia. All rights reserved. + * ========================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END===================================== + */ + +package org.onap.dcaegen2.services.sdk.standardization.moher.health.api; + +import io.vavr.Function0; +import java.util.function.Supplier; +import reactor.core.publisher.Mono; + +@FunctionalInterface +public interface HealthProvider { + Mono currentHealth(); + + static HealthProvider fromFunction(Function0 function) { + return () -> Mono.fromCallable(function::apply); + } + + static HealthProvider fromSupplier(Supplier function) { + return () -> Mono.fromCallable(function::get); + } +} diff --git a/standardization/moher-api/metrics/src/main/java/org/onap/dcaegen2/services/sdk/standardization/moher/metrics/impl/MetricsImpl.java b/standardization/moher-api/metrics/src/main/java/org/onap/dcaegen2/services/sdk/standardization/moher/metrics/impl/MetricsImpl.java index e592d976..8ccce84d 100644 --- a/standardization/moher-api/metrics/src/main/java/org/onap/dcaegen2/services/sdk/standardization/moher/metrics/impl/MetricsImpl.java +++ b/standardization/moher-api/metrics/src/main/java/org/onap/dcaegen2/services/sdk/standardization/moher/metrics/impl/MetricsImpl.java @@ -42,13 +42,13 @@ public class MetricsImpl implements Metrics { @Override public Mono collect() { - return Mono.just(registry.scrape()); + return Mono.fromCallable(registry::scrape); } @Override public Flux collect(Duration interval) { return Flux.interval(interval) - .map((l) -> registry.scrape()); + .map(l -> registry.scrape()); } @SuppressWarnings("squid:S2095") diff --git a/standardization/moher-api/server-adapters/pom.xml b/standardization/moher-api/server-adapters/pom.xml index 4b8f4a55..94d83473 100644 --- a/standardization/moher-api/server-adapters/pom.xml +++ b/standardization/moher-api/server-adapters/pom.xml @@ -35,7 +35,7 @@ pom - spring + spring-webflux reactor-netty \ No newline at end of file diff --git a/standardization/moher-api/server-adapters/reactor-netty/pom.xml b/standardization/moher-api/server-adapters/reactor-netty/pom.xml index f2f37412..9ae7df4c 100644 --- a/standardization/moher-api/server-adapters/reactor-netty/pom.xml +++ b/standardization/moher-api/server-adapters/reactor-netty/pom.xml @@ -32,4 +32,34 @@ MoHeR Project Reactor's Netty server adapter dcae-sdk-moher-reactor-netty + + + io.projectreactor.netty + reactor-netty + + + org.onap.dcaegen2.services.sdk + dcaegen2-sdk-moher-metrics + ${project.version} + + + org.onap.dcaegen2.services.sdk + dcaegen2-sdk-moher-healthstate + ${project.version} + + + org.junit.jupiter + junit-jupiter-engine + + + org.assertj + assertj-core + + + org.onap.dcaegen2.services.sdk.rest.services + http-client + ${project.version} + test + + \ No newline at end of file diff --git a/standardization/moher-api/server-adapters/reactor-netty/src/main/java/org/onap/dcaegen2/services/sdk/standardization/moher/adapters/reactornetty/HealthRoutes.java b/standardization/moher-api/server-adapters/reactor-netty/src/main/java/org/onap/dcaegen2/services/sdk/standardization/moher/adapters/reactornetty/HealthRoutes.java new file mode 100644 index 00000000..1b6411de --- /dev/null +++ b/standardization/moher-api/server-adapters/reactor-netty/src/main/java/org/onap/dcaegen2/services/sdk/standardization/moher/adapters/reactornetty/HealthRoutes.java @@ -0,0 +1,80 @@ +/* + * ============LICENSE_START==================================== + * DCAEGEN2-SERVICES-SDK + * ========================================================= + * Copyright (C) 2019 Nokia. All rights reserved. + * ========================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END===================================== + */ + +package org.onap.dcaegen2.services.sdk.standardization.moher.adapters.reactornetty; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.handler.codec.http.HttpResponseStatus; +import java.util.function.Consumer; +import org.onap.dcaegen2.services.sdk.standardization.moher.health.api.AliveMessage; +import org.onap.dcaegen2.services.sdk.standardization.moher.health.api.GsonAdaptersHealth; +import org.onap.dcaegen2.services.sdk.standardization.moher.health.api.Health; +import org.onap.dcaegen2.services.sdk.standardization.moher.health.api.HealthProvider; +import org.reactivestreams.Publisher; +import reactor.core.publisher.Mono; +import reactor.netty.http.server.HttpServerRequest; +import reactor.netty.http.server.HttpServerResponse; +import reactor.netty.http.server.HttpServerRoutes; + +public class HealthRoutes implements Consumer { + + public static final String APPLICATION_JSON = "application/json"; + private final Gson gson; + private final HealthProvider healthProvider; + + public HealthRoutes(Gson gson, + HealthProvider healthProvider) { + this.gson = gson; + this.healthProvider = healthProvider; + } + + public static HealthRoutes create(HealthProvider healthProvider) { + GsonBuilder gson = new GsonBuilder(); + gson.registerTypeAdapterFactory(new GsonAdaptersHealth()); + return new HealthRoutes(gson.create(), healthProvider); + } + + @Override + public void accept(HttpServerRoutes routes) { + routes.get("/health/ready", this::readinessCheck); + routes.get("/health/alive", this::livenessCheck); + } + + private Publisher readinessCheck(HttpServerRequest httpServerRequest, HttpServerResponse httpServerResponse) { + return healthProvider.currentHealth() + .flatMapMany(health -> + httpServerResponse.status(statusForHealth(health)) + .header(HttpHeaderNames.CONTENT_TYPE, APPLICATION_JSON) + .sendString(Mono.just(health).map(gson::toJson)) + ); + } + + private Publisher livenessCheck(HttpServerRequest httpServerRequest, HttpServerResponse httpServerResponse) { + return httpServerResponse.status(HttpResponseStatus.OK) + .header(HttpHeaderNames.CONTENT_TYPE, APPLICATION_JSON) + .sendString(Mono.just(AliveMessage.ALIVE_MESSAGE_JSON)); + } + + private HttpResponseStatus statusForHealth(Health health) { + return health.healthy() ? HttpResponseStatus.OK : HttpResponseStatus.SERVICE_UNAVAILABLE; + } +} diff --git a/standardization/moher-api/server-adapters/reactor-netty/src/main/java/org/onap/dcaegen2/services/sdk/standardization/moher/adapters/reactornetty/MetricsRoutes.java b/standardization/moher-api/server-adapters/reactor-netty/src/main/java/org/onap/dcaegen2/services/sdk/standardization/moher/adapters/reactornetty/MetricsRoutes.java new file mode 100644 index 00000000..b6db874d --- /dev/null +++ b/standardization/moher-api/server-adapters/reactor-netty/src/main/java/org/onap/dcaegen2/services/sdk/standardization/moher/adapters/reactornetty/MetricsRoutes.java @@ -0,0 +1,49 @@ +/* + * ============LICENSE_START==================================== + * DCAEGEN2-SERVICES-SDK + * ========================================================= + * Copyright (C) 2019 Nokia. All rights reserved. + * ========================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END===================================== + */ + +package org.onap.dcaegen2.services.sdk.standardization.moher.adapters.reactornetty; + +import java.util.function.Consumer; +import org.onap.dcaegen2.services.sdk.standardization.moher.metrics.api.Metrics; +import org.reactivestreams.Publisher; +import reactor.netty.http.server.HttpServerRequest; +import reactor.netty.http.server.HttpServerResponse; +import reactor.netty.http.server.HttpServerRoutes; + +public class MetricsRoutes implements Consumer { + + private final Metrics metrics; + + public MetricsRoutes(Metrics metrics) { + this.metrics = metrics; + } + + @Override + public void accept(HttpServerRoutes routes) { + routes.get("/metrics", this::prometheusMetrics); + } + + private Publisher prometheusMetrics( + HttpServerRequest httpServerRequest, + HttpServerResponse httpServerResponse) { + return httpServerResponse.sendString(metrics.collect()); + } + +} diff --git a/standardization/moher-api/server-adapters/reactor-netty/src/test/java/org/onap/dcaegen2/services/sdk/standardization/moher/adapters/reactornetty/HealthRoutesIT.java b/standardization/moher-api/server-adapters/reactor-netty/src/test/java/org/onap/dcaegen2/services/sdk/standardization/moher/adapters/reactornetty/HealthRoutesIT.java new file mode 100644 index 00000000..367f50e3 --- /dev/null +++ b/standardization/moher-api/server-adapters/reactor-netty/src/test/java/org/onap/dcaegen2/services/sdk/standardization/moher/adapters/reactornetty/HealthRoutesIT.java @@ -0,0 +1,122 @@ +/* + * ============LICENSE_START==================================== + * DCAEGEN2-SERVICES-SDK + * ========================================================= + * Copyright (C) 2019 Nokia. All rights reserved. + * ========================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END===================================== + */ + +package org.onap.dcaegen2.services.sdk.standardization.moher.adapters.reactornetty; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.util.concurrent.atomic.AtomicReference; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.onap.dcaegen2.services.sdk.rest.services.adapters.http.HttpMethod; +import org.onap.dcaegen2.services.sdk.rest.services.adapters.http.HttpResponse; +import org.onap.dcaegen2.services.sdk.rest.services.adapters.http.ImmutableHttpRequest; +import org.onap.dcaegen2.services.sdk.rest.services.adapters.http.RxHttpClient; +import org.onap.dcaegen2.services.sdk.rest.services.adapters.http.RxHttpClientFactory; +import org.onap.dcaegen2.services.sdk.standardization.moher.health.api.AliveMessage; +import org.onap.dcaegen2.services.sdk.standardization.moher.health.api.GsonAdaptersHealth; +import org.onap.dcaegen2.services.sdk.standardization.moher.health.api.Health; +import org.onap.dcaegen2.services.sdk.standardization.moher.health.api.HealthProvider; +import reactor.netty.DisposableServer; +import reactor.netty.http.server.HttpServer; + +class HealthRoutesIT { + + private static final Duration TIMEOUT = Duration.ofSeconds(5); + private final AtomicReference currentHealth = new AtomicReference<>(); + private final HealthRoutes sut = HealthRoutes.create(HealthProvider.fromSupplier(currentHealth::get)); + private final RxHttpClient rxHttpClient = RxHttpClientFactory.create(); + private final Gson gsonForDeserialization = new GsonBuilder() + .registerTypeAdapterFactory(new GsonAdaptersHealth()) + .create(); + private DisposableServer server; + private String baseUrl; + + @BeforeEach + void setUp() { + server = HttpServer.create().route(sut).bindNow(); + baseUrl = String.format("http://%s:%d", server.host(), server.port()); + } + + @AfterEach + void tearDown() { + server.disposeNow(TIMEOUT); + } + + @Test + void readinessProbeShouldReturnOkWhenHealthy() { + // given + final Health expectedHealth = Health.createHealthy("Ready to go"); + currentHealth.set(expectedHealth); + final String url = baseUrl + "/health/ready"; + + // when + final HttpResponse response = rxHttpClient + .call(ImmutableHttpRequest.builder().method(HttpMethod.GET).url(url).build()) + .block(TIMEOUT); + + // then + assertThat(response.successful()).describedAs("response should be successful").isTrue(); + final Health actualHealth = response.bodyAsJson(StandardCharsets.UTF_8, gsonForDeserialization, Health.class); + assertThat(actualHealth).describedAs("response body").isEqualTo(expectedHealth); + } + + @Test + void readinessProbeShouldReturnUnavailableWhenNotHealthy() { + // given + final Health expectedHealth = Health.createUnhealthy("Waiting for CBS update"); + currentHealth.set(expectedHealth); + final String url = baseUrl + "/health/ready"; + + // when + final HttpResponse response = rxHttpClient + .call(ImmutableHttpRequest.builder().method(HttpMethod.GET).url(url).build()) + .block(TIMEOUT); + + // then + assertThat(response.statusCode()).describedAs("response status code") + .isGreaterThanOrEqualTo(500) + .isLessThan(600); + assertThat(response.successful()).describedAs("response should not be successful").isFalse(); + final Health actualHealth = response.bodyAsJson(StandardCharsets.UTF_8, gsonForDeserialization, Health.class); + assertThat(actualHealth).describedAs("response body").isEqualTo(expectedHealth); + } + + @Test + void livenessProbeShouldAlwaysReturnOk() { + // given + final String url = baseUrl + "/health/alive"; + + // when + final HttpResponse response = rxHttpClient + .call(ImmutableHttpRequest.builder().method(HttpMethod.GET).url(url).build()) + .block(TIMEOUT); + + // then + assertThat(response.successful()).describedAs("response should be successful").isTrue(); + assertThat(response.bodyAsString()).describedAs("response body").isEqualTo(AliveMessage.ALIVE_MESSAGE_JSON); + } + +} \ No newline at end of file diff --git a/standardization/moher-api/server-adapters/reactor-netty/src/test/java/org/onap/dcaegen2/services/sdk/standardization/moher/adapters/reactornetty/MetricsRoutesIT.java b/standardization/moher-api/server-adapters/reactor-netty/src/test/java/org/onap/dcaegen2/services/sdk/standardization/moher/adapters/reactornetty/MetricsRoutesIT.java new file mode 100644 index 00000000..55c24c73 --- /dev/null +++ b/standardization/moher-api/server-adapters/reactor-netty/src/test/java/org/onap/dcaegen2/services/sdk/standardization/moher/adapters/reactornetty/MetricsRoutesIT.java @@ -0,0 +1,71 @@ +/* + * ============LICENSE_START==================================== + * DCAEGEN2-SERVICES-SDK + * ========================================================= + * Copyright (C) 2019 Nokia. All rights reserved. + * ========================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END===================================== + */ + +package org.onap.dcaegen2.services.sdk.standardization.moher.adapters.reactornetty; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.Duration; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.onap.dcaegen2.services.sdk.rest.services.adapters.http.HttpMethod; +import org.onap.dcaegen2.services.sdk.rest.services.adapters.http.HttpResponse; +import org.onap.dcaegen2.services.sdk.rest.services.adapters.http.ImmutableHttpRequest; +import org.onap.dcaegen2.services.sdk.rest.services.adapters.http.RxHttpClient; +import org.onap.dcaegen2.services.sdk.rest.services.adapters.http.RxHttpClientFactory; +import org.onap.dcaegen2.services.sdk.standardization.moher.metrics.api.Metrics; +import org.onap.dcaegen2.services.sdk.standardization.moher.metrics.api.MetricsFactory; +import reactor.netty.DisposableServer; +import reactor.netty.http.server.HttpServer; + +class MetricsRoutesIT { + + private static final Duration TIMEOUT = Duration.ofSeconds(5); + private final Metrics metrics = MetricsFactory.createMetrics(MetricsFactory.createDefaultRegistry()); + private final MetricsRoutes sut = new MetricsRoutes(metrics); + private final RxHttpClient rxHttpClient = RxHttpClientFactory.create(); + private DisposableServer server; + + @BeforeEach + void setUp() { + server = HttpServer.create().route(sut).bindNow(); + } + + @AfterEach + void tearDown() { + server.disposeNow(TIMEOUT); + } + + @Test + void prometheusMetrics() { + // given + final String url = String.format("http://%s:%d/metrics", server.host(), server.port()); + + // when + final HttpResponse response = rxHttpClient + .call(ImmutableHttpRequest.builder().method(HttpMethod.GET).url(url).build()) + .block(TIMEOUT); + + // then + assertThat(response.successful()).describedAs("response should be successfull").isTrue(); + assertThat(response.bodyAsString()).describedAs("response body").contains("system_cpu", "jvm_classes"); + } +} \ No newline at end of file diff --git a/standardization/moher-api/server-adapters/spring-webflux/pom.xml b/standardization/moher-api/server-adapters/spring-webflux/pom.xml new file mode 100644 index 00000000..7eff97aa --- /dev/null +++ b/standardization/moher-api/server-adapters/spring-webflux/pom.xml @@ -0,0 +1,81 @@ + + + + 4.0.0 + + dcaegen2-sdk-moher-server-adapters + org.onap.dcaegen2.services.sdk + 1.2.0-SNAPSHOT + + + Monitoring and Healthcheck :: Server Adapters :: Spring Webflux + MoHeR Spring server adapter + dcae-sdk-moher-spring-webflux + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + + + + + org.springframework + spring-webflux + + + org.onap.dcaegen2.services.sdk + dcaegen2-sdk-moher-metrics + ${project.version} + + + org.onap.dcaegen2.services.sdk + dcaegen2-sdk-moher-healthstate + ${project.version} + + + org.junit.jupiter + junit-jupiter-engine + + + org.assertj + assertj-core + + + org.hamcrest + hamcrest-core + + + org.springframework + spring-test + + + spring-boot-starter-webflux + org.springframework.boot + test + + + \ No newline at end of file diff --git a/standardization/moher-api/server-adapters/spring-webflux/src/main/java/org/onap/dcaegen2/services/sdk/standardization/moher/adapters/springwebflux/HealthController.java b/standardization/moher-api/server-adapters/spring-webflux/src/main/java/org/onap/dcaegen2/services/sdk/standardization/moher/adapters/springwebflux/HealthController.java new file mode 100644 index 00000000..1f9eb4b0 --- /dev/null +++ b/standardization/moher-api/server-adapters/spring-webflux/src/main/java/org/onap/dcaegen2/services/sdk/standardization/moher/adapters/springwebflux/HealthController.java @@ -0,0 +1,72 @@ +/* + * ============LICENSE_START==================================== + * DCAEGEN2-SERVICES-SDK + * ========================================================= + * Copyright (C) 2019 Nokia. All rights reserved. + * ========================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END===================================== + */ + +package org.onap.dcaegen2.services.sdk.standardization.moher.adapters.springwebflux; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import org.onap.dcaegen2.services.sdk.standardization.moher.health.api.AliveMessage; +import org.onap.dcaegen2.services.sdk.standardization.moher.health.api.GsonAdaptersHealth; +import org.onap.dcaegen2.services.sdk.standardization.moher.health.api.Health; +import org.onap.dcaegen2.services.sdk.standardization.moher.health.api.HealthProvider; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import reactor.core.publisher.Mono; + +@RestController +@RequestMapping(value = "/health", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) +public class HealthController { + private final Gson gson; + private final HealthProvider healthProvider; + + public static HealthController create(HealthProvider healthProvider) { + GsonBuilder gson = new GsonBuilder(); + gson.registerTypeAdapterFactory(new GsonAdaptersHealth()); + return new HealthController(gson.create(), healthProvider); + } + + public HealthController(Gson gson, HealthProvider healthProvider) { + this.gson = gson; + this.healthProvider = healthProvider; + } + + @GetMapping("/ready") + public Mono> readinessCheck() { + return healthProvider.currentHealth() + .map(health -> responseStatusForHealth(health) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .body(gson.toJson(health))); + } + + @GetMapping("/alive") + public Mono livenessCheck() { + return Mono.just(AliveMessage.ALIVE_MESSAGE_JSON); + } + + private ResponseEntity.BodyBuilder responseStatusForHealth(Health health) { + return health.healthy() + ? ResponseEntity.ok() + : ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE); + } +} diff --git a/standardization/moher-api/server-adapters/spring-webflux/src/main/java/org/onap/dcaegen2/services/sdk/standardization/moher/adapters/springwebflux/MetricsController.java b/standardization/moher-api/server-adapters/spring-webflux/src/main/java/org/onap/dcaegen2/services/sdk/standardization/moher/adapters/springwebflux/MetricsController.java new file mode 100644 index 00000000..4ce4fd12 --- /dev/null +++ b/standardization/moher-api/server-adapters/spring-webflux/src/main/java/org/onap/dcaegen2/services/sdk/standardization/moher/adapters/springwebflux/MetricsController.java @@ -0,0 +1,43 @@ +/* + * ============LICENSE_START==================================== + * DCAEGEN2-SERVICES-SDK + * ========================================================= + * Copyright (C) 2019 Nokia. All rights reserved. + * ========================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END===================================== + */ + +package org.onap.dcaegen2.services.sdk.standardization.moher.adapters.springwebflux; + +import org.onap.dcaegen2.services.sdk.standardization.moher.metrics.api.Metrics; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import reactor.core.publisher.Mono; + +@RestController +@RequestMapping(value = "/metrics", produces = MediaType.TEXT_PLAIN_VALUE) +public class MetricsController { + private final Metrics metrics; + + public MetricsController(Metrics metrics) { + this.metrics = metrics; + } + + @GetMapping + public Mono prometheusMetrics() { + return metrics.collect(); + } +} diff --git a/standardization/moher-api/server-adapters/spring-webflux/src/test/java/org/onap/dcaegen2/services/sdk/standardization/moher/adapters/springwebflux/HealthControllerIT.java b/standardization/moher-api/server-adapters/spring-webflux/src/test/java/org/onap/dcaegen2/services/sdk/standardization/moher/adapters/springwebflux/HealthControllerIT.java new file mode 100644 index 00000000..236cc16f --- /dev/null +++ b/standardization/moher-api/server-adapters/spring-webflux/src/test/java/org/onap/dcaegen2/services/sdk/standardization/moher/adapters/springwebflux/HealthControllerIT.java @@ -0,0 +1,81 @@ +/* + * ============LICENSE_START==================================== + * DCAEGEN2-SERVICES-SDK + * ========================================================= + * Copyright (C) 2019 Nokia. All rights reserved. + * ========================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END===================================== + */ + +package org.onap.dcaegen2.services.sdk.standardization.moher.adapters.springwebflux; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.concurrent.atomic.AtomicReference; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import org.junit.jupiter.api.Test; +import org.onap.dcaegen2.services.sdk.standardization.moher.health.api.AliveMessage; +import org.onap.dcaegen2.services.sdk.standardization.moher.health.api.GsonAdaptersHealth; +import org.onap.dcaegen2.services.sdk.standardization.moher.health.api.Health; +import org.onap.dcaegen2.services.sdk.standardization.moher.health.api.HealthProvider; +import org.springframework.http.MediaType; +import org.springframework.test.web.reactive.server.WebTestClient; + +import reactor.core.publisher.Mono; + +class HealthControllerIT { + + private final AtomicReference currentHealth = new AtomicReference<>(); + private final HealthProvider healthProvider = () -> Mono.fromCallable(currentHealth::get); + private final HealthController sut = HealthController.create(healthProvider); + private final WebTestClient client = WebTestClient.bindToController(sut).build(); + private final Gson gsonForDeserialization = new GsonBuilder().registerTypeAdapterFactory(new GsonAdaptersHealth()) + .create(); + + @Test + void readinessProbeShouldReturnOkWhenHealthy() { + final Health expectedHealth = Health.createHealthy("Ready to go"); + currentHealth.set(expectedHealth); + + client.get().uri("/health/ready").accept(MediaType.APPLICATION_JSON).exchange().expectStatus().isOk() + .expectBody(String.class).value(body -> { + final Health actualHealth = gsonForDeserialization.fromJson(body, Health.class); + assertThat(actualHealth).isEqualTo(expectedHealth); + }); + } + + @Test + void readinessProbeShouldReturnUnavailableWhenNotHealthy() { + final Health expectedHealth = Health.createUnhealthy("Waiting for CBS update"); + currentHealth.set(expectedHealth); + + client.get().uri("/health/ready").accept(MediaType.APPLICATION_JSON).exchange().expectStatus() + .is5xxServerError().expectBody(String.class).value(body -> { + final Health actualHealth = gsonForDeserialization.fromJson(body, Health.class); + assertThat(actualHealth).isEqualTo(expectedHealth); + }); + } + + @Test + void livenessProbeShouldAlwaysReturnOk() { + client.get().uri("/health/alive").accept(MediaType.APPLICATION_JSON).exchange().expectStatus().isOk() + .expectBody(String.class).value(body -> { + assertThat(body).isEqualTo(AliveMessage.ALIVE_MESSAGE_JSON); + }); + } + +} diff --git a/standardization/moher-api/server-adapters/spring-webflux/src/test/java/org/onap/dcaegen2/services/sdk/standardization/moher/adapters/springwebflux/MetricsControllerIT.java b/standardization/moher-api/server-adapters/spring-webflux/src/test/java/org/onap/dcaegen2/services/sdk/standardization/moher/adapters/springwebflux/MetricsControllerIT.java new file mode 100644 index 00000000..32d00449 --- /dev/null +++ b/standardization/moher-api/server-adapters/spring-webflux/src/test/java/org/onap/dcaegen2/services/sdk/standardization/moher/adapters/springwebflux/MetricsControllerIT.java @@ -0,0 +1,48 @@ +/* + * ============LICENSE_START==================================== + * DCAEGEN2-SERVICES-SDK + * ========================================================= + * Copyright (C) 2019 Nokia. All rights reserved. + * ========================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END===================================== + */ + +package org.onap.dcaegen2.services.sdk.standardization.moher.adapters.springwebflux; + + +import static org.assertj.core.api.Assertions.assertThat; + +import io.micrometer.prometheus.PrometheusMeterRegistry; +import org.junit.jupiter.api.Test; +import org.onap.dcaegen2.services.sdk.standardization.moher.metrics.api.Metrics; +import org.onap.dcaegen2.services.sdk.standardization.moher.metrics.api.MetricsFactory; +import org.springframework.http.MediaType; +import org.springframework.test.web.reactive.server.WebTestClient; + +class MetricsControllerIT { + + private final PrometheusMeterRegistry defaultRegistry = MetricsFactory.createDefaultRegistry(); + private final Metrics metrics = MetricsFactory.createMetrics(defaultRegistry); + private final MetricsController sut = new MetricsController(metrics); + private final WebTestClient client = WebTestClient.bindToController(sut).build(); + + @Test + void prometheusMetrics() { + client.get().uri("/metrics").accept(MediaType.ALL).exchange() + .expectStatus().isOk() + .expectHeader().contentTypeCompatibleWith(MediaType.TEXT_PLAIN) + .expectBody(String.class) + .value(body -> assertThat(body).contains("system_cpu").contains("jvm_classes")); + } +} diff --git a/standardization/moher-api/server-adapters/spring/pom.xml b/standardization/moher-api/server-adapters/spring/pom.xml deleted file mode 100644 index 52f1dad2..00000000 --- a/standardization/moher-api/server-adapters/spring/pom.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - 4.0.0 - - dcaegen2-sdk-moher-server-adapters - org.onap.dcaegen2.services.sdk - 1.2.0-SNAPSHOT - - - Monitoring and Healthcheck :: Server Adapters :: Spring - MoHeR Spring server adapter - dcae-sdk-moher-spring - - \ No newline at end of file -- cgit 1.2.3-korg