fake-gcs-server

repository·main·Indexed 23 days ago

https://github.com/fsouza/fake-gcs-server

An emulator for the Google Cloud Storage API that can be used as a Go library for unit testing or as a standalone Docker container/binary for integration testing. It supports HTTP and HTTPS protocols, resumable uploads via an internal configuration API, and preloading data through filesystem mounts. The server can be configured using command-line flags, environment variables prefixed with FAKE_GCS_, or the Options struct when used as a library.

Tokens
3.7K
Snippets
7
Records
16
Agent score
75%

What's inside fake-gcs-server

  1. Use fake-gcs-server with signed URLs

    main

    You can use fake-gcs-server with signed URLs by following these requirements:

    1. No Validation: The server does not validate query parameters like signature or expiration.
    2. URL Modification: Your client must replace storage.googleapis.com in the signed URL with the address of your fake-gcs-server instance.
    3. Public Host Configuration: You must configure the server to accept the local URL using the -public-host flag.
  2. Configure server protocol with -scheme

    main

    The server defaults to HTTPS, but you can change the protocol using the -scheme flag.

    • HTTPS (default): Use --insecure with curl to bypass certificate validation.
    • HTTP: Use -scheme http to run without SSL.
    • Both: Use -scheme both to run both HTTPS and HTTP servers simultaneously. When using both, HTTPS binds to -port (default 4443) and HTTP binds to -port-http (default 8000).

    Example running with HTTP:

    docker run -d --name fake-gcs-server -p 4443:4443 -v ${PWD}/examples/data:/data fsouza/fake-gcs-server -scheme http
  3. Preload data into fake-gcs-server

    main

    To preload data into the emulator, mount a local directory to the /data path inside the container. The directory structure should represent buckets and objects. For example, a folder named sample-bucket containing some_file.txt will result in a bucket named sample-bucket with one object named some_file.txt.

    Example command:

    docker run -d --name fake-gcs-server -p 4443:4443 -v ${PWD}/examples/data:/data fsouza/fake-gcs-server
  4. Use fake-gcs-server with Testcontainers in Node.js

    main

    You can integrate fake-gcs-server into your Node.js testing workflow using testcontainers. This allows you to spin up a containerized GCS emulator that your application code can interact with via the @google-cloud/storage library.

    To set this up:

    1. Start a GenericContainer using the fsouza/fake-gcs-server image.
    2. Configure the entrypoint to use the -scheme http flag.
    3. Expose the required port (e.g., 4443).
    4. Retrieve the dynamic host and mapped port from the container.
    5. Crucial: Update the server's internal configuration via the /_internal/config endpoint to set the externalUrl. This ensures that the emulator generates correct URLs for operations like signed URLs or redirects.
    6. Initialize the @google-cloud/storage client using the emulator's apiEndpoint.

    Note: The example uses ky for making the configuration HTTP request.

    import { Storage } from "@google-cloud/storage";
    import ky from "ky";
    import { GenericContainer } from "testcontainers";
    
    const PORT = 4443;
    
    const CONTAINER = await new GenericContainer("fsouza/fake-gcs-server:1.49.0")
      .withEntrypoint(["/bin/fake-gcs-server", "-scheme", "http"])
      .withExposedPorts(PORT)
      .start();
    
    const API_ENDPOINT = `http://${CONTAINER.getHost()}:${CONTAINER.getMappedPort(
      PORT
    )}`;
    
    await ky.put(`${API_ENDPOINT}/_internal/config`, {
      json: { externalUrl: API_ENDPOINT },
    });
    
    const STORAGE = new Storage({ apiEndpoint: API_ENDPOINT });
    
    // ...
  5. Handle resumable uploads with containerized fake-gcs-server

    main

    When running fake-gcs-server in a container (e.g., via Testcontainers), the IP and port are dynamic and unknown before startup. Because resumable upload operations require the server to respond with the correct Location header (the external-url), you must update the server's configuration via its internal API after the container has started.

    To do this:

    1. Retrieve the container's host and mapped port.
    2. Construct the external URL (e.g., http://<host>:<port>).
    3. Send a PUT request to the /_internal/config endpoint with a JSON body containing the externalUrl key.
    @Testcontainers
    class FakeGcsServerTest {
    
        @Container
        static final GenericContainer<?> fakeGcs = new GenericContainer<>("fsouza/fake-gcs-server")
          .withExposedPorts(4443)
          .withCreateContainerCmdModifier(cmd -> cmd.withEntrypoint(
              "/bin/fake-gcs-server",
              "-scheme", "http"
          ));
    
        @BeforeAll
        static void setUpFakeGcs() throws Exception {
          String fakeGcsExternalUrl = "http://" + fakeGcs.getHost() + ":" + fakeGcs.getFirstMappedPort();
    
          updateExternalUrlWithContainerUrl(fakeGcsExternalUrl);
    
          storageClient = StorageOptions.newBuilder()
              .setHost(fakeGcsExternalUrl)
              .setProjectId("test-project")
              .setCredentials(NoCredentials.getInstance())
              .build()
              .getService();
        }
    
        private static void updateExternalUrlWithContainerUrl(String fakeGcsExternalUrl) throws Exception {
          String modifyExternalUrlRequestUri = fakeGcsExternalUrl + "/_internal/config";
          String updateExternalUrlJson = "{" + "\"externalUrl\": \"" + fakeGcsExternalUrl + "\"}";
    
          HttpRequest req = HttpRequest.newBuilder()
              .uri(URI.create(modifyExternalUrlRequestUri))
              .header("Content-Type", "application/json")
              .PUT(BodyPublishers.ofString(updateExternalUrlJson))
              .build();
          HttpResponse<Void> response = HttpClient.newBuilder().build()
              .send(req, BodyHandlers.discarding());
    
          if (response.statusCode() != 200) {
              throw new RuntimeException(
                  "error updating fake-gcs-server with external url, response status code " + response.statusCode() + " != 200");
          }
        }
    
        @Test
        void shouldUploadFileByWriterChannel() throws IOException {
          storageClient.create(BucketInfo.newBuilder("sample-bucket2").build());
    
          WriteChannel channel = storageClient.writer(BlobInfo.newBuilder("sample-bucket2", "some_file2.txt").build());
          channel.write(ByteBuffer.wrap("line1\n".getBytes()));
          channel.write(ByteBuffer.wrap("line2\n".getBytes()));
          channel.close();
    
          Blob someFile2 = storageClient.get("sample-bucket2", "some_file2.txt");
          String fileContent = new String(someFile2.getContent());
          assertEquals("line1\nline2\n", fileContent);
        }
    }
  6. Run fake-gcs-server using Docker

    main

    You can run fake-gcs-server as a standalone server in a Docker container, which is ideal for integration tests or testing with non-Go languages. By default, the server uses HTTPS on port 4443.

    To start a basic container:

    docker run -d --name fake-gcs-server -p 4443:4443 fsouza/fake-gcs-server
  7. Configure fake-gcs-server via Environment Variables

    main

    All server flags can be set using environment variables prefixed with FAKE_GCS_. Command line flags take precedence over environment variables.

    FlagEnvironment VariableDescription
    -portFAKE_GCS_PORTBinding port for HTTPS (default 4443)
    -port-httpFAKE_GCS_PORT_HTTPBinding port for HTTP (default 8000)
    -schemeFAKE_GCS_SCHEMEProtocol scheme (https, http, or both)
    -backendFAKE_GCS_BACKENDStorage backend
    -filesystem-rootFAKE_GCS_FILESYSTEM_ROOTRoot directory for filesystem storage
    -public-hostFAKE_GCS_PUBLIC_HOSTThe public host address
    -external-urlFAKE_GCS_EXTERNAL_URLThe external URL

    Example using environment variables:

    docker run -d --name fake-gcs-server -e FAKE_GCS_SCHEME=http -p 4443:4443 fsouza/fake-gcs-server
  8. Run the fake-gcs-server as a standalone binary

    main

    The main.go file serves as the entrypoint for the fake-gcs-server CLI. When compiled and run, it initializes a storage server that can handle both HTTP and gRPC requests.

    To run the server, you pass configuration flags as command-line arguments. The server supports:

    • HTTP/HTTPS schemes: You can specify a single scheme or use both to run both HTTP and HTTPS listeners simultaneously.
    • gRPC support: The server automatically detects gRPC requests (via application/grpc content-type) and routes them to a gRPC backend.
    • TLS Configuration: For HTTPS, you can provide paths to a certificate and private key. If no certificates are provided, the server generates a self-signed certificate using Go's internal test server logic.
    • MIME Type Support: The server explicitly adds support for .yaml and .yml extensions as application/x-yaml.
  9. Initialize the fake GCS server as a library

    main

    You can embed the fake GCS server directly into your Go tests or applications using the fakestorage package. Use NewServer for a simple setup with initial objects, or NewServerWithOptions for advanced configuration like custom hosts, ports, or storage backends.

    If you use NewServerWithOptions, you can configure the server's behavior via the Options struct.

  10. Configure the Google Cloud Storage Java client for fake-gcs-server

    main

    To use the fake-gcs-server with the official Google Cloud Storage Java client, you must configure the StorageOptions builder to point to the emulator's host, provide a project ID, and use NoCredentials.getInstance() to bypass actual Google authentication.

              Storage storageClient = StorageOptions.newBuilder()
                .setHost(fakeGcsExternalUrl)
                .setProjectId("test-project")
                .setCredentials(NoCredentials.getInstance())
                .build()
                .getService();
  11. Configure the fake GCS server with Options

    main

    The Options struct allows fine-grained control over the emulator's behavior:

    FieldTypeDescription
    InitialObjects[]ObjectA list of objects to pre-load into the server.
    StorageRootstringIf set, the server uses a filesystem-based backend at this path instead of in-memory.
    SeedstringA directory path used to seed the server with objects (one directory per bucket).
    SchemestringThe protocol to use (http or https).
    HoststringThe host the server listens on.
    Portuint16The port the server listens on.
    NoListenerboolIf true, the server won't start a TCP listener; requests are processed via an internal mocked transport (useful for unit tests).
    ExternalURLstringThe URL returned in Location headers for resumable uploads (e.g., https://gcs.127.0.0.1.nip.io:4443).
    PublicHoststringThe host used for public access (e.g., storage.googleapis.com).
    AllowedCORSHeaders[]stringCustom headers to add to the CORS allowlist.
    Writerio.WriterDestination for server logs.
    EventOptionsEventManagerOptionsConfiguration for publishing events (Pub/Sub style).
    BucketsLocationstringLocation used for buckets.
    CertificateLocationstringPath to X.509 certificate for HTTPS.
    PrivateKeyLocationstringPath to private key for HTTPS.