- Notifications
You must be signed in to change notification settings - Fork185
A simple mock implementation of the AWS S3 API startable as Docker image, TestContainer, JUnit 4 rule, JUnit Jupiter extension or TestNG listener
License
adobe/S3Mock
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
- S3Mock
- Changelog
- Supported S3 operations
- Usage
- File System Structure
- Build & Run
- Contributing
- Licensing
S3Mock
is a lightweight server that implements parts of theAmazon S3 API.
It has been created to support local integration testing by reducing infrastructure dependencies.
TheS3Mock
server can be started as a standaloneDocker container, usingTestcontainers,JUnit4,JUnit5 andTestNG support, or programmatically.
SeeGitHub releases.
See also thechangelog for detailed information about changes in releases and changes planned for future releases.
Of theseoperations of the Amazon S3 API, all marked ✅ are supported by S3Mock:
S3Mock can be used with any of the available AWS S3 SDKs.
TheIntegration Tests contain various examples of how to use the S3Mock with the AWS SDK for Java v1 and v2 in Kotlin.The modules belowtestsupport contain examples in Java.
S3Client
orS3Presigner
instances are created here:
AWS S3 SDKs usually use domain-style access by default. Configuration is needed for path-style access.
S3Mock currently only supports path-style access (e.g.,http://localhost:9090/bucket/someKey
).
Domain-style access to buckets (e.g.,http://bucket.localhost:9090/someKey
) does not work.This is due to the fact that the domainlocalhost
is special and does not allow for subdomain access without modifications to the operating system.
S3 SDKs can be used tocreate presigned URLs, the S3 API supports access through those URLs.
S3Mock will accept presigned URLs, but itignores all parameters.
For instance, S3Mock does not verify the HTTP verb that the presigned uri was created with, and it does not validate whether the link is expired or not.
S3 SDKs can be used to create presigned URLs pointing to S3Mock if they're configured for path-style access. See the "Usage of..." section above for links to examples on how to use the SDK with presigned URLs.
S3Mock can be used with the AWS CLI. Setting the--endpoint-url
enables path-style access.
Examples:
Create bucket
aws s3api create-bucket --bucket my-bucket --endpoint-url=http://localhost:9090
Put object
aws s3api put-object --bucket my-bucket --key my-file --body ./my-file --endpoint-url=http://localhost:9090
Get object
aws s3api get-object --bucket my-bucket --key my-file --endpoint-url=http://localhost:9090 my-file-output
As long as the requests work with the S3 API, they will work with S3Mock as well.
Examples:
Create bucket
curl --request PUT"http://localhost:9090/my-test-bucket/"
Put object
curl --request PUT --upload-file ./my-file http://localhost:9090/my-test-bucket/my-file
Get object
curl --request GET http://localhost:9090/my-test-bucket/my-file
The mock can be configured with the following environment variables:
validKmsKeys
: list of KMS Key-Refs that are to be treated asvalid.- KMS keys must be configured as valid ARNs in the format of "
arn:aws:kms:region:acct-id:key/key-id
", for example "arn:aws:kms:us-east-1:1234567890:key/valid-test-key-id
" - The list must be comma separated keys like
arn-1, arn-2
- When requesting with KMS encryption, the key ID is passed to the SDK / CLI, in the example above this would be "
valid-test-key-id
". - S3Mock does not implement KMS encryption, if a key ID is passed in a request, S3Mock will just validate if a given Key was configured during startup and reject the request if the given Key was not configured.
- KMS keys must be configured as valid ARNs in the format of "
initialBuckets
: list of names for buckets that will be available initially.- The list must be comma separated names like
bucketa, bucketb
- The list must be comma separated names like
root
: the base directory to place the temporary files exposed by the mock. If S3Mock is started in Docker, a volume must be mounted as theroot
directory, see examples below.debug
: set totrue
to enableSpring Boot's debug output.trace
: set totrue
to enableSpring Boot's trace output.retainFilesOnExit
: set totrue
to let S3Mock keep all files that were created during its lifetime. Default isfalse
, all files are removed if S3Mock shuts down.
TheS3Mock
Docker container is the recommended way to useS3Mock
.
It is released toDocker Hub.
The container is lightweight, built on top of the officialLinux Alpine image.
If needed, configurememory andcpu limits for the S3Mock docker container.
The JVM will automatically use half the available memory.
Starting on the command-line:
docker run -p 9090:9090 -p 9191:9191 -t adobe/s3mock
The port9090
is for HTTP, port9191
is for HTTPS.
Example with configuration via environment variables:
docker run -p 9090:9090 -p 9191:9191 -e initialBuckets=test -e debug=true -t adobe/s3mock
Ourintegration tests are using the Amazon S3 Client to verify the server functionality against the S3Mock. During the Maven build, the Docker image is started using thedocker-maven-plugin and the corresponding ports are passed to the JUnit test through themaven-failsafe-plugin
. SeeBucketV2IT
as an example on how it's used in the code.
This way, one can easily switch between calling the S3Mock or the real S3 endpoint and this doesn't add any additional Java dependencies to the project.
TheS3MockContainer
is aTestcontainer
implementation that comes pre-configured exposing HTTP and HTTPS ports. Environment variables can be set on startup.
The exampleS3MockContainerJupiterTest
demonstrates the usage with JUnit 5. The exampleS3MockContainerManualTest
demonstrates the usage with plain Java.
Testcontainers provides integrations for JUnit 4, JUnit 5 and Spock.
For more information, visit theTestcontainers website.
To use theS3MockContainer
, use the following Maven artifact intest
scope:
<dependency> <groupId>com.adobe.testing</groupId> <artifactId>s3mock-testcontainers</artifactId> <version>...</version> <scope>test</scope></dependency>
Create a filedocker-compose.yml
services:s3mock:image:adobe/s3mock:latestenvironment: -initialBuckets=bucket1ports: -9090:9090
Start withdocker compose up -d
Stop withdocker compose down
Suppose we want to see what S3Mock is persisting, and look at the logs it generates in detail.
A local directory is needed, let's call itlocals3root
. This directory must be mounted as a volume into the Docker container when it's started, and that mounted volume must then be configured as theroot
for S3Mock. Let's call the mounted volume inside the containercontainers3root
. S3Mock will delete all files when it shuts down,retainFilesOnExit=true
tells it to leave all files instead.
Also, to see debug logs,debug=true
must be configured for S3Mock.
Create a filedocker-compose.yml
services:s3mock:image:adobe/s3mock:latestenvironment: -debug=true -retainFilesOnExit=true -root=containers3rootports: -9090:9090 -9191:9191volumes: -./locals3root:/containers3root
Create a directorylocals3root
.
Start withdocker compose up -d
Create a bucket "my-test-bucket" withcurl --request PUT "http://localhost:9090/my-test-bucket/"
Stop withdocker compose down
Look into the directorylocals3root
where metadata and contents of the bucket are stored.
$ mkdir s3mock-mounttest$cd s3mock-mounttest$ mkdir locals3root$ cat docker-compose.ymlservices: s3mock: image: adobe/s3mock:latest environment: - debug=true - retainFilesOnExit=true - root=containers3root ports: - 9090:9090 - 9191:9191 volumes: - ./locals3root:/containers3root$ docker compose up -d[+] Running 2/2 ✔ Network s3mock-mounttest_default Created ✔ Container s3mock-mounttest-s3mock-1 Started$ curl --request PUT"http://localhost:9090/my-test-bucket/"$ docker compose down[+] Running 2/0 ✔ Container s3mock-mounttest-s3mock-1 Removed ✔ Network s3mock-mounttest_default Removed $ ls locals3rootmy-test-bucket$ ls locals3root/my-test-bucketbucketMetadata.json
S3Mock
Java libraries are released and published to the Sonatype Maven Repository and subsequently published tothe officialMaven mirrors.
Using the Java libraries isdiscouraged, see explanation below |
Using the Docker image isencouraged to insulate both S3Mock and your application at runtime. |
S3Mock
is built using Spring Boot, if projects useS3Mock
by adding the dependency to their project and startingtheS3Mock
during a JUnit test, classpaths of the tested application and of theS3Mock
are merged, leadingto unpredictable and undesired effects such as class conflicts or dependency version conflicts.
This is especially problematic if the tested application itself is a Spring (Boot) application, as both applications will load configurations based on availability of certain classes in the classpath, leading to unpredictable runtime behaviour.
This is the opposite of what software engineers are trying to achieve when thoroughly testing code in continuous integration...
S3Mock
dependencies are updated regularly, any update could break any number of projects.
See alsoissues labelled "dependency-problem".
See alsothe Java section below
The exampleS3MockRuleTest
demonstrates the usage of theS3MockRule
, which can be configured through abuilder.
To use the JUnit4 Rule, use the following Maven artifact intest
scope:
<dependency> <groupId>com.adobe.testing</groupId> <artifactId>s3mock-junit4</artifactId> <version>...</version> <scope>test</scope></dependency>
TheS3MockExtension
can currently be used in two ways:
Declaratively using
@ExtendWith(S3MockExtension.class)
and by injecting a properly configured instance ofAmazonS3
client and/or the startedS3MockApplication
to the tests.See examples:S3MockExtensionDeclarativeTest
(for SDKv1)orS3MockExtensionDeclarativeTest
(for SDKv2)Programmatically using
@RegisterExtension
and by creating and configuring theS3MockExtension
using abuilder.See examples:S3MockExtensionProgrammaticTest
(for SDKv1)orS3MockExtensionProgrammaticTest
(for SDKv2)
To use the JUnit5 Extension, use the following Maven artifact intest
scope:
<dependency> <groupId>com.adobe.testing</groupId> <artifactId>s3mock-junit5</artifactId> <version>...</version> <scope>test</scope></dependency>
The exampleS3MockListenerXMLConfigurationTest
demonstrates the usage of theS3MockListener
, which can be configured as shown intestng.xml
. The listener bootstraps the S3Mock application before TestNG execution starts and shuts down the application just before the execution terminates. Please refer toIExecutionListener
from the TestNG API.
To use the TestNG Listener, use the following Maven artifact intest
scope:
<dependency> <groupId>com.adobe.testing</groupId> <artifactId>s3mock-testng</artifactId> <version>...</version> <scope>test</scope></dependency>
Include the following dependency and use one of thestart
methods incom.adobe.testing.s3mock.S3MockApplication
:
<dependency> <groupId>com.adobe.testing</groupId> <artifactId>s3mock</artifactId> <version>...</version></dependency>
S3Mock stores Buckets, Objects, Parts and other data on disk.
This lets users inspect the stored data while the S3Mock is running.
If the config propertyretainFilesOnExit
is set totrue
, this data will not be deleted when S3Mock is shut down.
❗ FYI |
---|
While itmay be possible to start S3Mock on a root folder from a previous run and have all data available through the S3 API, the structure and contents of the files are not considered Public API, and are subject to change in later releases. |
Also, there are no automated test cases for this behaviour. |
S3Mock stores buckets and objects a root-folder.
This folder is expected to be empty when S3Mock starts. See also FYI above.
/<root-folder>/
Buckets are stored as a folder with their name as created through the S3 API directly below the root:
/<root-folder>/<bucket-name>/
BucketMetadata is stored in a file in the bucket directory, serialized as JSON.
BucketMetadata contains the "key" -> "uuid" dictionary for all objects uploaded to this bucket among other data.
/<root-folder>/<bucket-name>/bucketMetadata.json
Objects are stored in folders below the bucket they were created in.A folder is created that uses the Object's UUID assigned in theBucketMetadata as a name.
/<root-folder>/<bucket-name>/<uuid>/
Object data is stored below that UUID folder.
Binary data is always stored in a filebinaryData
/<root-folder>/<bucket-name>/<uuid>/binaryData
Object metadata is serialized as JSON and stored asobjectMetadata.json
/<root-folder>/<bucket-name>/<uuid>/objectMetadata.json
Object ACL is serialized as JSON and stored asobjectAcl.xml
/<root-folder>/<bucket-name>/<uuid>/objectAcl.json
Multipart Uploads are created in a bucket using object keys and an uploadId.
The object is assigned a UUID within the bucket (stored inBucketMetadata).
TheMultipart upload metadata is currently not stored on disk.
The multiparts folder is created below the bucket folder and named with theuploadId
:
/<root-folder>/<bucket-name>/multiparts/<uploadId>/
The multiparts metadata file is created below the folder named with theuploadId
:
/<root-folder>/<bucket-name>/multiparts/<uploadId>/multipartMetadata.json
Each part is stored in the parts folder with thepartNo
as name and.part
as a suffix.
/<root-folder>/<bucket-name>/multiparts/<uploadId>/<partNo>.part
Once the MultipartUpload is completed, theuploadId
folder is deleted.
To build this project, you need Docker, JDK 17 or higher, and Maven:
./mvnw clean install
If you want to skip the Docker build, pass the optional parameter "skipDocker":
./mvnw clean install -DskipDocker
You can run the S3Mock from the sources by either of the following methods:
- Run or Debug the class
com.adobe.testing.s3mock.S3MockApplication
in the IDE. - using Docker:
./mvnw clean package -pl server -am -DskipTests
docker run -p 9090:9090 -p 9191:9191 -t adobe/s3mock:latest
- using the Docker Maven plugin:
./mvnw clean package docker:start -pl server -am -DskipTests -Ddocker.follow -Dit.s3mock.port_http=9090 -Dit.s3mock.port_https=9191
(stop withctrl-c
)
Once the application is started, you can execute the*IT
tests from your IDE.
This repo is built with Java 17, output iscurrently bytecode compatible with Java 17.
TheIntegration Tests are built in Kotlin.
Contributions are welcomed! Read theContributing Guide for more information.
This project is licensed under the Apache V2 License. SeeLICENSE for more information.
About
A simple mock implementation of the AWS S3 API startable as Docker image, TestContainer, JUnit 4 rule, JUnit Jupiter extension or TestNG listener