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 bt...@apache.org on 2019/10/01 02:09:32 UTC

[james-project] 03/03: JAMES-2563 Implement ElasticSearch healthCheck

This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit e0cfb68a2a5a4a676e7402035ce21e8e18b033a3
Author: Jeroen Reijn <j....@gmail.com>
AuthorDate: Tue Oct 16 22:12:59 2018 +0200

    JAMES-2563 Implement ElasticSearch healthCheck
---
 backends-common/elasticsearch/pom.xml              |  4 +
 .../backends/es/ElasticSearchHealthCheck.java      | 90 ++++++++++++++++++++++
 .../james/backends/es/DockerElasticSearch.java     | 15 +++-
 .../es/ElasticSearchHealthCheckConnectionTest.java | 44 ++++++++---
 .../backends/es/ElasticSearchHealthCheckTest.java  | 79 +++++++++++++++++++
 .../modules/mailbox/ElasticSearchClientModule.java | 23 ++++++
 6 files changed, 244 insertions(+), 11 deletions(-)

diff --git a/backends-common/elasticsearch/pom.xml b/backends-common/elasticsearch/pom.xml
index ea60bba..911297c 100644
--- a/backends-common/elasticsearch/pom.xml
+++ b/backends-common/elasticsearch/pom.xml
@@ -30,6 +30,10 @@
     <dependencies>
         <dependency>
             <groupId>${james.groupId}</groupId>
+            <artifactId>james-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
             <artifactId>james-server-util</artifactId>
         </dependency>
         <dependency>
diff --git a/backends-common/elasticsearch/src/main/java/org/apache/james/backends/es/ElasticSearchHealthCheck.java b/backends-common/elasticsearch/src/main/java/org/apache/james/backends/es/ElasticSearchHealthCheck.java
new file mode 100644
index 0000000..1305420
--- /dev/null
+++ b/backends-common/elasticsearch/src/main/java/org/apache/james/backends/es/ElasticSearchHealthCheck.java
@@ -0,0 +1,90 @@
+/****************************************************************
+ * 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.backends.es;
+
+import java.io.IOException;
+import java.util.Set;
+
+import javax.inject.Inject;
+
+import org.apache.commons.lang3.NotImplementedException;
+import org.apache.james.core.healthcheck.ComponentName;
+import org.apache.james.core.healthcheck.HealthCheck;
+import org.apache.james.core.healthcheck.Result;
+import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest;
+import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
+import org.elasticsearch.client.RequestOptions;
+import org.elasticsearch.client.Requests;
+import org.elasticsearch.client.RestHighLevelClient;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.annotations.VisibleForTesting;
+
+
+public class ElasticSearchHealthCheck implements HealthCheck {
+    private static final Logger LOGGER = LoggerFactory.getLogger(ElasticSearchHealthCheck.class);
+    private static final ComponentName COMPONENT_NAME = new ComponentName("ElasticSearch Backend");
+
+    private final Set<IndexName> indexNames;
+    private final RestHighLevelClient client;
+
+    @Inject
+    ElasticSearchHealthCheck(RestHighLevelClient client, Set<IndexName> indexNames) {
+        this.client = client;
+        this.indexNames = indexNames;
+    }
+
+    @Override
+    public ComponentName componentName() {
+        return COMPONENT_NAME;
+    }
+
+    @Override
+    public Result check() {
+        String[] indices = indexNames.stream()
+            .map(IndexName::getValue)
+            .toArray(String[]::new);
+        ClusterHealthRequest request = Requests.clusterHealthRequest(indices);
+
+        try {
+            ClusterHealthResponse response = client.cluster()
+                .health(request, RequestOptions.DEFAULT);
+
+            return toHealthCheckResult(response);
+        } catch (IOException e) {
+            LOGGER.error("Error while contacting cluster", e);
+            return Result.unhealthy(COMPONENT_NAME, "Error while contacting cluster. Check James server logs.");
+        }
+    }
+
+    @VisibleForTesting
+    Result toHealthCheckResult(ClusterHealthResponse response) {
+        switch (response.getStatus()) {
+            case GREEN:
+            case YELLOW:
+                return Result.healthy(COMPONENT_NAME);
+            case RED:
+                return Result.unhealthy(COMPONENT_NAME, response.getClusterName() + " status is RED");
+            default:
+                throw new NotImplementedException("Un-handled ElasticSearch cluster status");
+        }
+    }
+}
diff --git a/backends-common/elasticsearch/src/test/java/org/apache/james/backends/es/DockerElasticSearch.java b/backends-common/elasticsearch/src/test/java/org/apache/james/backends/es/DockerElasticSearch.java
index 9398dae..e3365b2 100644
--- a/backends-common/elasticsearch/src/test/java/org/apache/james/backends/es/DockerElasticSearch.java
+++ b/backends-common/elasticsearch/src/test/java/org/apache/james/backends/es/DockerElasticSearch.java
@@ -114,12 +114,23 @@ public class DockerElasticSearch {
         }
     }
 
+    public ElasticSearchConfiguration configuration(Optional<Duration> requestTimeout) {
+        return ElasticSearchConfiguration.builder()
+            .addHost(getHttpHost())
+            .requestTimeout(requestTimeout)
+            .build();
+    }
+
     public ElasticSearchConfiguration configuration() {
-        return ElasticSearchConfiguration.builder().addHost(getHttpHost()).build();
+        return configuration(Optional.empty());
     }
 
     public ClientProvider clientProvider() {
-        return new ClientProvider(configuration());
+        return new ClientProvider(configuration(Optional.empty()));
+    }
+
+    public ClientProvider clientProvider(Duration requestTimeout) {
+        return new ClientProvider(configuration(Optional.of(requestTimeout)));
     }
 
     private ElasticSearchAPI esAPI() {
diff --git a/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/ElasticSearchClientModule.java b/backends-common/elasticsearch/src/test/java/org/apache/james/backends/es/ElasticSearchHealthCheckConnectionTest.java
similarity index 50%
copy from server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/ElasticSearchClientModule.java
copy to backends-common/elasticsearch/src/test/java/org/apache/james/backends/es/ElasticSearchHealthCheckConnectionTest.java
index d6a6c11..448390f 100644
--- a/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/ElasticSearchClientModule.java
+++ b/backends-common/elasticsearch/src/test/java/org/apache/james/backends/es/ElasticSearchHealthCheckConnectionTest.java
@@ -16,21 +16,47 @@
  * specific language governing permissions and limitations      *
  * under the License.                                           *
  ****************************************************************/
