Repository: james-project Updated Branches: refs/heads/master 633b0c263 -> 719e254ac
JAMES-2576 add body for aggregation healthcheck Project: http://git-wip-us.apache.org/repos/asf/james-project/repo Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/719e254a Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/719e254a Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/719e254a Branch: refs/heads/master Commit: 719e254ac565cdfa74ba8f3802ea75a5d7f333b6 Parents: 633b0c2 Author: datph <[email protected]> Authored: Wed Oct 31 14:27:33 2018 +0700 Committer: datph <[email protected]> Committed: Fri Nov 2 09:36:51 2018 +0700 ---------------------------------------------------------------------- .../james/core/healthcheck/ResultStatus.java | 14 ++ .../core/healthcheck/ResultStatusTest.java | 62 ++++++ server/protocols/webadmin/webadmin-core/pom.xml | 5 + ...HeathCheckAggregationExecutionResultDto.java | 45 ++++ .../webadmin/routes/HealthCheckRoutes.java | 49 +++-- .../webadmin/routes/HealthCheckRoutesTest.java | 204 ++++++++++++++++--- 6 files changed, 335 insertions(+), 44 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/james-project/blob/719e254a/core/src/main/java/org/apache/james/core/healthcheck/ResultStatus.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/james/core/healthcheck/ResultStatus.java b/core/src/main/java/org/apache/james/core/healthcheck/ResultStatus.java index d962755..c07b298 100644 --- a/core/src/main/java/org/apache/james/core/healthcheck/ResultStatus.java +++ b/core/src/main/java/org/apache/james/core/healthcheck/ResultStatus.java @@ -18,6 +18,8 @@ ****************************************************************/ package org.apache.james.core.healthcheck; +import com.google.common.base.Preconditions; + public enum ResultStatus { HEALTHY("healthy"), DEGRADED("degraded"), @@ -32,4 +34,16 @@ public enum ResultStatus { public String getValue() { return value; } + + public static ResultStatus merge(ResultStatus resultStatus1, ResultStatus resultStatus2) { + Preconditions.checkNotNull(resultStatus1); + Preconditions.checkNotNull(resultStatus2); + if (resultStatus1 == UNHEALTHY || resultStatus2 == UNHEALTHY) { + return UNHEALTHY; + } + if (resultStatus1 == DEGRADED || resultStatus2 == DEGRADED) { + return DEGRADED; + } + return HEALTHY; + } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/james-project/blob/719e254a/core/src/test/java/org/apache/james/core/healthcheck/ResultStatusTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/james/core/healthcheck/ResultStatusTest.java b/core/src/test/java/org/apache/james/core/healthcheck/ResultStatusTest.java new file mode 100644 index 0000000..1003ef5 --- /dev/null +++ b/core/src/test/java/org/apache/james/core/healthcheck/ResultStatusTest.java @@ -0,0 +1,62 @@ +/**************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you 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. * + ****************************************************************/ + +package org.apache.james.core.healthcheck; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.assertj.core.api.SoftAssertions; +import org.junit.Test; + +public class ResultStatusTest { + + @Test + public void mergeReturnHealthyWhenMergeWithHealthy() { + assertThat(ResultStatus.merge(ResultStatus.HEALTHY, ResultStatus.HEALTHY)) + .isEqualTo(ResultStatus.HEALTHY); + } + + @Test + public void mergeReturnUnHealthyWhenMergeWithUnHealthy() { + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(ResultStatus.merge(ResultStatus.HEALTHY, ResultStatus.UNHEALTHY)) + .isEqualTo(ResultStatus.UNHEALTHY); + softly.assertThat(ResultStatus.merge(ResultStatus.DEGRADED, ResultStatus.UNHEALTHY)) + .isEqualTo(ResultStatus.UNHEALTHY); + softly.assertThat(ResultStatus.merge(ResultStatus.UNHEALTHY, ResultStatus.HEALTHY)) + .isEqualTo(ResultStatus.UNHEALTHY); + softly.assertThat(ResultStatus.merge(ResultStatus.UNHEALTHY, ResultStatus.DEGRADED)) + .isEqualTo(ResultStatus.UNHEALTHY); + softly.assertThat(ResultStatus.merge(ResultStatus.UNHEALTHY, ResultStatus.UNHEALTHY)) + .isEqualTo(ResultStatus.UNHEALTHY); + }); + } + + @Test + public void mergeReturnDegradedWhenMergeWithDegraded() { + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(ResultStatus.merge(ResultStatus.HEALTHY, ResultStatus.DEGRADED)) + .isEqualTo(ResultStatus.DEGRADED); + softly.assertThat(ResultStatus.merge(ResultStatus.DEGRADED, ResultStatus.DEGRADED)) + .isEqualTo(ResultStatus.DEGRADED); + softly.assertThat(ResultStatus.merge(ResultStatus.DEGRADED, ResultStatus.HEALTHY)) + .isEqualTo(ResultStatus.DEGRADED); + }); + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/719e254a/server/protocols/webadmin/webadmin-core/pom.xml ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-core/pom.xml b/server/protocols/webadmin/webadmin-core/pom.xml index 3641203..ec1b223 100644 --- a/server/protocols/webadmin/webadmin-core/pom.xml +++ b/server/protocols/webadmin/webadmin-core/pom.xml @@ -143,6 +143,11 @@ <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> </dependency> + <dependency> + <groupId>net.javacrumbs.json-unit</groupId> + <artifactId>json-unit-assertj</artifactId> + <scope>test</scope> + </dependency> </dependencies> <build> http://git-wip-us.apache.org/repos/asf/james-project/blob/719e254a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/dto/HeathCheckAggregationExecutionResultDto.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/dto/HeathCheckAggregationExecutionResultDto.java b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/dto/HeathCheckAggregationExecutionResultDto.java new file mode 100644 index 0000000..120d4f5 --- /dev/null +++ b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/dto/HeathCheckAggregationExecutionResultDto.java @@ -0,0 +1,45 @@ +/**************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you 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. * + ****************************************************************/ + +package org.apache.james.webadmin.dto; + +import java.util.List; + +import org.apache.james.core.healthcheck.ResultStatus; + +import com.google.common.collect.ImmutableList; + +public class HeathCheckAggregationExecutionResultDto { + + private final ResultStatus status; + private final ImmutableList<HealthCheckExecutionResultDto> checks; + + public HeathCheckAggregationExecutionResultDto(ResultStatus status, ImmutableList<HealthCheckExecutionResultDto> checks) { + this.status = status; + this.checks = checks; + } + + public String getStatus() { + return status.getValue(); + } + + public List<HealthCheckExecutionResultDto> getChecks() { + return checks; + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/719e254a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/routes/HealthCheckRoutes.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/routes/HealthCheckRoutes.java b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/routes/HealthCheckRoutes.java index 0ac1a3f..8d53407 100644 --- a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/routes/HealthCheckRoutes.java +++ b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/routes/HealthCheckRoutes.java @@ -28,9 +28,11 @@ import javax.ws.rs.Path; import org.apache.james.core.healthcheck.HealthCheck; import org.apache.james.core.healthcheck.Result; +import org.apache.james.core.healthcheck.ResultStatus; import org.apache.james.webadmin.PublicRoutes; import org.apache.james.webadmin.dto.HealthCheckDto; import org.apache.james.webadmin.dto.HealthCheckExecutionResultDto; +import org.apache.james.webadmin.dto.HeathCheckAggregationExecutionResultDto; import org.apache.james.webadmin.utils.ErrorResponder; import org.apache.james.webadmin.utils.JsonTransformer; import org.eclipse.jetty.http.HttpStatus; @@ -38,6 +40,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.github.steveash.guavate.Guavate; +import com.google.common.collect.ImmutableList; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; @@ -77,7 +80,7 @@ public class HealthCheckRoutes implements PublicRoutes { @Override public void define(Service service) { - service.get(HEALTHCHECK, this::validateHealthchecks, jsonTransformer); + service.get(HEALTHCHECK, this::validateHealthChecks, jsonTransformer); service.get(HEALTHCHECK + "/checks/:" + PARAM_COMPONENT_NAME, this::performHealthCheckForComponent, jsonTransformer); service.get(HEALTHCHECK + CHECKS, this::getHealthChecks, jsonTransformer); } @@ -89,12 +92,11 @@ public class HealthCheckRoutes implements PublicRoutes { @ApiResponse(code = HttpStatus.INTERNAL_SERVER_ERROR_500, message = "Internal server error - When one check has failed.") }) - public Object validateHealthchecks(Request request, Response response) { - List<Result> anyUnhealthyOrDegraded = retrieveUnhealthyOrDegradedHealthChecks(); - - anyUnhealthyOrDegraded.forEach(this::logFailedCheck); - response.status(getCorrespondingStatusCode(anyUnhealthyOrDegraded)); - return response; + public Object validateHealthChecks(Request request, Response response) { + ImmutableList<Result> results = executeHealthChecks(); + ResultStatus status = retrieveAggregationStatus(results); + response.status(getCorrespondingStatusCode(status)); + return new HeathCheckAggregationExecutionResultDto(status, mapResultToDto(results)); } @GET @@ -119,7 +121,7 @@ public class HealthCheckRoutes implements PublicRoutes { Result result = healthCheck.check(); logFailedCheck(result); - response.status(getCorrespondingStatusCode(result)); + response.status(getCorrespondingStatusCode(result.getStatus())); return new HealthCheckExecutionResultDto(result); } @@ -133,17 +135,9 @@ public class HealthCheckRoutes implements PublicRoutes { .map(healthCheck -> new HealthCheckDto(healthCheck.componentName())) .collect(Guavate.toImmutableList()); } - - private int getCorrespondingStatusCode(List<Result> anyUnhealthy) { - if (anyUnhealthy.isEmpty()) { - return HttpStatus.OK_200; - } else { - return HttpStatus.INTERNAL_SERVER_ERROR_500; - } - } - private int getCorrespondingStatusCode(Result result) { - switch (result.getStatus()) { + private int getCorrespondingStatusCode(ResultStatus resultStatus) { + switch (resultStatus) { case HEALTHY: return HttpStatus.OK_200; case DEGRADED: @@ -171,11 +165,24 @@ public class HealthCheckRoutes implements PublicRoutes { } } - private List<Result> retrieveUnhealthyOrDegradedHealthChecks() { + private ImmutableList<Result> executeHealthChecks() { return healthChecks.stream() .map(HealthCheck::check) - .filter(result -> result.isUnHealthy() || result.isDegraded()) - .collect(Guavate.toImmutableList()); + .peek(this::logFailedCheck) + .collect(ImmutableList.toImmutableList()); + } + + private ResultStatus retrieveAggregationStatus(List<Result> results) { + return results.stream() + .map(Result::getStatus) + .reduce(ResultStatus::merge) + .orElse(ResultStatus.HEALTHY); + } + + private ImmutableList<HealthCheckExecutionResultDto> mapResultToDto(List<Result> results) { + return results.stream() + .map(HealthCheckExecutionResultDto::new) + .collect(ImmutableList.toImmutableList()); } private HaltException throw404(String componentName) { http://git-wip-us.apache.org/repos/asf/james-project/blob/719e254a/server/protocols/webadmin/webadmin-core/src/test/java/org/apache/james/webadmin/routes/HealthCheckRoutesTest.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-core/src/test/java/org/apache/james/webadmin/routes/HealthCheckRoutesTest.java b/server/protocols/webadmin/webadmin-core/src/test/java/org/apache/james/webadmin/routes/HealthCheckRoutesTest.java index 7bed3ab..2396bc8 100644 --- a/server/protocols/webadmin/webadmin-core/src/test/java/org/apache/james/webadmin/routes/HealthCheckRoutesTest.java +++ b/server/protocols/webadmin/webadmin-core/src/test/java/org/apache/james/webadmin/routes/HealthCheckRoutesTest.java @@ -21,6 +21,7 @@ package org.apache.james.webadmin.routes; import static io.restassured.RestAssured.given; import static io.restassured.RestAssured.when; +import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson; import static org.apache.james.webadmin.WebAdminServer.NO_CONFIGURATION; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.equalTo; @@ -45,6 +46,7 @@ import org.junit.Before; import org.junit.Test; import io.restassured.RestAssured; +import net.javacrumbs.jsonunit.core.Option; public class HealthCheckRoutesTest { @@ -95,55 +97,211 @@ public class HealthCheckRoutesTest { } @Test - public void validateHealthchecksShouldReturnOkWhenNoHealthChecks() { - when() + public void validateHealthChecksShouldReturnOkWhenNoHealthChecks() { + String healthCheckBody = + "{\"status\":\"healthy\"," + + " \"checks\":[]}"; + + String retrieveBody = when() .get() .then() - .statusCode(HttpStatus.OK_200); + .statusCode(HttpStatus.OK_200) + .extract() + .body().asString(); + + assertThatJson(retrieveBody) + .isEqualTo(healthCheckBody); } @Test - public void validateHealthchecksShouldReturnOkWhenHealthChecksAreHealthy() { + public void validateHealthChecksShouldReturnOkWhenHealthChecksAreHealthy() { healthChecks.add(healthCheck(Result.healthy(COMPONENT_NAME_1))); healthChecks.add(healthCheck(Result.healthy(COMPONENT_NAME_2))); + String healthCheckBody = + "{\"status\": \"healthy\"," + + " \"checks\": [" + + " {" + + " \"componentName\": \"component-1\"," + + " \"escapedComponentName\": \"component-1\"," + + " \"status\": \"healthy\"," + + " \"cause\": null" + + " }," + + " {" + + " \"componentName\": \"component-2\"," + + " \"escapedComponentName\": \"component-2\"," + + " \"status\": \"healthy\"," + + " \"cause\": null" + + "}]}"; - when() - .get() - .then() - .statusCode(HttpStatus.OK_200); + String retrieveBody = when() + .get() + .then() + .statusCode(HttpStatus.OK_200) + .extract() + .body().asString(); + + assertThatJson(retrieveBody) + .when(Option.IGNORING_ARRAY_ORDER) + .isEqualTo(healthCheckBody); } @Test - public void validateHealthchecksShouldReturnInternalErrorWhenOneHealthCheckIsUnhealthy() { + public void validateHealthChecksShouldReturnInternalErrorWhenOneHealthCheckIsUnhealthy() { healthChecks.add(healthCheck(Result.unhealthy(COMPONENT_NAME_1, "cause"))); healthChecks.add(healthCheck(Result.healthy(COMPONENT_NAME_2))); + String healthCheckBody = + "{\"status\": \"unhealthy\"," + + " \"checks\": [" + + " {" + + " \"componentName\": \"component-1\"," + + " \"escapedComponentName\": \"component-1\"," + + " \"status\": \"unhealthy\"," + + " \"cause\": \"cause\"" + + " }," + + " {" + + " \"componentName\": \"component-2\"," + + " \"escapedComponentName\": \"component-2\"," + + " \"status\": \"healthy\"," + + " \"cause\": null" + + "}]}"; - when() - .get() - .then() - .statusCode(HttpStatus.INTERNAL_SERVER_ERROR_500); + String retrieveBody = when() + .get() + .then() + .statusCode(HttpStatus.INTERNAL_SERVER_ERROR_500) + .extract() + .body().asString(); + + assertThatJson(retrieveBody) + .when(Option.IGNORING_ARRAY_ORDER) + .isEqualTo(healthCheckBody); } @Test - public void validateHealthchecksShouldReturnInternalErrorWhenAllHealthChecksAreUnhealthy() { + public void validateHealthChecksShouldReturnInternalErrorWhenAllHealthChecksAreUnhealthy() { healthChecks.add(healthCheck(Result.unhealthy(COMPONENT_NAME_1, "cause"))); healthChecks.add(healthCheck(Result.unhealthy(COMPONENT_NAME_2))); + String healthCheckBody = + "{\"status\": \"unhealthy\"," + + " \"checks\": [" + + " {" + + " \"componentName\": \"component-1\"," + + " \"escapedComponentName\": \"component-1\"," + + " \"status\": \"unhealthy\"," + + " \"cause\": \"cause\"" + + " }," + + " {" + + " \"componentName\": \"component-2\"," + + " \"escapedComponentName\": \"component-2\"," + + " \"status\": \"unhealthy\"," + + " \"cause\": null" + + "}]}"; - when() - .get() - .then() - .statusCode(HttpStatus.INTERNAL_SERVER_ERROR_500); + String retrieveBody = when() + .get() + .then() + .statusCode(HttpStatus.INTERNAL_SERVER_ERROR_500) + .extract() + .body().asString(); + + assertThatJson(retrieveBody) + .when(Option.IGNORING_ARRAY_ORDER) + .isEqualTo(healthCheckBody); } @Test - public void validateHealthchecksShouldReturnInternalErrorWhenOneHealthCheckIsDegraded() { + public void validateHealthChecksShouldReturnInternalErrorWhenOneHealthCheckIsDegraded() { healthChecks.add(healthCheck(Result.degraded(COMPONENT_NAME_1, "cause"))); healthChecks.add(healthCheck(Result.healthy(COMPONENT_NAME_2))); + String healthCheckBody = + "{\"status\": \"degraded\"," + + " \"checks\": [" + + " {" + + " \"componentName\": \"component-1\"," + + " \"escapedComponentName\": \"component-1\"," + + " \"status\": \"degraded\"," + + " \"cause\": \"cause\"" + + " }," + + " {" + + " \"componentName\": \"component-2\"," + + " \"escapedComponentName\": \"component-2\"," + + " \"status\": \"healthy\"," + + " \"cause\": null" + + "}]}"; - when() - .get() - .then() - .statusCode(HttpStatus.INTERNAL_SERVER_ERROR_500); + String retrieveBody = when() + .get() + .then() + .statusCode(HttpStatus.INTERNAL_SERVER_ERROR_500) + .extract() + .body().asString(); + + assertThatJson(retrieveBody) + .when(Option.IGNORING_ARRAY_ORDER) + .isEqualTo(healthCheckBody); + } + + @Test + public void validateHealthChecksShouldReturnInternalErrorWhenAllHealthCheckAreDegraded() { + healthChecks.add(healthCheck(Result.degraded(COMPONENT_NAME_1, "cause"))); + healthChecks.add(healthCheck(Result.degraded(COMPONENT_NAME_2, "cause"))); + String healthCheckBody = + "{\"status\": \"degraded\"," + + " \"checks\": [" + + " {" + + " \"componentName\": \"component-1\"," + + " \"escapedComponentName\": \"component-1\"," + + " \"status\": \"degraded\"," + + " \"cause\": \"cause\"" + + " }," + + " {" + + " \"componentName\": \"component-2\"," + + " \"escapedComponentName\": \"component-2\"," + + " \"status\": \"degraded\"," + + " \"cause\": \"cause\"" + + "}]}"; + + String retrieveBody = when() + .get() + .then() + .statusCode(HttpStatus.INTERNAL_SERVER_ERROR_500) + .extract() + .body().asString(); + + assertThatJson(retrieveBody) + .when(Option.IGNORING_ARRAY_ORDER) + .isEqualTo(healthCheckBody); + } + + @Test + public void validateHealthChecksShouldReturnStatusUnHealthyWhenOneIsUnHealthyAndOtherIsDegraded() { + healthChecks.add(healthCheck(Result.degraded(COMPONENT_NAME_1, "cause"))); + healthChecks.add(healthCheck(Result.unhealthy(COMPONENT_NAME_2, "cause"))); + String healthCheckBody = + "{\"status\": \"unhealthy\"," + + " \"checks\": [" + + " {" + + " \"componentName\": \"component-1\"," + + " \"escapedComponentName\": \"component-1\"," + + " \"status\": \"degraded\"," + + " \"cause\": \"cause\"" + + " }," + + " {" + + " \"componentName\": \"component-2\"," + + " \"escapedComponentName\": \"component-2\"," + + " \"status\": \"unhealthy\"," + + " \"cause\": \"cause\"" + + "}]}"; + String retrieveBody = when() + .get() + .then() + .statusCode(HttpStatus.INTERNAL_SERVER_ERROR_500) + .extract() + .body().asString(); + + assertThatJson(retrieveBody) + .when(Option.IGNORING_ARRAY_ORDER) + .isEqualTo(healthCheckBody); } @Test --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
