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

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

commit e30f399730bf88e872a0abc480541a922bbe58cf
Author: Quan Tran <[email protected]>
AuthorDate: Tue Sep 8 09:27:41 2026 +0700

    JAMES-4222 Add reusable Ceph S3 test container
    
    Introduce an always-on Testcontainers fixture backed by pinned Ceph 19.2.0 
Squid and cover basic S3 write/read compatibility. Later S3 compatibility work 
can reuse the container and JUnit extension.
---
 .../objectstorage/aws/DockerCephS3Container.java   | 79 ++++++++++++++++++++++
 .../aws/DockerCephS3ContainerTest.java             | 67 ++++++++++++++++++
 .../objectstorage/aws/DockerCephS3Extension.java   | 50 ++++++++++++++
 3 files changed, 196 insertions(+)

diff --git 
a/server/blob/blob-s3/src/test/java/org/apache/james/blob/objectstorage/aws/DockerCephS3Container.java
 
b/server/blob/blob-s3/src/test/java/org/apache/james/blob/objectstorage/aws/DockerCephS3Container.java
new file mode 100644
index 0000000000..8d3d19d39f
--- /dev/null
+++ 
b/server/blob/blob-s3/src/test/java/org/apache/james/blob/objectstorage/aws/DockerCephS3Container.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.blob.objectstorage.aws;
+
+import java.net.URI;
+import java.time.Duration;
+
+import org.apache.james.blob.api.BucketName;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.wait.strategy.Wait;
+import org.testcontainers.utility.DockerImageName;
+
+public class DockerCephS3Container {
+    private static final DockerImageName CEPH_IMAGE = 
DockerImageName.parse("quay.io/ceph/demo")
+        .withTag("main-30dc8b9a-squid-centos-stream9-x86_64");
+    private static final int RGW_PORT = 8080;
+
+    public static final String ACCESS_KEY = "james-access-key";
+    public static final String SECRET_KEY = "james-secret-key";
+    public static final Region REGION = Region.of("us-east-1");
+    public static final BucketName TEST_BUCKET = 
BucketName.of("james-ceph-test");
+
+    private final GenericContainer<?> container;
+
+    public DockerCephS3Container() {
+        container = new GenericContainer<>(CEPH_IMAGE)
+            .withExposedPorts(RGW_PORT)
+            .withEnv("CEPH_DEMO_UID", "james")
+            .withEnv("CEPH_DEMO_ACCESS_KEY", ACCESS_KEY)
+            .withEnv("CEPH_DEMO_SECRET_KEY", SECRET_KEY)
+            .withEnv("CEPH_DEMO_BUCKET", TEST_BUCKET.asString())
+            .withEnv("CEPH_PUBLIC_NETWORK", "0.0.0.0/0")
+            .withEnv("MON_IP", "127.0.0.1")
+            .withEnv("NETWORK_AUTO_DETECT", "4")
+            .withEnv("RGW_FRONTEND_PORT", Integer.toString(RGW_PORT))
+            .withEnv("RGW_NAME", "localhost")
+            .withCommand("demo")
+            
.waitingFor(Wait.forLogMessage(".*\\/opt\\/ceph-container\\/bin\\/demo: 
SUCCESS.*\\n", 1)
+                .withStartupTimeout(Duration.ofMinutes(5)))
+            .withCreateContainerCmdModifier(createContainerCmd -> 
createContainerCmd.withPlatform("linux/amd64"));
+    }
+
+    public void start() {
+        container.start();
+    }
+
+    public void stop() {
+        container.stop();
+    }
+
+    public URI getEndpoint() {
+        return URI.create("http://"; + container.getHost() + ":" + 
container.getMappedPort(RGW_PORT));
+    }
+
+    public AwsS3AuthConfiguration getAwsS3AuthConfiguration() {
+        return AwsS3AuthConfiguration.builder()
+            .endpoint(getEndpoint())
+            .accessKeyId(ACCESS_KEY)
+            .secretKey(SECRET_KEY)
+            .build();
+    }
+}
diff --git 
a/server/blob/blob-s3/src/test/java/org/apache/james/blob/objectstorage/aws/DockerCephS3ContainerTest.java
 
b/server/blob/blob-s3/src/test/java/org/apache/james/blob/objectstorage/aws/DockerCephS3ContainerTest.java
new file mode 100644
index 0000000000..6fc96665aa
--- /dev/null
+++ 
b/server/blob/blob-s3/src/test/java/org/apache/james/blob/objectstorage/aws/DockerCephS3ContainerTest.java
@@ -0,0 +1,67 @@
+/****************************************************************
+ * 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.apache.james.blob.api.BlobStoreDAOFixture.SHORT_BYTEARRAY;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.UUID;
+
+import org.apache.james.blob.api.TestBlobId;
+import org.apache.james.metrics.api.NoopGaugeRegistry;
+import org.apache.james.metrics.tests.RecordingMetricFactory;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+import reactor.core.publisher.Mono;
+
+@ExtendWith(DockerCephS3Extension.class)
+class DockerCephS3ContainerTest {
+    private static S3ClientFactory s3ClientFactory;
+    private static S3BlobStoreDAO testee;
+
+    @BeforeAll
+    static void setUp(DockerCephS3Container ceph) {
+        S3BlobStoreConfiguration configuration = 
S3BlobStoreConfiguration.builder()
+            .authConfiguration(ceph.getAwsS3AuthConfiguration())
+            .region(DockerCephS3Container.REGION)
+            .build();
+
+        s3ClientFactory = new S3ClientFactory(configuration, new 
RecordingMetricFactory(), new NoopGaugeRegistry());
+        testee = new S3BlobStoreDAO(s3ClientFactory, configuration, new 
TestBlobId.Factory(), S3RequestOption.DEFAULT);
+    }
+
+    @AfterAll
+    static void tearDown() {
+        s3ClientFactory.close();
+    }
+
+    @Test
+    void shouldSupportS3WriteAndRead() {
+        TestBlobId blobId = new TestBlobId(UUID.randomUUID().toString());
+
+        Mono.from(testee.save(DockerCephS3Container.TEST_BUCKET, blobId, 
SHORT_BYTEARRAY)).block();
+
+        
assertThat(Mono.from(testee.readBytes(DockerCephS3Container.TEST_BUCKET, 
blobId)).block())
+            .isEqualTo(SHORT_BYTEARRAY);
+    }
+}
diff --git 
a/server/blob/blob-s3/src/test/java/org/apache/james/blob/objectstorage/aws/DockerCephS3Extension.java
 
b/server/blob/blob-s3/src/test/java/org/apache/james/blob/objectstorage/aws/DockerCephS3Extension.java
new file mode 100644
index 0000000000..5c25fe0b04
--- /dev/null
+++ 
b/server/blob/blob-s3/src/test/java/org/apache/james/blob/objectstorage/aws/DockerCephS3Extension.java
@@ -0,0 +1,50 @@
+/****************************************************************
+ * 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 org.junit.jupiter.api.extension.AfterAllCallback;
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ParameterContext;
+import org.junit.jupiter.api.extension.ParameterResolver;
+
+public class DockerCephS3Extension implements BeforeAllCallback, 
AfterAllCallback, ParameterResolver {
+    private final DockerCephS3Container container = new 
DockerCephS3Container();
+
+    @Override
+    public void beforeAll(ExtensionContext context) {
+        container.start();
+    }
+
+    @Override
+    public void afterAll(ExtensionContext context) {
+        container.stop();
+    }
+
+    @Override
+    public boolean supportsParameter(ParameterContext parameterContext, 
ExtensionContext extensionContext) {
+        return parameterContext.getParameter().getType() == 
DockerCephS3Container.class;
+    }
+
+    @Override
+    public DockerCephS3Container resolveParameter(ParameterContext 
parameterContext, ExtensionContext extensionContext) {
+        return container;
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to