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