Skip to main content

S3 Storage PaaS Plugin for MetaPaaS

A production-ready S3-compatible object storage service (RustFS + MinIO) packaged as a MetaPaaS plugin.

What is This?

This repository provides the s3aas plugin: a complete, installable PaaS service for Exordos MetaPaaS. Instead of deploying separate control-plane infrastructure for S3 storage, this plugin integrates with a shared MetaPaaS runtime.

Key components:

  • CP (Control Plane): REST API for managing S3 instances, buckets, users, policies, access keys (Python)
  • DP (Data Plane): RustFS-based S3-compatible object storage VM (Packer-built)
  • Integration: Automatic installation + scaling via MetaPaaS orchestration

Quick Start

Prerequisites

  • Running exordos_core deployment
  • Running exordos_metapaas deployment (shares one control-plane)
  • Local build tools: exordos CLI, packer, Python 3.10+

Build

make build \
  REPOSITORY=http://10.20.0.1:8080/exordos-elements \
  INDEX_URL=http://10.20.0.1:8080/simple/

Produces:

  • output/dist/exordos_paas_s3-*.whl — Control-plane package
  • output/exordos-metapaas-s3-dp.raw.zst — Data-plane VM image
  • output/manifests/s3aas.yaml — Element manifest for Exordos Core

Publish to Index

make publish-wheel
# Copies .whl to /srv/exordos-local-repo/simple/

Install to Exordos Core

exordos -e http://10.20.0.2:11010 \
  -u admin -p <admin-password> \
  ee install metapaas --version 0.0.7 --repository http://10.20.0.1:8080/exordos-elements

exordos -e http://10.20.0.2:11010 \
  -u admin -p <admin-password> \
  ee install s3aas --version 0.0.1 --repository http://10.20.0.1:8080/exordos-elements

Wait for both elements to become ACTIVE.

Create an S3 Instance

curl -X POST http://metapaas-cp:8080/v1/types/s3/instances \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer <token>' \
  -d '{
    "name": "s3-data",
    "version": "0.0.1",
    "bucket_name": "data-bucket",
    "encryption": "aes256",
    "versioning": true
  }'

Response (example):

{
  "id": "s3-instance-uuid",
  "name": "s3-data",
  "status": "CREATING",
  "bucket_name": "data-bucket",
  "encryption": "aes256",
  "versioning": true,
  "nodes": [
    {
      "id": "node-uuid",
      "ip": "10.20.0.21",
      "port": 9000
    }
  ]
}

Wait for status to become ACTIVE, then access via boto3:

import boto3

s3 = boto3.client(
    "s3",
    endpoint_url="http://10.20.0.21:9000",
    aws_access_key_id="<access-key>",
    aws_secret_access_key="<secret-key>",
    region_name="us-east-1",
)

s3.put_object(Bucket="data-bucket", Key="test.txt", Body=b"hello")
obj = s3.get_object(Bucket="data-bucket", Key="test.txt")
print(obj["Body"].read())  # b'hello'

API Reference

Instances

Method Endpoint Description
POST /v1/types/s3/instances Create instance
GET /v1/types/s3/instances List instances
GET /v1/types/s3/instances/{id} Get instance details
PATCH /v1/types/s3/instances/{id} Update configuration
DELETE /v1/types/s3/instances/{id} Delete instance

Instance Fields

Field Type Mutable Description
name string ✓ Instance name (unique per project)
version string ✗ PaaS version (e.g., "0.0.1")
bucket_name string ✗ Default bucket name
encryption string ✓ Encryption mode: "none" or "aes256"
versioning boolean ✓ Enable S3 object versioning
mfa_delete boolean ✓ Require MFA to delete objects
status string ✗ Instance status: PENDING, CREATING, ACTIVE, ERROR
nodes array ✗ Data-plane nodes with IP, port

Buckets

