This is an automated email from the ASF dual-hosted git repository. rcordier pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/james-project.git
commit 5ed599e4ed1130dc4b730f1405a5819ed6585c51 Author: TungTV <vtt...@linagora.com> AuthorDate: Tue Nov 19 10:17:14 2024 +0700 JAMES-4085 Introduce S3MinioExtension - support https --- server/blob/blob-s3/pom.xml | 6 ++ .../blob/objectstorage/aws/S3MinioDocker.java | 109 +++++++++++++++++++++ .../blob/objectstorage/aws/S3MinioExtension.java | 59 +++++++++++ .../james/blob/objectstorage/aws/S3MinioTest.java | 40 ++------ .../blob-s3/src/test/resources/minio/private.key | 5 + .../blob-s3/src/test/resources/minio/public.crt | 14 +++ 6 files changed, 199 insertions(+), 34 deletions(-) diff --git a/server/blob/blob-s3/pom.xml b/server/blob/blob-s3/pom.xml index 2de0f6a4c1..01573fdaf7 100644 --- a/server/blob/blob-s3/pom.xml +++ b/server/blob/blob-s3/pom.xml @@ -56,6 +56,12 @@ <groupId>${james.groupId}</groupId> <artifactId>james-core</artifactId> </dependency> + <dependency> + <groupId>${james.groupId}</groupId> + <artifactId>james-server-guice-common</artifactId> + <type>test-jar</type> + <scope>test</scope> + </dependency> <dependency> <groupId>${james.groupId}</groupId> <artifactId>james-server-lifecycle-api</artifactId> diff --git a/server/blob/blob-s3/src/test/java/org/apache/james/blob/objectstorage/aws/S3MinioDocker.java b/server/blob/blob-s3/src/test/java/org/apache/james/blob/objectstorage/aws/S3MinioDocker.java new file mode 100644 index 0000000000..54bf3e5a91 --- /dev/null +++ b/server/blob/blob-s3/src/test/java/org/apache/james/blob/objectstorage/aws/S3MinioDocker.java @@ -0,0 +1,109 @@ +/**************************************************************** + * 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.time.Duration; +import java.util.UUID; + +import org.apache.http.client.utils.URIBuilder; +import org.testcontainers.containers.BindMode; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.Network; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.utility.DockerImageName; + +import com.github.fge.lambdas.Throwing; +import com.google.common.base.Preconditions; + +public class S3MinioDocker { + + public static final DockerImageName DOCKER_IMAGE_NAME = DockerImageName.parse("minio/minio") + .withTag("RELEASE.2024-10-13T13-34-11Z"); + + public static final int MINIO_PORT = 9000; + public static final int MINIO_WEB_ADMIN_PORT = 9090; + public static final String MINIO_ROOT_USER = "minio"; + public static final String MINIO_ROOT_PASSWORD = "minio123"; + + private final GenericContainer<?> container; + + public S3MinioDocker() { + this.container = getContainer(); + } + + public S3MinioDocker(Network network) { + this.container = getContainer() + .withNetwork(network); + } + + private GenericContainer<?> getContainer() { + return new GenericContainer<>(DOCKER_IMAGE_NAME) + .withExposedPorts(MINIO_PORT, MINIO_WEB_ADMIN_PORT) + .withEnv("MINIO_ROOT_USER", MINIO_ROOT_USER) + .withEnv("MINIO_ROOT_PASSWORD", MINIO_ROOT_PASSWORD) + .withCommand("server", "--certs-dir", "/opt/minio/certs", "/data", "--console-address", ":" + MINIO_WEB_ADMIN_PORT) + .withClasspathResourceMapping("/minio/private.key", + "/opt/minio/certs/private.key", + BindMode.READ_ONLY) + .withClasspathResourceMapping("/minio/public.crt", + "/opt/minio/certs/public.crt", + BindMode.READ_ONLY) + .waitingFor(Wait.forLogMessage(".*MinIO Object Storage Server.*", 1) + .withStartupTimeout(Duration.ofMinutes(2))) + .withCreateContainerCmdModifier(createContainerCmd -> createContainerCmd.withName("james-minio-s3-test-" + UUID.randomUUID())); + } + + public void start() { + if (!container.isRunning()) { + container.start(); + setupMC(); + } + } + + public void stop() { + container.stop(); + } + + public AwsS3AuthConfiguration getAwsS3AuthConfiguration() { + Preconditions.checkArgument(container.isRunning(), "Container is not running"); + return AwsS3AuthConfiguration.builder() + .endpoint(Throwing.supplier(() -> new URIBuilder() + .setScheme("https") + .setHost(container.getHost()) + .setPort(container.getMappedPort(MINIO_PORT)) + .build()).get()) + .accessKeyId(MINIO_ROOT_USER) + .secretKey(MINIO_ROOT_PASSWORD) + .trustAll(true) + .build(); + } + + private void setupMC() { + Preconditions.checkArgument(container.isRunning(), "Container is not running"); + Throwing.runnable(() -> container.execInContainer("mc", "alias", "set", "--insecure", "james", "https://localhost:9000", MINIO_ROOT_USER, MINIO_ROOT_PASSWORD)).run(); + } + + public void flushAll() { + // Remove all objects + Throwing.runnable(() -> container.execInContainer("mc", "--insecure", "rm", "--recursive", "--force", "--dangerous", "james/")).run(); + // Remove all buckets + Throwing.runnable(() -> container.execInContainer("mc", "--insecure", "rb", "--force", "--dangerous", "james/")).run(); + } +} diff --git a/server/blob/blob-s3/src/test/java/org/apache/james/blob/objectstorage/aws/S3MinioExtension.java b/server/blob/blob-s3/src/test/java/org/apache/james/blob/objectstorage/aws/S3MinioExtension.java new file mode 100644 index 0000000000..12d95b3170 --- /dev/null +++ b/server/blob/blob-s3/src/test/java/org/apache/james/blob/objectstorage/aws/S3MinioExtension.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.objectstorage.aws; + +import org.apache.james.GuiceModuleTestExtension; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.api.extension.ParameterResolutionException; + +public class S3MinioExtension implements GuiceModuleTestExtension { + + private static final S3MinioDocker s3MinioDocker = new S3MinioDocker(); + + @Override + public void beforeAll(ExtensionContext extensionContext) { + s3MinioDocker.start(); + } + + @Override + public void afterAll(ExtensionContext extensionContext) { + s3MinioDocker.stop(); + } + + @Override + public void afterEach(ExtensionContext extensionContext) { + s3MinioDocker.flushAll(); + } + + @Override + public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException { + return parameterContext.getParameter().getType() == S3MinioDocker.class; + } + + @Override + public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException { + return s3MinioDocker; + } + + public S3MinioDocker minioDocker() { + return s3MinioDocker; + } +} diff --git a/server/blob/blob-s3/src/test/java/org/apache/james/blob/objectstorage/aws/S3MinioTest.java b/server/blob/blob-s3/src/test/java/org/apache/james/blob/objectstorage/aws/S3MinioTest.java index 6f372240f3..ad506127d2 100644 --- a/server/blob/blob-s3/src/test/java/org/apache/james/blob/objectstorage/aws/S3MinioTest.java +++ b/server/blob/blob-s3/src/test/java/org/apache/james/blob/objectstorage/aws/S3MinioTest.java @@ -22,15 +22,11 @@ package org.apache.james.blob.objectstorage.aws; import static org.apache.james.blob.api.BlobStoreDAOFixture.SHORT_BYTEARRAY; import static org.apache.james.blob.api.BlobStoreDAOFixture.TEST_BLOB_ID; import static org.apache.james.blob.api.BlobStoreDAOFixture.TEST_BUCKET_NAME; -import static org.apache.james.blob.objectstorage.aws.DockerAwsS3Container.ACCESS_KEY_ID; -import static org.apache.james.blob.objectstorage.aws.DockerAwsS3Container.SECRET_ACCESS_KEY; import static org.apache.james.blob.objectstorage.aws.S3BlobStoreConfiguration.UPLOAD_RETRY_EXCEPTION_PREDICATE; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import java.net.URI; import java.util.Optional; -import java.util.UUID; import java.util.concurrent.ExecutionException; import org.apache.james.blob.api.BlobId; @@ -40,50 +36,31 @@ 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.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; -import org.testcontainers.containers.GenericContainer; -import org.testcontainers.junit.jupiter.Container; -import org.testcontainers.junit.jupiter.Testcontainers; +import org.junit.jupiter.api.extension.RegisterExtension; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.util.retry.Retry; import software.amazon.awssdk.services.s3.model.S3Exception; -@Testcontainers public class S3MinioTest implements BlobStoreDAOContract { - private static final String MINIO_IMAGE = "minio/minio"; - private static final String MINIO_TAG = "RELEASE.2024-10-13T13-34-11Z"; - private static final String MINIO_IMAGE_FULL = MINIO_IMAGE + ":" + MINIO_TAG; - private static final int MINIO_PORT = 9000; - private static S3BlobStoreDAO testee; + @RegisterExtension + static S3MinioExtension minoExtension = new S3MinioExtension(); + private static S3BlobStoreDAO testee; private static S3ClientFactory s3ClientFactory; - @Container - private static final GenericContainer<?> minioContainer = new GenericContainer<>(MINIO_IMAGE_FULL) - .withExposedPorts(MINIO_PORT) - .withEnv("MINIO_ROOT_USER", ACCESS_KEY_ID) - .withEnv("MINIO_ROOT_PASSWORD", SECRET_ACCESS_KEY) - .withCommand("server", "/data", "--console-address", ":9090") - .withCreateContainerCmdModifier(createContainerCmd -> createContainerCmd.withName("james-minio-s3-test-" + UUID.randomUUID())); - - @BeforeAll static void setUp() { - AwsS3AuthConfiguration authConfiguration = AwsS3AuthConfiguration.builder() - .endpoint(URI.create(String.format("http://%s:%s/", minioContainer.getHost(), minioContainer.getMappedPort(MINIO_PORT)))) - .accessKeyId(ACCESS_KEY_ID) - .secretKey(SECRET_ACCESS_KEY) - .build(); + AwsS3AuthConfiguration awsS3AuthConfiguration = minoExtension.minioDocker().getAwsS3AuthConfiguration(); S3BlobStoreConfiguration s3Configuration = S3BlobStoreConfiguration.builder() - .authConfiguration(authConfiguration) + .authConfiguration(awsS3AuthConfiguration) .region(DockerAwsS3Container.REGION) .uploadRetrySpec(Optional.of(Retry.backoff(3, java.time.Duration.ofSeconds(1)) .filter(UPLOAD_RETRY_EXCEPTION_PREDICATE))) @@ -98,11 +75,6 @@ public class S3MinioTest implements BlobStoreDAOContract { s3ClientFactory.close(); } - @AfterEach - void tearDown() { - testee.deleteAllBuckets().block(); - } - @BeforeEach void beforeEach() throws Exception { // Why? https://github.com/apache/james-project/pull/1981#issuecomment-2380396460 diff --git a/server/blob/blob-s3/src/test/resources/minio/private.key b/server/blob/blob-s3/src/test/resources/minio/private.key new file mode 100644 index 0000000000..f829a5dd37 --- /dev/null +++ b/server/blob/blob-s3/src/test/resources/minio/private.key @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgDhp6TEYrEFHMHusJ +Xstem4lh0czPokiWEdAzzMXRXxGhRANCAAQ7jB4mm+Rieqr3lQfdc1xoPsgAltNP +CvUYDZyu5903CkLiDuhBU6UWvQP5F19XFt95RkaYDPvc6ka0485NWFMb +-----END PRIVATE KEY----- diff --git a/server/blob/blob-s3/src/test/resources/minio/public.crt b/server/blob/blob-s3/src/test/resources/minio/public.crt new file mode 100644 index 0000000000..5a6013846c --- /dev/null +++ b/server/blob/blob-s3/src/test/resources/minio/public.crt @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICFjCCAbugAwIBAgIRAK8/sg1OIuWOQV2gWo9vDQUwCgYIKoZIzj0EAwIwTTEc +MBoGA1UEChMTQ2VydGdlbiBEZXZlbG9wbWVudDEtMCsGA1UECwwkdHVuZ3R2QE1h +Y0Jvb2stUHJvLmxvY2FsIChUcmFuIFR1bmcpMB4XDTI0MDkyNjAyNDcwNFoXDTI1 +MDkyNjAyNDcwNFowTTEcMBoGA1UEChMTQ2VydGdlbiBEZXZlbG9wbWVudDEtMCsG +A1UECwwkdHVuZ3R2QE1hY0Jvb2stUHJvLmxvY2FsIChUcmFuIFR1bmcpMFkwEwYH +KoZIzj0CAQYIKoZIzj0DAQcDQgAEO4weJpvkYnqq95UH3XNcaD7IAJbTTwr1GA2c +rufdNwpC4g7oQVOlFr0D+RdfVxbfeUZGmAz73OpGtOPOTVhTG6N8MHowDgYDVR0P +AQH/BAQDAgKkMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMBAf8w +HQYDVR0OBBYEFKIu0NT9bG853GA+o10GsbDOi9zMMCMGA1UdEQQcMBqCCWxvY2Fs +aG9zdIINczNtaW5pby5sb2NhbDAKBggqhkjOPQQDAgNJADBGAiEAxLfNK4gPWw0G +8QAv0QzARY0L5+8Lmpf5SwVzrjZ+zosCIQCA41tgccZ20+W1igansLkqnSTcY8Nw +mtKwMTxUEwNRTw== +-----END CERTIFICATE----- --------------------------------------------------------------------- To unsubscribe, e-mail: notifications-unsubscr...@james.apache.org For additional commands, e-mail: notifications-h...@james.apache.org