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 <45198168+hungphan...@users.noreply.github.com>
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: notifications-unsubscr...@james.apache.org
For additional commands, e-mail: notifications-h...@james.apache.org

Reply via email to