diff --git a/docs/modules/gcloud.md b/docs/modules/gcloud.md index 049931b3be1..84fab5e0e3b 100644 --- a/docs/modules/gcloud.md +++ b/docs/modules/gcloud.md @@ -5,7 +5,7 @@ Testcontainers module for the Google Cloud Platform's [Cloud SDK](https://cloud.google.com/sdk/). -Currently, the module supports `BigQuery`, `Bigtable`, `Datastore`, `Firestore`, `Spanner`, and `Pub/Sub` emulators. In order to use it, you should use the following classes: +Currently, the module supports `BigQuery`, `Bigtable`, `Datastore`, `Firestore`, `Spanner`, `Pub/Sub`, and `CloudStorage` emulators. In order to use it, you should use the following classes: Class | Container Image -|- @@ -15,6 +15,7 @@ DatastoreEmulatorContainer | [gcr.io/google.com/cloudsdktool/google-cloud-cli:em FirestoreEmulatorContainer | [gcr.io/google.com/cloudsdktool/google-cloud-cli:emulators](https://gcr.io/google.com/cloudsdktool/google-cloud-cli) SpannerEmulatorContainer | [gcr.io/cloud-spanner-emulator/emulator](https://gcr.io/cloud-spanner-emulator/emulator) PubSubEmulatorContainer | [gcr.io/google.com/cloudsdktool/google-cloud-cli:emulators](https://gcr.io/google.com/cloudsdktool/google-cloud-cli) +CloudStorageEmulatorContainer | [hub.docker.com/r/fsouza/fake-gcs-server](https://hub.docker.com/r/fsouza/fake-gcs-server) ## Usage example @@ -150,6 +151,15 @@ See more examples: * [Full sample code](https://github.com/testcontainers/testcontainers-java/tree/main/modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java) * [With Spring Boot](https://github.com/saturnism/testcontainers-gcloud-examples/tree/main/springboot/pubsub-example/src/test/java/com/example/springboot/pubsub/PubSubIntegrationTests.java) +### CloudStorage + +Start the CloudStorage emulator and test against it: + + +[Testing with a CloudStorage Emulator container](../../modules/gcloud/src/test/java/org/testcontainers/containers/CloudStorageEmulatorContainerTest.java) inside_block:testWithCloudStorageEmulatorContainer + + + ## Adding this module to your project dependencies Add the following dependency to your `pom.xml`/`build.gradle` file: diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index cb55e466e22..a8c75d357bf 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -10,5 +10,6 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-pubsub' testImplementation 'com.google.cloud:google-cloud-spanner' testImplementation 'com.google.cloud:google-cloud-bigtable' + testImplementation 'com.google.cloud:google-cloud-storage' testImplementation 'org.assertj:assertj-core:3.26.3' } diff --git a/modules/gcloud/src/main/java/org/testcontainers/containers/CloudStorageEmulatorContainer.java b/modules/gcloud/src/main/java/org/testcontainers/containers/CloudStorageEmulatorContainer.java new file mode 100644 index 00000000000..48d68a81c18 --- /dev/null +++ b/modules/gcloud/src/main/java/org/testcontainers/containers/CloudStorageEmulatorContainer.java @@ -0,0 +1,83 @@ +package org.testcontainers.containers; + +import org.testcontainers.containers.wait.strategy.WaitStrategy; +import org.testcontainers.containers.wait.strategy.WaitStrategyTarget; +import org.testcontainers.utility.DockerImageName; + +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.time.Duration; + +/** + * Testcontainers implementation for Google Cloud Storage. + * Default port: 4443 + *

+ * Supported image: {@code hub.docker.com/r/fsouza/fake-gcs-server} + *

+ * @see fsouza/fake-gcs-server on GitHub + */ +public class CloudStorageEmulatorContainer extends GenericContainer { + + public static final DockerImageName DEFAULT_IMAGE = DockerImageName.parse("fsouza/fake-gcs-server"); + + public static final int EMULATOR_PORT = 4443; + + public CloudStorageEmulatorContainer(String image) { + this(DockerImageName.parse(image)); + } + + public CloudStorageEmulatorContainer(DockerImageName dockerImageName) { + super(dockerImageName); + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE); + addExposedPorts(EMULATOR_PORT); + withCreateContainerCmdModifier(cmd -> cmd.withEntrypoint("/bin/fake-gcs-server", "-scheme", "http")); + waitingFor( + new WaitStrategy() { + @Override + public void waitUntilReady(WaitStrategyTarget target) { + updateFakeGcsExternalUrl(getEmulatorHttpEndpoint()); + } + + @Override + public WaitStrategy withStartupTimeout(Duration startupTimeout) { + return getWaitStrategy().withStartupTimeout(startupTimeout); + } + } + ); + } + + public String getEmulatorHttpEndpoint() { + return String.format("http://%s:%d", getHost(), getMappedPort(EMULATOR_PORT)); + } + + private static void updateFakeGcsExternalUrl(String gcsUrl) { + String json = String.format("{ \"externalUrl\": \"%s\" }", gcsUrl); + + HttpRequest req = HttpRequest + .newBuilder() + .uri(URI.create(gcsUrl + "/_internal/config")) + .header("Content-Type", "application/json") + .PUT(HttpRequest.BodyPublishers.ofString(json)) + .build(); + + try { + HttpResponse response = HttpClient + .newBuilder() + .build() + .send(req, HttpResponse.BodyHandlers.discarding()); + + if (response.statusCode() != 200) { + throw new RuntimeException( + "error updating fake-gcs-server with external url, response status code " + + response.statusCode() + + " != 200" + ); + } + } catch (IOException | InterruptedException e) { + throw new RuntimeException(e); + } + } +} diff --git a/modules/gcloud/src/test/java/org/testcontainers/containers/CloudStorageEmulatorContainerTest.java b/modules/gcloud/src/test/java/org/testcontainers/containers/CloudStorageEmulatorContainerTest.java new file mode 100644 index 00000000000..e7e2baab0de --- /dev/null +++ b/modules/gcloud/src/test/java/org/testcontainers/containers/CloudStorageEmulatorContainerTest.java @@ -0,0 +1,44 @@ +package org.testcontainers.containers; + +import com.google.cloud.NoCredentials; +import com.google.cloud.storage.Blob; +import com.google.cloud.storage.BlobInfo; +import com.google.cloud.storage.BucketInfo; +import com.google.cloud.storage.Storage; +import com.google.cloud.storage.StorageOptions; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class CloudStorageEmulatorContainerTest { + + // testWithCloudStorageEmulatorContainer { + @Test + public void shouldWriteAndReadFile() { + final String BUCKET_NAME = "test-bucket"; + final String FILE_NAME = "test-file.txt"; + final String FILE_CONTENT = "test file content"; + + try ( + CloudStorageEmulatorContainer container = new CloudStorageEmulatorContainer("fsouza/fake-gcs-server:1.50.2") + ) { + container.start(); + + Storage storageClient = StorageOptions + .newBuilder() + .setHost(container.getEmulatorHttpEndpoint()) + .setProjectId("test-project") + .setCredentials(NoCredentials.getInstance()) + .build() + .getService(); + storageClient.create(BucketInfo.of(BUCKET_NAME)); + + storageClient.create(BlobInfo.newBuilder(BUCKET_NAME, FILE_NAME).build(), FILE_CONTENT.getBytes()); + + Blob blob = storageClient.get(BUCKET_NAME, FILE_NAME); + assertThat(blob.getContent()).asString().isEqualTo(FILE_CONTENT); + } + } + // } + +}