Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions dev-container/Dockerfile.development
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
FROM public.ecr.aws/amazonlinux/amazonlinux:2023

RUN dnf upgrade -y && \
dnf install -y \
fuse \
fuse-devel \
cmake3 \
clang \
clang-devel \
git \
pkg-config \
jq && \
dnf clean all

# Configure FUSE
RUN echo "user_allow_other" >> /etc/fuse.conf

# Create non-root user
RUN useradd -m -s /bin/bash dev-user && \
usermod -aG wheel dev-user

# Install Rust as dev-user
USER dev-user
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain none
ENV PATH="/home/dev-user/.cargo/bin:${PATH}"

# Set colored prompt
RUN echo 'PS1="\[\033[1;36m\][dev-container]:\[\033[1;34m\]\w\[\033[0m\] \$ "' >> /home/dev-user/.bashrc

WORKDIR /workspace

RUN mkdir -p /workspace/target

# Install cargo-nextest and Rust version based on toolchain config at build time
COPY --chown=dev-user:dev-user rust-toolchain.toml ./
RUN rustup show && cargo install cargo-nextest --locked

ENV RUST_BACKTRACE=1

COPY --chown=dev-user:dev-user dev-container/entrypoint.sh /home/dev-user/entrypoint.sh
RUN chmod +x /home/dev-user/entrypoint.sh

ENTRYPOINT ["/home/dev-user/entrypoint.sh"]
50 changes: 50 additions & 0 deletions dev-container/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Development Container

Docker container for running Mountpoint S3 tests on Linux. Useful for macOS users to test against a Linux kernel/OS.

The container uses runtime builds with persistent caching:
- System dependencies are baked into the image at build time
- Rust toolchains (`~/.rustup/`), cargo dependencies, and build artifacts are cached in Docker volumes between runs
- On each container start, the entrypoint ensures the correct Rust toolchain (from `rust-toolchain.toml`) is installed
- Source code is mounted at runtime (not baked into image)

## Quick Start

```bash
# Build and run the container
./dev-container/run.py --build

# Interactive shell
./dev-container/run.py

# Run a command (use -- to separate script args from container args)
./dev-container/run.py --use-credentials-from-aws-config -- cargo nextest run --features s3_tests,fuse_tests -p mountpoint-s3-fs
```

## Credential Options

You'll need to configure AWS credentials if you want to run things like the integration tests which access S3.

As an example, you might have credentials available in your `~/.aws/` folder.
You can use the `--use-credentials-from-aws-config` option to bind mount that directory into the container.

See `--help` for more available arguments.

## When to Rebuild

Rebuild the image when:
- System dependencies need updating

You do **not** need to rebuild when:
- Rust toolchain version changes in `rust-toolchain.toml` (the entrypoint installs the correct toolchain automatically)
- Code or cargo dependencies change (cached in Docker volumes)

## Cleanup

To remove the cached rustup volume (e.g., when old toolchains accumulate):

```bash
./dev-container/run.py --drop-rustup-volume
```

Build artifacts and cargo dependencies are cached in Docker volumes between runs, so you don't need to rebuild for code or dependency changes.
11 changes: 11 additions & 0 deletions dev-container/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/bin/bash
set -e

# Ensure the correct Rust toolchain is installed (reads /workspace/rust-toolchain.toml)
rustup show > /dev/null

if [ $# -eq 0 ]; then
exec /bin/bash
else
exec "$@"
fi
70 changes: 70 additions & 0 deletions dev-container/run.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#!/usr/bin/env python3
import argparse
import os
import subprocess

CONTAINER_USER = 'dev-user'
CARGO_CACHE_VOLUME = 'mountpoint-s3-cargo-cache'
CARGO_TARGET_VOLUME = 'mountpoint-s3-target-cache'
RUSTUP_VOLUME = 'mountpoint-s3-rustup-home'


def main():
parser = argparse.ArgumentParser(description='Run Mountpoint S3 development container')
parser.add_argument('--image', default='mountpoint-s3-dev', help='Docker image name')
parser.add_argument('--build', action='store_true', help='Build the image before running')
parser.add_argument('--drop-rustup-volume', action='store_true', help='Remove the rustup Docker volume and exit')

creds_group = parser.add_mutually_exclusive_group()
creds_group.add_argument('--use-credentials-from-env', action='store_true', help='Pass through AWS env vars')
creds_group.add_argument('--use-credentials-from-aws-config', action='store_true', help='Pass through AWS env vars')

args, container_args = parser.parse_known_args()

# Remove '--' separator if present
if container_args and container_args[0] == '--':
container_args = container_args[1:]

if args.drop_rustup_volume:
subprocess.run(['docker', 'volume', 'rm', RUSTUP_VOLUME], check=True)
return

if args.build:
subprocess.run(
['docker', 'build', '-f', 'dev-container/Dockerfile.development', '-t', args.image, '.'], check=True
)

docker_args = [
'docker',
'run',
'-it',
'--rm',
'--device=/dev/fuse',
'--privileged',
# f'--user={os.getuid()}:{os.getgid()}',
f'--env-file={os.getcwd()}/.env',
f'-v={os.getcwd()}:/workspace',
f'-v={CARGO_CACHE_VOLUME}:/home/{CONTAINER_USER}/.cargo/registry',
f'-v={CARGO_TARGET_VOLUME}:/workspace/target',
f'-v={RUSTUP_VOLUME}:/home/{CONTAINER_USER}/.rustup',
]

if args.use_credentials_from_env:
for var in ['AWS_ACCESS_KEY_ID', 'AWS_SECRET_ACCESS_KEY', 'AWS_SESSION_TOKEN', 'AWS_REGION']:
if var in os.environ:
docker_args.extend(['-e', var])
elif args.use_credentials_from_aws_config:
aws_dir = os.path.expanduser('~/.aws')
if os.path.exists(aws_dir):
docker_args.extend(['-v', f'{aws_dir}:/home/{CONTAINER_USER}/.aws:ro'])

if container_args:
docker_args.extend([args.image, '/bin/bash', '-c', ' '.join(container_args)])
else:
docker_args.extend([args.image, '/bin/bash'])

subprocess.run(docker_args)


if __name__ == '__main__':
main()
1 change: 1 addition & 0 deletions doc/DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ The `docs/INSTALL.md` has a section on building from source which can get you st
For running tests, you should [install cargo-nextest](https://nexte.st/docs/installation/pre-built-binaries/).

You will need a Linux environment that has FUSE support.
If you wish to use macOS, there is a container available to support testing in `dev-container/`.

You should also have AWS credentials available for testing.
Short-term AWS credentials are recommended.
Expand Down
Loading