diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..49f32ce --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,20 @@ +# CODEOWNERS for testcontainers-floci-python +# +# Routes module code to this repository's maintainers (see MAINTAINERS.md) and keeps +# release-sensitive paths with the Lead, per the org-wide GOVERNANCE.md. +# +# Order matters: the LAST matching pattern wins, so the Lead-reserved paths below stay +# authoritative over the broader area rules above them. +# +# Docs: https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners + +# Default: no global owner (unowned files fall through to normal review rules). + +# Module code +/floci/ @hectorvent +/tests/ @hectorvent + +# CI, release automation, workflow hygiene +/.github/ @hectorvent +/.releaserc.json @hectorvent +/pyproject.toml @hectorvent diff --git a/.gitignore b/.gitignore index 2fa7f1a..c62d396 100644 --- a/.gitignore +++ b/.gitignore @@ -31,4 +31,4 @@ htmlcov/ # macOS .DS_Store -local/ \ No newline at end of file +/local/ \ No newline at end of file diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..66c0d6b --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,31 @@ +# Code of Conduct + +## Our Pledge + +We as members, contributors, and maintainers pledge to make participation in Floci a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +**Positive behavior includes:** +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members + +**Unacceptable behavior includes:** +- The use of sexualized language or imagery +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information without explicit permission +- Other conduct which could reasonably be considered inappropriate in a professional setting + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening a GitHub issue or contacting the maintainers directly. All complaints will be reviewed and investigated promptly and fairly. + +Maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, issues, and other contributions that are not aligned with this Code of Conduct. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 2.1. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dbc574c..9675475 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,7 +3,8 @@ Thanks for contributing. Issues, reviews, docs, and pull requests are all welcome, and you don't need permission to start. Before you dig in, a quick read of the project [GOVERNANCE.md](https://github.com/floci-io/.github/blob/main/GOVERNANCE.md) explains how -decisions are made and how contributors grow into Maintainers over time. +decisions are made and how contributors grow into Maintainers over time. Everyone taking part is +expected to follow the [Code of Conduct](CODE_OF_CONDUCT.md). ## Developer Certificate of Origin (DCO) sign-off @@ -36,8 +37,9 @@ Why Floci uses the DCO and not a CLA is explained in the ## Running tests locally -Set up a development environment with [uv](https://docs.astral.sh/uv/) (or -`pip install -e ".[dev]"`): +Set up a development environment with [uv](https://docs.astral.sh/uv/). The commands below run the +tools from the project environment with `uv run`; with pip instead, run `pip install -e ".[dev]"` +inside an activated virtualenv and drop the `uv run` prefix. ```bash uv sync --extra dev @@ -46,22 +48,22 @@ uv sync --extra dev Lint, format check and type check, as CI runs them: ```bash -ruff check . -ruff format --check . -mypy floci +uv run ruff check . +uv run ruff format --check . +uv run mypy floci ``` Unit tests need no Docker: ```bash -pytest -m "not integration" -v +uv run pytest -m "not integration" -v ``` Integration tests start a real Floci container, so they need Docker: ```bash docker pull floci/floci:latest -pytest -m integration -v +uv run pytest -m integration -v ``` ## Pull Request Limits and Review Bandwidth @@ -81,4 +83,8 @@ Once your current pull requests are reviewed, merged, or closed, you are welcome - 🗣️ **[GitHub Discussions](https://github.com/orgs/floci-io/discussions)**: feature ideas, design tradeoffs, and proposals. By contributing, you agree to abide by the project -[Code of Conduct](https://github.com/floci-io/.github/blob/main/CODE_OF_CONDUCT.md). +[Code of Conduct](CODE_OF_CONDUCT.md). + +## Reporting Security Issues + +Please do **not** open public issues for security vulnerabilities. See [SECURITY.md](SECURITY.md) for how to report them privately. diff --git a/LICENSE b/LICENSE index bfbcd23..a3f385c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2026 Hector Ventura +Copyright (c) 2026 Floci and its contributors. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 6abea84..14dbdde 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,52 @@ -# testcontainers-floci - -[![PyPI version](https://img.shields.io/pypi/v/testcontainers-floci.svg)](https://pypi.org/project/testcontainers-floci/) -[![Python versions](https://img.shields.io/pypi/pyversions/testcontainers-floci.svg)](https://pypi.org/project/testcontainers-floci/) -[![CI](https://github.com/floci-io/testcontainers-floci-python/actions/workflows/ci.yml/badge.svg)](https://github.com/floci-io/testcontainers-floci-python/actions/workflows/ci.yml) -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) - -Python [Testcontainers](https://testcontainers.com) module for [Floci](https://github.com/floci-io/floci) — the open-source, drop-in replacement for LocalStack Community Edition. - -Floci emulates **41 AWS services** in a single container with: -- **~24 ms** startup time (native image) -- **~13 MiB** idle memory -- **~90 MB** Docker image -- No auth tokens, no feature gates, MIT license +