+package org.apache.james.backends.es;
 
-package org.apache.james.modules.mailbox;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.time.Duration;
 
-import org.apache.james.backends.es.ClientProvider;
 import org.elasticsearch.client.RestHighLevelClient;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableSet;
+
+public class ElasticSearchHealthCheckConnectionTest {
+    private static final Duration REQUEST_TIMEOUT = Duration.ofSeconds(5);
+
+    @Rule
+    public DockerElasticSearchRule elasticSearch = new DockerElasticSearchRule();
+    private ElasticSearchHealthCheck elasticSearchHealthCheck;
 
-import com.google.inject.AbstractModule;
-import com.google.inject.Scopes;
+    @Before
+    public void setUp() {
+        RestHighLevelClient client = elasticSearch.getDockerElasticSearch().clientProvider(REQUEST_TIMEOUT).get();
 
-public class ElasticSearchClientModule extends AbstractModule {
+        elasticSearchHealthCheck = new ElasticSearchHealthCheck(client, ImmutableSet.of());
+    }
 
-    @Override
-    protected void configure() {
-        bind(ClientProvider.class).in(Scopes.SINGLETON);
-        bind(RestHighLevelClient.class).toProvider(ClientProvider.class);
+    @Test
+    public void checkShouldSucceedWhenElasticSearchIsRunning() {
+        assertThat(elasticSearchHealthCheck.check().isHealthy()).isTrue();
     }
 
+    @Test
+    public void checkShouldFailWhenElasticSearchIsPaused() {
+
+        elasticSearch.getDockerElasticSearch().pause();
+
+        try {
+            assertThat(elasticSearchHealthCheck.check().isUnHealthy()).isTrue();
+        } finally {
+            elasticSearch.getDockerElasticSearch().unpause();
+        }
+    }
 }
diff --git a/backends-common/elasticsearch/src/test/java/org/apache/james/backends/es/ElasticSearchHealthCheckTest.java b/backends-common/elasticsearch/src/test/java/org/apache/james/backends/es/ElasticSearchHealthCheckTest.java
new file mode 100644
index 0000000..feef5da
--- /dev/null
+++ b/backends-common/elasticsearch/src/test/java/org/apache/james/backends/es/ElasticSearchHealthCheckTest.java
@@ -0,0 +1,79 @@
+/****************************************************************
+ * 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.backends.es;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
+import org.elasticsearch.cluster.ClusterName;
+import org.elasticsearch.cluster.ClusterState;
+import org.elasticsearch.cluster.block.ClusterBlocks;
+import org.elasticsearch.cluster.health.ClusterHealthStatus;
+import org.elasticsearch.cluster.node.DiscoveryNodes;
+import org.elasticsearch.cluster.routing.RoutingTable;
+import org.junit.Before;
+import org.junit.Test;
+import org.testcontainers.shaded.com.google.common.collect.ImmutableSet;
+
+public class ElasticSearchHealthCheckTest {
+    private ElasticSearchHealthCheck healthCheck;
+
+    @Before
+    public void setup() {
+        healthCheck = new ElasticSearchHealthCheck(null, ImmutableSet.of());
+    }
+
+    @Test
+    public void checkShouldReturnHealthyWhenElasticSearchClusterHealthStatusIsGreen() {
+        FakeClusterHealthResponse response = new FakeClusterHealthResponse(ClusterHealthStatus.GREEN);
+
+        assertThat(healthCheck.toHealthCheckResult(response).isHealthy()).isTrue();
+    }
+
+    @Test
+    public void checkShouldReturnUnHealthyWhenElasticSearchClusterHealthStatusIsRed() {
+        FakeClusterHealthResponse response = new FakeClusterHealthResponse(ClusterHealthStatus.RED);
+
+        assertThat(healthCheck.toHealthCheckResult(response).isUnHealthy()).isTrue();
+    }
+
+    @Test
+    public void checkShouldReturnHealthyWhenElasticSearchClusterHealthStatusIsYellow() {
+        FakeClusterHealthResponse response = new FakeClusterHealthResponse(ClusterHealthStatus.YELLOW);
+
+        assertThat(healthCheck.toHealthCheckResult(response).isHealthy()).isTrue();
+    }
+
+    private static class FakeClusterHealthResponse extends ClusterHealthResponse {
+        private final ClusterHealthStatus status;
+
+        private FakeClusterHealthResponse(ClusterHealthStatus clusterHealthStatus) {
+            super("fake-cluster", new String[0],
+                new ClusterState(new ClusterName("fake-cluster"), 0, null, null, RoutingTable.builder().build(),
+                    DiscoveryNodes.builder().build(),
+                    ClusterBlocks.builder().build(), null, false));
+            this.status = clusterHealthStatus;
+        }
+
+        @Override
+        public ClusterHealthStatus getStatus() {
+            return this.status;
+        }
+    }
+}
diff --git a/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/ElasticSearchClientModule.java b/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/ElasticSearchClientModule.java
index d6a6c11..7aab691 100644
--- a/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/ElasticSearchClientModule.java
+++ b/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/ElasticSearchClientModule.java
@@ -19,11 +19,22 @@
 
 package org.apache.james.modules.mailbox;
 
+import java.util.Set;
+
 import org.apache.james.backends.es.ClientProvider;
+import org.apache.james.backends.es.ElasticSearchHealthCheck;
+import org.apache.james.backends.es.IndexName;
+import org.apache.james.core.healthcheck.HealthCheck;
+import org.apache.james.mailbox.elasticsearch.ElasticSearchMailboxConfiguration;
+import org.apache.james.quota.search.elasticsearch.ElasticSearchQuotaConfiguration;
 import org.elasticsearch.client.RestHighLevelClient;
 
+import com.google.common.collect.ImmutableSet;
 import com.google.inject.AbstractModule;
+import com.google.inject.Provides;
 import com.google.inject.Scopes;
+import com.google.inject.Singleton;
+import com.google.inject.multibindings.Multibinder;
 
 public class ElasticSearchClientModule extends AbstractModule {
 
@@ -31,6 +42,18 @@ public class ElasticSearchClientModule extends AbstractModule {
     protected void configure() {
         bind(ClientProvider.class).in(Scopes.SINGLETON);
         bind(RestHighLevelClient.class).toProvider(ClientProvider.class);
+
+        Multibinder.newSetBinder(binder(), HealthCheck.class)
+            .addBinding()
+            .to(ElasticSearchHealthCheck.class);
     }
 
+    @Provides
+    @Singleton
+    Set<IndexName> provideIndexNames(ElasticSearchMailboxConfiguration mailboxConfiguration,
+                                     ElasticSearchQuotaConfiguration quotaConfiguration) {
+        return ImmutableSet.of(
+            mailboxConfiguration.getIndexMailboxName(),
+            quotaConfiguration.getIndexQuotaRatioName());
+    }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org