Method Endpoint Description
POST /v1/types/s3/instances/{id}/buckets Create bucket
GET /v1/types/s3/instances/{id}/buckets List buckets
DELETE /v1/types/s3/instances/{id}/buckets/{name} Delete bucket

Users

Method Endpoint Description
POST /v1/types/s3/instances/{id}/users Create IAM user
GET /v1/types/s3/instances/{id}/users List users
DELETE /v1/types/s3/instances/{id}/users/{name} Delete user

Access Keys

Method Endpoint Description
POST /v1/types/s3/instances/{id}/users/{user}/access-keys Create key pair
GET /v1/types/s3/instances/{id}/users/{user}/access-keys List keys
DELETE /v1/types/s3/instances/{id}/users/{user}/access-keys/{key} Delete key

Testing

Unit Tests

make test
# or: tox -e py312

Lint & Type Checking

make lint      # ruff check
make format    # ruff format
make typecheck # mypy

Functional Tests

Requires live exordos_core + exordos_metapaas deployment:

EXORDOS_ENDPOINT=http://10.20.0.2:80/api/core \
EXORDOS_USERNAME=admin \
EXORDOS_PASSWORD=<pass> \
EXORDOS_S3_CP_URL=http://10.20.0.2:80/api/metapaas \
make functional

To build that environment from scratch — what the build workflow does on a throwaway runner:

exordos compute hypervisors init --connection-uri qemu+tcp://10.20.0.1/system
exordos bootstrap -i latest -f -m core --pool-agent-placement local \
  --admin-password <admin-pass> --cidr 10.20.0.0/22
exordos e e install metapaas
exordos e e install s3aas          # add -v <version> to pin a build

Wait for both elements to report ACTIVE (exordos ee l -o json -f name=s3aas) and the suite has everything it needs. To test a build of this working tree rather than the published element, make build it and exordos push it to a repository the core can reach first.

Architecture

Control Plane (exordos_paas_s3/)

  • models.py — SQLAlchemy: S3Instance, Bucket, User, Policy, AccessKey, Version
  • controllers.py — REST endpoints with field-level IAM permissions
  • iam_config.py — IAM roles: owner, operator, viewer
  • migrations/ — Database schema migrations (per-paas independent)
  • tests/ — Unit + functional test suites

Data Plane (exordos/s3-dp/)

  • packer.pkr.hcl — Builds RustFS-based S3 VM image
  • conf/ — Configuration templates (rendered per-instance)
  • systemd/ — Service units (rustfs, minio-gateway, etc.)

Manifest & Build

  • exordos/exordos.yaml — Build config (orchestrates CP wheel + DP image + manifest)
  • exordos/manifests/s3aas.yaml.j2 — Jinja2 element manifest (deployed to exordos_core)

CI/CD

  • .github/workflows/tests.yaml — Unit tests on Python 3.10/3.12/3.13
  • .github/workflows/func_tests.yaml — Full E2E: bootstrap core → build → install → test

Repository Structure

.
├── exordos_paas_s3/              # Control-plane Python package
│   ├── models.py                 # SQLAlchemy ORM models
│   ├── controllers.py            # REST API controllers
│   ├── iam_config.py             # IAM setup
│   ├── migrations/               # Database migrations
│   └── tests/
│       ├── test_*.py             # Unit tests
│       └── functional/           # E2E tests
├── exordos/
│   ├── exordos.yaml              # Build config
│   ├── manifests/
│   │   └── s3aas.yaml.j2         # Element manifest template
│   └── s3-dp/
│       └── packer.pkr.hcl        # Data-plane image config
├── pyproject.toml                # Project metadata + dependencies
├── tox.ini                       # Test automation
├── Makefile                      # Build targets
├── .github/workflows/            # CI/CD pipelines
├── README.md                     # This file
└── output/                       # Build artifacts (git-ignored)
    ├── dist/                     # Python wheel
    ├── manifests/                # Rendered manifests
    └── images/                   # Packer-built images

Configuration

Build Manifest Variables

