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
The following commit(s) were added to refs/heads/master by this push:
new 63740a8efd JAMES-2600 implement an object storage healthcheck (#1637)
63740a8efd is described below
commit 63740a8efd739fcdc9b040e1e2a6fb7a8e9cd519
Author: hungphan227 <[email protected]>
AuthorDate: Tue Jul 11 14:23:01 2023 +0700
JAMES-2600 implement an object storage healthcheck (#1637)
---
server/blob/blob-api/pom.xml | 8 +++
.../james/blob/api/ObjectStorageHealthCheck.java | 59 ++++++++++++++++
.../objectstorage/aws/DockerAwsS3Container.java | 15 ++++
.../blob/objectstorage/aws/S3HealthCheckTest.java | 82 ++++++++++++++++++++++
server/container/guice/distributed/pom.xml | 4 ++
.../modules/blobstore/BlobStoreModulesChooser.java | 4 ++
...itMQWebAdminServerIntegrationImmutableTest.java | 2 +-
7 files changed, 173 insertions(+), 1 deletion(-)
diff --git a/server/blob/blob-api/pom.xml b/server/blob/blob-api/pom.xml
index 8c779e796f..5613a52e17 100644
--- a/server/blob/blob-api/pom.xml
+++ b/server/blob/blob-api/pom.xml
@@ -33,6 +33,10 @@
<name>Apache James :: Server :: Blob :: API</name>
<dependencies>
+ <dependency>
+ <groupId>${james.groupId}</groupId>
+ <artifactId>james-core</artifactId>
+ </dependency>
<dependency>
<groupId>${james.groupId}</groupId>
<artifactId>james-server-util</artifactId>
@@ -61,6 +65,10 @@
<artifactId>commons-io</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>io.projectreactor.addons</groupId>
+ <artifactId>reactor-extra</artifactId>
+ </dependency>
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
diff --git
a/server/blob/blob-api/src/main/java/org/apache/james/blob/api/ObjectStorageHealthCheck.java
b/server/blob/blob-api/src/main/java/org/apache/james/blob/api/ObjectStorageHealthCheck.java
new file mode 100644
index 0000000000..555c1d5fed
--- /dev/null
+++
b/server/blob/blob-api/src/main/java/org/apache/james/blob/api/ObjectStorageHealthCheck.java
@@ -0,0 +1,59 @@
+/****************************************************************
+ * 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.blob.api;
+
+import java.time.Duration;
+
+import javax.inject.Inject;
+
+import org.apache.james.core.healthcheck.ComponentName;
+import org.apache.james.core.healthcheck.HealthCheck;
+import org.apache.james.core.healthcheck.Result;
+
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+public class ObjectStorageHealthCheck implements HealthCheck {
+
+ private static final Integer HEALTH_CHECK_TIMEOUT = 10;
+
+ private static final ComponentName COMPONENT_NAME = new
ComponentName("ObjectStorage");
+
+ private final BlobStoreDAO blobStoreDAO;
+
+ @Inject
+ public ObjectStorageHealthCheck(BlobStoreDAO blobStoreDAO) {
+ this.blobStoreDAO = blobStoreDAO;
+ }
+
+ @Override
+ public ComponentName componentName() {
+ return COMPONENT_NAME;
+ }
+
+ @Override
+ public Mono<Result> check() {
+ return Flux.from(blobStoreDAO.listBuckets())
+ .timeout(Duration.ofSeconds(HEALTH_CHECK_TIMEOUT))
+ .next()
+ .thenReturn(Result.healthy(COMPONENT_NAME))
+ .onErrorResume(e -> Mono.just(Result.unhealthy(COMPONENT_NAME,
"Error checking ObjectSotrage", e)));
+ }
+}
diff --git
a/server/blob/blob-s3/src/test/java/org/apache/james/blob/objectstorage/aws/DockerAwsS3Container.java
b/server/blob/blob-s3/src/test/java/org/apache/james/blob/objectstorage/aws/DockerAwsS3Container.java
index 98e0e8070b..d47469054c 100644
---
a/server/blob/blob-s3/src/test/java/org/apache/james/blob/objectstorage/aws/DockerAwsS3Container.java
+++
b/server/blob/blob-s3/src/test/java/org/apache/james/blob/objectstorage/aws/DockerAwsS3Container.java
@@ -61,6 +61,21 @@ public class DockerAwsS3Container {
awsS3Container.stop();
}
+ public void pause() {
+
awsS3Container.getDockerClient().pauseContainerCmd(awsS3Container.getContainerId()).exec();
+ }
+
+ public void unpause() {
+
awsS3Container.getDockerClient().unpauseContainerCmd(awsS3Container.getContainerId()).exec();
+ }
+
+ public boolean isPaused() {
+ return
awsS3Container.getDockerClient().inspectContainerCmd(awsS3Container.getContainerId())
+ .exec()
+ .getState()
+ .getPaused();
+ }
+
public Host getHost() {
return Host.from(getIp(), getPort());
}
diff --git
a/server/blob/blob-s3/src/test/java/org/apache/james/blob/objectstorage/aws/S3HealthCheckTest.java
b/server/blob/blob-s3/src/test/java/org/apache/james/blob/objectstorage/aws/S3HealthCheckTest.java
new file mode 100644
index 0000000000..93569ac599
--- /dev/null
+++
b/server/blob/blob-s3/src/test/java/org/apache/james/blob/objectstorage/aws/S3HealthCheckTest.java
@@ -0,0 +1,82 @@
+/****************************************************************
+ * 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.blob.objectstorage.aws;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.apache.james.blob.api.BlobStoreDAO;
+import org.apache.james.blob.api.ObjectStorageHealthCheck;
+import org.apache.james.blob.api.TestBlobId;
+import org.apache.james.core.healthcheck.Result;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+@ExtendWith(DockerAwsS3Extension.class)
+public class S3HealthCheckTest {
+
+ private ObjectStorageHealthCheck s3HealthCheck;
+
+ @BeforeEach
+ void setUp(DockerAwsS3Container dockerAwsS3) {
+ AwsS3AuthConfiguration authConfiguration =
AwsS3AuthConfiguration.builder()
+ .endpoint(dockerAwsS3.getEndpoint())
+ .accessKeyId(DockerAwsS3Container.ACCESS_KEY_ID)
+ .secretKey(DockerAwsS3Container.SECRET_ACCESS_KEY)
+ .build();
+
+ S3BlobStoreConfiguration s3Configuration =
S3BlobStoreConfiguration.builder()
+ .authConfiguration(authConfiguration)
+ .region(dockerAwsS3.dockerAwsS3().region())
+ .build();
+
+ BlobStoreDAO s3BlobStoreDAO = new S3BlobStoreDAO(s3Configuration, new
TestBlobId.Factory());
+ s3HealthCheck = new ObjectStorageHealthCheck(s3BlobStoreDAO);
+ }
+
+ @AfterEach
+ void reset(DockerAwsS3Container dockerAwsS3) {
+ if (dockerAwsS3.isPaused()) {
+ dockerAwsS3.unpause();
+ }
+ }
+
+ @Test
+ void checkShouldReturnHealthyWhenS3IsRunning() {
+ Result check = s3HealthCheck.check().block();
+ assertThat(check.isHealthy()).isTrue();
+ }
+
+ @Test
+ void checkShouldReturnUnhealthyWhenS3IsNotRunning(DockerAwsS3Container
dockerAwsS3) {
+ dockerAwsS3.pause();
+ Result check = s3HealthCheck.check().block();
+ assertThat(check.isUnHealthy()).isTrue();
+ }
+
+ @Test
+ void checkShouldDetectWhenS3Recovered(DockerAwsS3Container dockerAwsS3) {
+ dockerAwsS3.pause();
+ dockerAwsS3.unpause();
+ Result check = s3HealthCheck.check().block();
+ assertThat(check.isHealthy()).isTrue();
+ }
+}
diff --git a/server/container/guice/distributed/pom.xml
b/server/container/guice/distributed/pom.xml
index 35b8d63e7e..d593089fd8 100644
--- a/server/container/guice/distributed/pom.xml
+++ b/server/container/guice/distributed/pom.xml
@@ -51,6 +51,10 @@
<groupId>${james.groupId}</groupId>
<artifactId>blob-aes</artifactId>
</dependency>
+ <dependency>
+ <groupId>${james.groupId}</groupId>
+ <artifactId>blob-api</artifactId>
+ </dependency>
<dependency>
<groupId>${james.groupId}</groupId>
<artifactId>blob-deduplication-gc-guice</artifactId>
diff --git
a/server/container/guice/distributed/src/main/java/org/apache/james/modules/blobstore/BlobStoreModulesChooser.java
b/server/container/guice/distributed/src/main/java/org/apache/james/modules/blobstore/BlobStoreModulesChooser.java
index a3793f1c43..ccb4625c41 100644
---
a/server/container/guice/distributed/src/main/java/org/apache/james/modules/blobstore/BlobStoreModulesChooser.java
+++
b/server/container/guice/distributed/src/main/java/org/apache/james/modules/blobstore/BlobStoreModulesChooser.java
@@ -26,9 +26,11 @@ import org.apache.james.blob.aes.AESBlobStoreDAO;
import org.apache.james.blob.aes.CryptoConfig;
import org.apache.james.blob.api.BlobStore;
import org.apache.james.blob.api.BlobStoreDAO;
+import org.apache.james.blob.api.ObjectStorageHealthCheck;
import org.apache.james.blob.cassandra.CassandraBlobStoreDAO;
import org.apache.james.blob.cassandra.cache.CachedBlobStore;
import org.apache.james.blob.objectstorage.aws.S3BlobStoreDAO;
+import org.apache.james.core.healthcheck.HealthCheck;
import
org.apache.james.modules.blobstore.validation.BlobStoreConfigurationValidationStartUpCheck.StorageStrategySupplier;
import
org.apache.james.modules.blobstore.validation.StoragePolicyConfigurationSanityEnforcementModule;
import org.apache.james.modules.mailbox.BlobStoreAPIModule;
@@ -45,6 +47,7 @@ import com.google.inject.AbstractModule;
import com.google.inject.Module;
import com.google.inject.Provides;
import com.google.inject.Singleton;
+import com.google.inject.multibindings.Multibinder;
import com.google.inject.name.Named;
import com.google.inject.name.Names;
@@ -68,6 +71,7 @@ public class BlobStoreModulesChooser {
install(new DefaultBucketModule());
bind(BlobStoreDAO.class).annotatedWith(Names.named(UNENCRYPTED)).to(S3BlobStoreDAO.class);
+ Multibinder.newSetBinder(binder(),
HealthCheck.class).addBinding().to(ObjectStorageHealthCheck.class);
}
}
diff --git
a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQWebAdminServerIntegrationImmutableTest.java
b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQWebAdminServerIntegrationImmutableTest.java
index 8563add768..d549dff0ab 100644
---
a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQWebAdminServerIntegrationImmutableTest.java
+++
b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQWebAdminServerIntegrationImmutableTest.java
@@ -138,6 +138,6 @@ class RabbitMQWebAdminServerIntegrationImmutableTest
extends WebAdminServerInteg
"RabbitMQ backend", "RabbitMQMailQueueDeadLetterQueueHealthCheck",
"RabbitMQEventBusDeadLetterQueueHealthCheck", "MailReceptionCheck",
"Cassandra backend", "EventDeadLettersHealthCheck",
"MessageFastViewProjection",
- "RabbitMQMailQueue BrowseStart", "OpenSearch Backend");
+ "RabbitMQMailQueue BrowseStart", "OpenSearch Backend",
"ObjectStorage");
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]