+ Floci + Floci +

+ +

+ Any Cloud. Locally.
+ Light, fluffy, and always free: Testcontainers for Python
+ No account. No auth token. No feature gates. +

+ +

+ PyPI version + Python versions + CI + License: MIT + GitHub Stars +

+ +

+ Quick Start · + Configuration · + Emulators · + Docs +

+ +--- + +## What is this? + +A Python [Testcontainers](https://testcontainers.com/) module for [Floci](https://github.com/floci-io), the free, +open-source local cloud emulators. `FlociContainer` starts a Floci (AWS) emulator container for your integration tests +and gives you an endpoint and credentials to point boto3 at, plus typed, per-service config dataclasses over the +emulator's environment variables. No cloud account, no auth token. + +See the [Floci documentation](https://floci.io/floci/services/) for the full list of emulated AWS services. + +### The Floci emulators + +testcontainers-floci-python is the Python member of the [Floci](https://github.com/floci-io) Testcontainers family. +Floci is named after [floccus](https://en.wikipedia.org/wiki/Cirrocumulus_floccus), the cloud formation that looks +like popcorn. + +| Emulator | Cloud | Port | Supported | +|----------------------------------------------------|-------|:----:|:--------------------------------------------------------------------------:| +| [floci](https://github.com/floci-io/floci) | AWS | 4566 | ✅ [`testcontainers-floci`](https://pypi.org/project/testcontainers-floci/) | +| [floci-az](https://github.com/floci-io/floci-az) | Azure | 4577 | Planned | +| [floci-gcp](https://github.com/floci-io/floci-gcp) | GCP | 4588 | Planned | +| [floci-oci](https://github.com/floci-io/floci-oci) | OCI | 4599 | Planned | ## Installation @@ -25,6 +60,7 @@ pip install testcontainers-floci import boto3 from floci import FlociContainer + def test_s3(): with FlociContainer() as floci: s3 = boto3.client( @@ -39,18 +75,20 @@ def test_s3(): assert "my-bucket" in buckets ``` -## Using pytest fixtures +### Using pytest fixtures ```python import pytest import boto3 from floci import FlociContainer + @pytest.fixture(scope="session") def floci(): with FlociContainer() as container: yield container + @pytest.fixture def s3_client(floci): return boto3.client( @@ -62,6 +100,7 @@ def s3_client(floci): config=boto3.session.Config(s3={"addressing_style": "path"}), ) + def test_upload(s3_client): s3_client.create_bucket(Bucket="uploads") s3_client.put_object(Bucket="uploads", Key="hello.txt", Body=b"hello") @@ -71,9 +110,14 @@ def test_upload(s3_client): ## Service configuration -Each of Floci's 41 services can be configured individually using typed config dataclasses. +Each AWS service emulated by Floci can be configured individually using a typed config dataclass passed to the +matching `with_*_config(...)` method on `FlociContainer`. Every service config supports at least an `enabled` flag; +some services expose additional settings. See the [Floci documentation](https://floci.io/floci/services/) for the +full list of supported services. + +### Examples -### S3 +#### S3 ```python from floci import FlociContainer @@ -84,7 +128,7 @@ container = FlociContainer().with_s3_config( ) ``` -### SQS +#### SQS ```python from floci.config import SqsConfig @@ -94,7 +138,7 @@ container = FlociContainer().with_sqs_config( ) ``` -### DynamoDB +#### DynamoDB ```python from floci.config import DynamoDbConfig @@ -102,7 +146,7 @@ from floci.config import DynamoDbConfig container = FlociContainer().with_dynamo_db_config(DynamoDbConfig(enabled=True)) ``` -### Lambda +#### Lambda ```python from floci.config import LambdaConfig @@ -117,7 +161,7 @@ container = FlociContainer().with_lambda_config( ) ``` -### RDS (PostgreSQL / MySQL / MariaDB) +#### RDS (PostgreSQL / MySQL / MariaDB) ```python from floci.config import RdsConfig @@ -131,7 +175,7 @@ container = FlociContainer().with_rds_config( ) ``` -### ElastiCache (Redis / Valkey) +#### ElastiCache (Redis / Valkey) ```python from floci.config import ElastiCacheConfig @@ -141,17 +185,15 @@ container = FlociContainer().with_elasti_cache_config( ) ``` -### OpenSearch +#### OpenSearch ```python from floci.config import OpenSearchConfig -container = FlociContainer().with_open_search_config( - OpenSearchConfig(enabled=True, mock=False) -) +container = FlociContainer().with_open_search_config(OpenSearchConfig(enabled=True, mock=False)) ``` -### MSK (Kafka via Redpanda) +#### MSK (Kafka via Redpanda) ```python from floci.config import MskConfig @@ -171,6 +213,7 @@ container = FlociContainer().with_msk_config( | `AppConfigConfig` | AppConfig | | `AppConfigDataConfig` | AppConfig Data | | `AthenaConfig` | Athena | +| `BackupConfig` | AWS Backup | | `BedrockRuntimeConfig` | Bedrock Runtime | | `CloudFormationConfig` | CloudFormation | | `CloudWatchLogsConfig` | CloudWatch Logs | @@ -197,6 +240,7 @@ container = FlociContainer().with_msk_config( | `PipesConfig` | EventBridge Pipes | | `RdsConfig` | RDS | | `ResourceGroupsTaggingConfig` | Resource Groups Tagging | +| `Route53Config` | Route 53 | | `S3Config` | S3 | | `SchedulerConfig` | EventBridge Scheduler | | `SecretsManagerConfig` | Secrets Manager | @@ -206,6 +250,11 @@ container = FlociContainer().with_msk_config( | `SqsConfig` | SQS | | `SsmConfig` | SSM Parameter Store | | `StepFunctionsConfig` | Step Functions | +| `TextractConfig` | Textract | +| `TransferFamilyConfig` | Transfer Family | + +Cross-cutting settings use `TlsConfig` (`with_tls_config(...)`) and `StorageConfig` (`with_storage_config(...)`), +both importable from `floci.config`. ## Container options @@ -215,26 +264,55 @@ container = ( .with_region("eu-west-1") .with_account_id("111122223333") .with_availability_zone("eu-west-1a") - .with_dedicated_network() # isolated Docker network for stateful services + .with_dedicated_network() # isolated Docker network for stateful services ) ``` +| Method | Description | +|----------------------------------|-----------------------------------------------------------------------------------------------| +| `FlociContainer(image=...)` | Creates a container; the default image is `floci/floci:latest` | +| `with_region(str)` | Sets the AWS region (default: `us-east-1`) | +| `with_account_id(str)` | Sets the default AWS account ID (default: `000000000000`) | +| `with_availability_zone(str)` | Sets the default availability zone (default: `us-east-1a`) | +| `with_access_key(str)` | Sets the access key returned by `get_access_key()` (default: `test`) | +| `with_secret_key(str)` | Sets the secret key returned by `get_secret_key()` (default: `test`) | +| `with_log_level(str)` | Sets the Floci log level (e.g. `DEBUG`, `INFO`, `WARN`, `ERROR`) | +| `with_dedicated_network()` | Creates a dedicated Docker network shared by Floci and its sibling containers (RDS, Lambda, …) | +| `with_tls_config(TlsConfig)` | Configures TLS/HTTPS | +| `with_storage_config(StorageConfig)` | Configures persistent storage and volume behaviour | + +The host Docker socket (`/var/run/docker.sock`) is mounted into the container so that Docker-backed services +(RDS, Lambda, ElastiCache, …) can start their sibling containers. + ### Connection details | Method | Returns | |---|---| | `get_endpoint()` | `http://host:port` — pass as `endpoint_url` to boto3 | -| `get_region()` | AWS region string | +| `get_region()` | AWS region string (`"us-east-1"` by default) | | `get_access_key()` | Access key (`"test"` by default) | | `get_secret_key()` | Secret key (`"test"` by default) | -| `get_account_id()` | AWS account ID | +| `get_account_id()` | AWS account ID (`"000000000000"` by default) | +| `get_availability_zone()` | Default availability zone (`"us-east-1a"` by default) | +| `get_dedicated_network_name()` | Name of the dedicated Docker network, or `None` | -## Docker image variants +## Docker image tags -| Tag | Description | -|---|---| -| `floci/floci:latest` | Native image — sub-second startup (recommended) | -| `floci/floci:x.y.z` | Pinned release (native) | +By default `FlociContainer` runs the floating `floci/floci:latest` tag, so you always test against the current +emulator. Pass an image to the constructor to pin a release or follow `main`: + +```python +FlociContainer(image="floci/floci:x.y.z") # a specific release +FlociContainer(image="floci/floci:nightly") # built from main every night +``` + +| Tag | Description | +|-----------------------|-----------------------------------| +| `floci/floci:latest` | Latest release (library default) | +| `floci/floci:x.y.z` | Pinned release | +| `floci/floci:nightly` | Built from `main` every night | + +Every emulator publishes `latest`, `x.y.z` and `nightly` tags. ## Requirements @@ -242,12 +320,45 @@ container = ( - Docker (running locally or in CI) - `testcontainers >= 4.0.0` -## Related projects +## Building and testing + +```bash +uv sync --extra dev +uv run ruff check . && uv run ruff format --check . && uv run mypy floci # lint, format and type check, as CI runs them +uv run pytest -m "not integration" # unit tests, no Docker needed +uv run pytest -m integration # integration tests, starts real Floci containers +``` + +With pip instead of uv, run `pip install -e ".[dev]"` inside an activated virtualenv and drop the +`uv run` prefix. -- [Floci](https://github.com/floci-io/floci) — the emulator itself -- [testcontainers-floci](https://github.com/floci-io/testcontainer-floci) — Java / Spring Boot module -- [Testcontainers for Python](https://testcontainers-python.readthedocs.io) +Integration tests need Docker running. See [CONTRIBUTING.md](CONTRIBUTING.md) for the full contributor workflow. + +## Other languages + +| Language | Repository | +|---|---| +| Java | [testcontainers-floci](https://github.com/floci-io/testcontainers-floci) | +| Node.js / TypeScript | [testcontainers-floci-node](https://github.com/floci-io/testcontainers-floci-node) | +| Python | **testcontainers-floci-python** (this repo) | +| Go | [testcontainers-floci-go](https://github.com/floci-io/testcontainers-floci-go) | +| .NET | [testcontainers-floci-dotnet](https://github.com/floci-io/testcontainers-floci-dotnet) | + +## Community + +- 💬 [Slack](https://join.slack.com/t/floci/shared_invite/zt-3tjn02s3q-A00kEjJ1cZxsg_imTfy6Cw): quick questions and community chat +- 🗣️ [GitHub Discussions](https://github.com/orgs/floci-io/discussions): ideas, design tradeoffs, and proposals +- [CONTRIBUTING.md](CONTRIBUTING.md) · [SECURITY.md](SECURITY.md) · [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) · [MAINTAINERS.md](MAINTAINERS.md) ## License -MIT +MIT. See [LICENSE](LICENSE). + +--- + +
+ +Floci™ is a trademark of Hector Ventura. Code is MIT-licensed; see +[TRADEMARK.md](https://github.com/floci-io/.github/blob/main/TRADEMARK.md) for name and logo use. + +
diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..f18cecd --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,24 @@ +# Security Policy + +## Supported Versions + +Only the current stable release line receives security fixes. Older +releases are best-effort and may not get patches. The stable line is the +most recent minor version tagged on this repo (see +[Releases](https://github.com/floci-io/testcontainers-floci-python/releases)). + +## Reporting a Vulnerability + +Please do **not** open public GitHub issues for security vulnerabilities. + +Report them privately via +[GitHub private vulnerability reporting](https://github.com/floci-io/testcontainers-floci-python/security/advisories/new). +This is the only supported reporting channel and produces a private +thread with the maintainers. + +Expect an initial acknowledgement within a few business days. Once the +report is confirmed, we will coordinate a fix, a release, and (where +appropriate) a security advisory with CVE assignment. + +See [CONTRIBUTING.md](CONTRIBUTING.md#reporting-security-issues) for the +corresponding contributor-facing note. diff --git a/pyproject.toml b/pyproject.toml index e75e0df..6fb1656 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ description = "Testcontainers module for Floci — the open-source local AWS emu readme = "README.md" license = { text = "MIT" } requires-python = ">=3.9" -keywords = ["testcontainers", "floci", "aws", "localstack", "testing"] +keywords = ["testcontainers", "floci", "aws", "testing"] classifiers = [ "Development Status :: 4 - Beta", "Intended Audience :: Developers",