You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by ad...@apache.org on 2018/08/27 12:18:22 UTC
[5/8] james-project git commit: JAMES-2526 Introduce health checks
routes in WebAdmin
JAMES-2526 Introduce health checks routes in WebAdmin
Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/c9cbe2c3
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/c9cbe2c3
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/c9cbe2c3
Branch: refs/heads/master
Commit: c9cbe2c3554dc5d1474185ccf04f4150bd1d5c13
Parents: f30ffeb
Author: Antoine Duprat <ad...@linagora.com>
Authored: Wed Aug 22 15:34:24 2018 +0200
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Mon Aug 27 14:18:06 2018 +0200
----------------------------------------------------------------------
server/protocols/webadmin/webadmin-core/pom.xml | 4 +
.../webadmin/routes/HealthCheckRoutes.java | 118 ++++++++++++++++
.../webadmin/routes/HealthCheckRoutesTest.java | 135 +++++++++++++++++++
3 files changed, 257 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/james-project/blob/c9cbe2c3/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 6d27db4..3641203 100644
--- a/server/protocols/webadmin/webadmin-core/pom.xml
+++ b/server/protocols/webadmin/webadmin-core/pom.xml
@@ -35,6 +35,10 @@
<dependencies>
<dependency>
<groupId>${james.groupId}</groupId>
+ <artifactId>james-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${james.groupId}</groupId>
<artifactId>james-server-jwt</artifactId>
</dependency>
<dependency>
http://git-wip-us.apache.org/repos/asf/james-project/blob/c9cbe2c3/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
new file mode 100644
index 0000000..d9c7e67
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/routes/HealthCheckRoutes.java
@@ -0,0 +1,118 @@
+/****************************************************************
+ * 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.routes;
+
+import java.util.List;
+import java.util.Set;
+
+import javax.inject.Inject;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+import org.apache.james.core.healthcheck.HealthCheck;
+import org.apache.james.core.healthcheck.Result;
+import org.apache.james.webadmin.Routes;
+import org.eclipse.jetty.http.HttpStatus;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.steveash.guavate.Guavate;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+import spark.Service;
+
+@Api(tags = "Healthchecks")
+@Path(HealthCheckRoutes.HEALTHCHECK)
+public class HealthCheckRoutes implements Routes {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(HealthCheckRoutes.class);
+
+ public static final String HEALTHCHECK = "/healthcheck";
+
+
+ private final Set<HealthCheck> healthChecks;
+ private Service service;
+
+ @Inject
+ public HealthCheckRoutes(Set<HealthCheck> healthChecks) {
+ this.healthChecks = healthChecks;
+ }
+
+ @Override
+ public void define(Service service) {
+ this.service = service;
+
+ validateHealthchecks();
+ }
+
+ @GET
+ @ApiOperation(value = "Validate all health checks")
+ @ApiResponses(value = {
+ @ApiResponse(code = HttpStatus.OK_200, message = "OK"),
+ @ApiResponse(code = HttpStatus.INTERNAL_SERVER_ERROR_500,
+ message = "Internal server error - When one check has failed.")
+ })
+ public void validateHealthchecks() {
+ service.get(HEALTHCHECK,
+ (request, response) -> {
+ List<Result> anyUnhealthyOrDegraded = retrieveUnhealthyOrDegradedHealthChecks();
+
+ anyUnhealthyOrDegraded.forEach(this::logFailedCheck);
+ response.status(getCorrespondingStatusCode(anyUnhealthyOrDegraded));
+ return response;
+ });
+ }
+
+ private int getCorrespondingStatusCode(List<Result> anyUnhealthy) {
+ if (anyUnhealthy.isEmpty()) {
+ return HttpStatus.OK_200;
+ } else {
+ return HttpStatus.INTERNAL_SERVER_ERROR_500;
+ }
+ }
+
+ private void logFailedCheck(Result result) {
+ switch (result.getStatus()) {
+ case UNHEALTHY:
+ LOGGER.error("HealthCheck failed for {} : {}",
+ result.getComponentName().getName(),
+ result.getCause().orElse(""));
+ break;
+ case DEGRADED:
+ LOGGER.warn("HealthCheck is unstable for {} : {}",
+ result.getComponentName().getName(),
+ result.getCause().orElse(""));
+ break;
+ case HEALTHY:
+ // Here only to fix a warning, such cases are already filtered
+ break;
+ }
+ }
+
+ private List<Result> retrieveUnhealthyOrDegradedHealthChecks() {
+ return healthChecks.stream()
+ .map(HealthCheck::check)
+ .filter(result -> result.isUnHealthy() || result.isDegraded())
+ .collect(Guavate.toImmutableList());
+ }
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/c9cbe2c3/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
new file mode 100644
index 0000000..e0d5b83
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-core/src/test/java/org/apache/james/webadmin/routes/HealthCheckRoutesTest.java
@@ -0,0 +1,135 @@
+/****************************************************************
+ * 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.routes;
+
+import static io.restassured.RestAssured.when;
+import static org.apache.james.webadmin.WebAdminServer.NO_CONFIGURATION;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.james.core.healthcheck.ComponentName;
+import org.apache.james.core.healthcheck.HealthCheck;
+import org.apache.james.core.healthcheck.Result;
+import org.apache.james.metrics.logger.DefaultMetricFactory;
+import org.apache.james.webadmin.WebAdminServer;
+import org.apache.james.webadmin.WebAdminUtils;
+import org.eclipse.jetty.http.HttpStatus;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import io.restassured.RestAssured;
+
+public class HealthCheckRoutesTest {
+
+ private static final ComponentName COMPONENT_NAME_1 = new ComponentName("component-1");
+ private static final ComponentName COMPONENT_NAME_2 = new ComponentName("component-2");
+
+ private static HealthCheck healthCheck(Result result) {
+ return new HealthCheck() {
+ @Override
+ public ComponentName componentName() {
+ return result.getComponentName();
+ }
+
+ @Override
+ public Result check() {
+ return result;
+ }
+ };
+ }
+
+ private WebAdminServer webAdminServer;
+ private Set<HealthCheck> healthChecks;
+
+ @Before
+ public void setUp() throws Exception {
+ healthChecks = new HashSet<>();
+
+ webAdminServer = WebAdminUtils.createWebAdminServer(
+ new DefaultMetricFactory(),
+ new HealthCheckRoutes(healthChecks));
+
+ webAdminServer.configure(NO_CONFIGURATION);
+ webAdminServer.await();
+
+ RestAssured.requestSpecification = WebAdminUtils.buildRequestSpecification(webAdminServer)
+ .setBasePath(HealthCheckRoutes.HEALTHCHECK)
+ .build();
+ }
+
+ @After
+ public void tearDown() {
+ webAdminServer.destroy();
+ }
+
+ @Test
+ public void validateHealthchecksShouldReturnOkWhenNoHealthChecks() {
+ when()
+ .get()
+ .then()
+ .statusCode(HttpStatus.OK_200);
+ }
+
+ @Test
+ public void validateHealthchecksShouldReturnOkWhenHealthChecksAreHealthy() {
+ healthChecks.add(healthCheck(Result.healthy(COMPONENT_NAME_1)));
+ healthChecks.add(healthCheck(Result.healthy(COMPONENT_NAME_2)));
+
+ when()
+ .get()
+ .then()
+ .statusCode(HttpStatus.OK_200);
+ }
+
+ @Test
+ public void validateHealthchecksShouldReturnInternalErrorWhenOneHealthCheckIsUnhealthy() {
+ healthChecks.add(healthCheck(Result.unhealthy(COMPONENT_NAME_1, "cause")));
+ healthChecks.add(healthCheck(Result.healthy(COMPONENT_NAME_2)));
+
+ when()
+ .get()
+ .then()
+ .statusCode(HttpStatus.INTERNAL_SERVER_ERROR_500);
+ }
+
+ @Test
+ public void validateHealthchecksShouldReturnInternalErrorWhenAllHealthChecksAreUnhealthy() {
+ healthChecks.add(healthCheck(Result.unhealthy(COMPONENT_NAME_1, "cause")));
+ healthChecks.add(healthCheck(Result.unhealthy(COMPONENT_NAME_2)));
+
+ when()
+ .get()
+ .then()
+ .statusCode(HttpStatus.INTERNAL_SERVER_ERROR_500);
+ }
+
+ @Test
+ public void validateHealthchecksShouldReturnInternalErrorWhenOneHealthCheckIsDegraded() {
+ healthChecks.add(healthCheck(Result.degraded(COMPONENT_NAME_1, "cause")));
+ healthChecks.add(healthCheck(Result.healthy(COMPONENT_NAME_2)));
+
+ when()
+ .get()
+ .then()
+ .statusCode(HttpStatus.INTERNAL_SERVER_ERROR_500);
+ }
+}
\ No newline at end of file
---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org