Control how the element image and manifest are rendered:

exordos build \
  --manifest-var repository=http://custom-repo.local/exordos-elements \
  --manifest-var index_url=http://custom-repo.local/simple/

These override defaults in exordos/exordos.yaml.

Instance Configuration

When creating an S3 instance, specify:

{
  "name": "my-s3",
  "version": "0.0.1",
  "bucket_name": "default-bucket",
  "encryption": "aes256",
  "versioning": true,
  "mfa_delete": false
}

Configuration is applied to all data-plane nodes in the instance (currently single-node; clustering planned).

Troubleshooting

Instance stuck in CREATING

Check PluginReconciler logs on metapaas-cp:

exordos -e http://10.20.0.2:11010 -u admin -p <pass> \
  cn exec metapaas-cp -- \
  journalctl -u metapaas-plugin-reconciler -f

Also check orchestration logs:

exordos -e http://10.20.0.2:11010 -u admin -p <pass> \
  cn exec metapaas-cp -- \
  tail -f /var/log/orch-api.log

Cannot connect to S3 API

Verify data-plane node is ACTIVE and reachable:

# Get node IP from instance
curl -s http://metapaas-cp:8080/v1/types/s3/instances/<id> \
  -H 'Authorization: Bearer <token>' | jq '.nodes[].ip'

# Test connectivity
telnet <ip> 9000

# Check S3 health
curl -s http://<ip>:9000/health

Permissions error (403 Forbidden)

Ensure test user has owner role in METAPAAS_PROJECT_ID:

# Query user's roles
curl -s http://10.20.0.2:11010/v1/iam/projects/4d657461-0000-0000-0000-000000000002/roles \
  -u admin:<pass> | jq '.items[] | select(.user_id=="<your-uuid>")'

Development Guidelines

Adding New Endpoints

  1. Add model field to models.py
  2. Add controller method + permissions to controllers.py
  3. Add migration in migrations/ with UUID name
  4. Test in tests/functional/

Read-Only Fields

Fields like status, id, created_at, nodes, kind are read-only and auto-populated. Omit them from CREATE payloads.

IAM Model

Permissions are bound to METAPAAS_PROJECT_ID, not instance project. Users must have explicit role grant in metapaas project to access any S3 instance.

References

License

Proprietary — Exordos

Metadata

Release files for exordos-s3 0.1.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for exordos-s3 0.1.2
File Size Uploaded
exordos_s3-0.1.2.tar.gz 1.2 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for exordos-s3 0.1.2
File Interpreter ABI Platform
exordos_s3-0.1.2-py3-none-any.whl Python 3 none any Details

Total release size: 1.3 MB

Release files / exordos_s3-0.1.2.tar.gz

Download URL exordos_s3-0.1.2.tar.gz
Size 1.2 MB
Tags Source
SHA-256 checksum
How to use checksums
6f8b115c91f0039a0bcd627385d354c854c7652e4fcf478eaed86d847a8a00f2
BLAKE2b-256 checksum
How to use checksums
49c08ce26afd49ef5335d3493e150fde071f8f6c8c600096f30b50f8ce9177a0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 22, 2026.

Transparency log

Release files / exordos_s3-0.1.2-py3-none-any.whl

Download URL exordos_s3-0.1.2-py3-none-any.whl
Size 53.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ce9c77548e7aa9b86974d9db5851be35933aec687017d89ec68ede43231d77ba
BLAKE2b-256 checksum
How to use checksums
ff512785bc8dc751f2d3542b73d6c221c977c31e68c1f8009e93ccf12f5fc31b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 22, 2026.

Transparency log

Release history Release notifications | RSS feed

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

This release

0.1.2 This release

2 release files

0.1.1

2 release files

0.1.0

2 release files

0.0.9

2 release files

0.0.8

2 release files

0.0.7

2 release files

0.0.6

2 release files

0.0.5

2 release files

0.0.4

2 release files

0.0.3

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page