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