-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Add Ceph support #2687
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Ceph support #2687
Changes from all commits
e45c6b1
ef38b42
678ae39
b5ab8cf
b9a5ca1
736437e
b788c4e
1366a29
5fd4f56
2417ebc
480817a
34f72fb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| # Ceph Module | ||
|
|
||
| !!! note | ||
| This module is INCUBATING. While it is ready for use and operational in the current version of Testcontainers, it is possible that it may receive breaking changes in the future. See [our contributing guidelines](/contributing/#incubating-modules) for more information on our incubating modules policy. | ||
|
|
||
| Testcontainers module for [Ceph](https://ceph.io/). | ||
|
|
||
| ## Ceph vs Localstack for simulating S3 | ||
|
|
||
| One possible usage for the Ceph module is to take advantage of its S3 compatibility. | ||
|
|
||
| For simulating S3 you may wish to also consider the [Localstack](./localstack.md) module. | ||
| Ceph and Localstack provide good compatibility with the S3 API, and we encourage you to evaluate both. | ||
|
|
||
| Localstack is likely to be a better choice if you require simulation of other AWS services along with S3. | ||
|
|
||
| ## Usage example | ||
|
|
||
| Creating a Ceph container: | ||
|
|
||
| <!--codeinclude--> | ||
| [Creating a Ceph container](../../modules/ceph/src/test/java/org/testcontainers/containers/CephContainerTest.java) inside_block:creating_container | ||
| <!--/codeinclude--> | ||
|
|
||
| Configuring an S3 Client to use Ceph: | ||
|
|
||
| <!--codeinclude--> | ||
| [Configuring an S3 Client to use Ceph](../../modules/ceph/src/test/java/org/testcontainers/containers/CephContainerTest.java) inside_block:setting_up_s3_client | ||
| <!--/codeinclude--> | ||
|
|
||
| ## Adding this module to your project dependencies | ||
|
|
||
| Add the following dependency to your `pom.xml`/`build.gradle` file: | ||
|
|
||
| ```groovy tab='Gradle' | ||
| testCompile "org.testcontainers:ceph:{{latest_version}}" | ||
| ``` | ||
|
|
||
| ```xml tab='Maven' | ||
| <dependency> | ||
| <groupId>org.testcontainers</groupId> | ||
| <artifactId>ceph</artifactId> | ||
| <version>{{latest_version}}</version> | ||
| <scope>test</scope> | ||
| </dependency> | ||
| ``` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| description = "Testcontainers :: Ceph" | ||
|
|
||
| dependencies { | ||
| compile project(':testcontainers') | ||
|
|
||
| provided 'com.amazonaws:aws-java-sdk-s3:1.11.495' | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,117 @@ | ||
| package org.testcontainers.containers; | ||
|
|
||
| import com.amazonaws.auth.AWSCredentialsProvider; | ||
| import com.amazonaws.auth.AWSStaticCredentialsProvider; | ||
| import com.amazonaws.auth.BasicAWSCredentials; | ||
| import com.amazonaws.client.builder.AwsClientBuilder; | ||
| import lombok.AllArgsConstructor; | ||
| import lombok.Getter; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.testcontainers.containers.output.Slf4jLogConsumer; | ||
| import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; | ||
|
|
||
| import java.time.Duration; | ||
|
|
||
| @Slf4j | ||
| @Getter | ||
| public class CephContainer extends GenericContainer<CephContainer> { | ||
|
|
||
| public static final String IMAGE = "ceph/daemon"; | ||
| public static final String DEFAULT_TAG = "v3.2.13-stable-3.2-mimic-centos-7"; | ||
|
|
||
| public static final int S3_PORT = 8080; | ||
| public static final int REST_API_PORT = 5000; | ||
|
|
||
| private String awsAccessKey = "ceph"; | ||
|
|
||
| private String awsSecretKey = "ceph"; | ||
|
|
||
| private String bucketName = "CEPH"; | ||
|
|
||
| private String rgwName = "localhost"; | ||
|
|
||
| private String demoUid = "ceph"; | ||
|
|
||
| private NetworkAutoDetectMode networkAutoDetectMode = NetworkAutoDetectMode.IPV4_ONLY; | ||
|
|
||
| public CephContainer() { | ||
| super(IMAGE + ":" + DEFAULT_TAG); | ||
| } | ||
|
|
||
| public CephContainer(String dockerImageName) { | ||
| super(dockerImageName); | ||
| } | ||
|
|
||
| @Override | ||
| protected void configure() { | ||
| withEnv("RGW_NAME", rgwName); | ||
| withEnv("NETWORK_AUTO_DETECT", networkAutoDetectMode.value); | ||
| withEnv("CEPH_DAEMON", "demo"); | ||
| withEnv("CEPH_DEMO_UID", demoUid); | ||
| withEnv("CEPH_DEMO_ACCESS_KEY", awsAccessKey); | ||
| withEnv("CEPH_DEMO_SECRET_KEY", awsSecretKey); | ||
| withEnv("CEPH_DEMO_BUCKET", bucketName); | ||
| withExposedPorts(REST_API_PORT, S3_PORT); | ||
| waitingFor( | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd move "non-dynamic" config to the constructor, otherwise the users won't be able to override it |
||
| new HttpWaitStrategy() | ||
| .forPath("/") | ||
| .forPort(REST_API_PORT) | ||
| .forStatusCode(200) | ||
| .withStartupTimeout(Duration.ofMinutes(5)) | ||
| ); | ||
| withLogConsumer(new Slf4jLogConsumer(log)); | ||
| } | ||
|
|
||
| public AWSCredentialsProvider getAWSCredentialsProvider() { | ||
| return new AWSStaticCredentialsProvider( | ||
| new BasicAWSCredentials(awsAccessKey, awsSecretKey) | ||
| ); | ||
| } | ||
|
|
||
| public AwsClientBuilder.EndpointConfiguration getAWSEndpointConfiguration() { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IIRC we agreed not to use 3rd party types in our public API (plus, I believe it may not work with AWS SDK v2) |
||
| return new AwsClientBuilder.EndpointConfiguration( | ||
| getContainerIpAddress() + ":" + getMappedPort(S3_PORT), | ||
| "us-east-1" | ||
| ); | ||
| } | ||
|
|
||
| public CephContainer withAwsAccessKey(String awsAccessKey) { | ||
| this.awsAccessKey = awsAccessKey; | ||
| return self(); | ||
| } | ||
|
|
||
| public CephContainer withAwsSecretKey(String awsSecretKey) { | ||
| this.awsSecretKey = awsSecretKey; | ||
| return self(); | ||
| } | ||
|
|
||
| public CephContainer withBucketName(String bucketName) { | ||
| //because s3cmd transforming bucket name to uppercase | ||
| this.bucketName = bucketName.toUpperCase(); | ||
| return self(); | ||
| } | ||
|
|
||
| public CephContainer withRgwName(String rgwName) { | ||
| this.rgwName = rgwName; | ||
| return self(); | ||
| } | ||
|
|
||
| public CephContainer withDemoUid(String demoUid) { | ||
| this.demoUid = demoUid; | ||
| return self(); | ||
| } | ||
|
|
||
| public CephContainer withNetworkAutoDetectMode(NetworkAutoDetectMode networkAutoDetectMode) { | ||
| this.networkAutoDetectMode = networkAutoDetectMode; | ||
| return self(); | ||
| } | ||
|
|
||
| @AllArgsConstructor | ||
| public enum NetworkAutoDetectMode { | ||
| IPV6_OR_IPV4("1"), | ||
| IPV4_ONLY("4"), | ||
| IPV6_ONLY("6"); | ||
|
|
||
| private final String value; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,112 @@ | ||
| package org.testcontainers.containers; | ||
|
|
||
| import com.amazonaws.ClientConfiguration; | ||
| import com.amazonaws.Protocol; | ||
| import com.amazonaws.services.s3.AmazonS3; | ||
| import com.amazonaws.services.s3.AmazonS3ClientBuilder; | ||
| import com.amazonaws.services.s3.model.Bucket; | ||
| import com.amazonaws.services.s3.model.S3Object; | ||
| import org.apache.commons.io.IOUtils; | ||
| import org.junit.Test; | ||
|
|
||
| import java.io.IOException; | ||
| import java.io.InputStream; | ||
| import java.net.URL; | ||
| import java.nio.charset.StandardCharsets; | ||
| import java.time.Duration; | ||
| import java.time.Instant; | ||
| import java.util.Date; | ||
| import java.util.List; | ||
|
|
||
| import static org.junit.Assert.assertEquals; | ||
| import static org.junit.Assert.assertFalse; | ||
| import static org.junit.Assert.assertTrue; | ||
|
|
||
| public class CephContainerTest { | ||
| @Test | ||
| public void testS3Bucket() { | ||
| try ( | ||
| // creating_container { | ||
| CephContainer cephContainer = new CephContainer() | ||
| .withAwsAccessKey("test") | ||
| .withAwsSecretKey("test") | ||
| .withBucketName("test"); | ||
| // } | ||
| ) { | ||
| cephContainer.start(); | ||
|
|
||
| final AmazonS3 amazonS3 = buildS3Client(cephContainer); | ||
|
|
||
| String bucketName = "test2"; | ||
|
|
||
| //check current bucket | ||
| assertTrue(amazonS3.doesBucketExistV2(cephContainer.getBucketName())); | ||
|
|
||
| //create another bucket | ||
| amazonS3.createBucket(bucketName); | ||
| assertTrue(amazonS3.doesBucketExistV2(bucketName)); | ||
| List<Bucket> buckets = amazonS3.listBuckets(); | ||
| assertEquals(2, buckets.size()); | ||
| assertTrue(buckets.stream().anyMatch( | ||
| bucket -> bucket.getName().equals(bucketName) | ||
| )); | ||
|
|
||
| //remove bucket | ||
| amazonS3.deleteBucket(bucketName); | ||
| assertFalse(amazonS3.doesBucketExistV2(bucketName)); | ||
| buckets = amazonS3.listBuckets(); | ||
| assertEquals(1, buckets.size()); | ||
| assertFalse(buckets.stream().anyMatch( | ||
| bucket -> bucket.getName().equals(bucketName) | ||
| )); | ||
| } | ||
| } | ||
|
|
||
| @Test | ||
| public void testS3Object() throws IOException { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. perhaps we don't need both tests (or at least should make the container static + |
||
| try ( | ||
| CephContainer cephContainer = new CephContainer() | ||
| .withAwsAccessKey("test") | ||
| .withAwsSecretKey("test") | ||
| .withBucketName("test"); | ||
| ) { | ||
| cephContainer.start(); | ||
|
|
||
| final AmazonS3 amazonS3 = buildS3Client(cephContainer); | ||
| String objectId = "test"; | ||
| String testData = "This is test data"; | ||
|
|
||
| //put object | ||
| amazonS3.putObject(cephContainer.getBucketName(), objectId, testData); | ||
| assertEquals(1, amazonS3.listObjects(cephContainer.getBucketName()).getObjectSummaries().size()); | ||
| S3Object object = amazonS3.getObject(cephContainer.getBucketName(), objectId); | ||
| assertEquals(testData, IOUtils.toString(object.getObjectContent(), StandardCharsets.UTF_8)); | ||
|
|
||
| //generate presigned url and download file | ||
| URL url = amazonS3.generatePresignedUrl( | ||
| cephContainer.getBucketName(), | ||
| objectId, | ||
| Date.from(Instant.now().plusSeconds(Duration.ofMinutes(5).toMillis()))); | ||
|
|
||
| try (InputStream inputStream = url.openStream()) { | ||
| assertEquals(testData, IOUtils.toString(inputStream, StandardCharsets.UTF_8)); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private AmazonS3 buildS3Client(final CephContainer cephContainer) { | ||
| // setting_up_s3_client { | ||
| final AmazonS3 amazonS3 = AmazonS3ClientBuilder.standard() | ||
| .withCredentials(cephContainer.getAWSCredentialsProvider()) | ||
| .withEndpointConfiguration(cephContainer.getAWSEndpointConfiguration()) | ||
| .withPathStyleAccessEnabled(true) | ||
| .withClientConfiguration( | ||
| new ClientConfiguration() | ||
| .withProtocol(Protocol.HTTP) | ||
| .withSignerOverride("S3SignerType") | ||
| ) | ||
| .build(); | ||
| // } | ||
| return amazonS3; | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.