S3 PaaS plugin for the Exordos MetaPaaS runtime
Project description
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_coredeployment - Running
exordos_metapaasdeployment (shares one control-plane) - Local build tools:
exordosCLI,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 packageoutput/exordos-metapaas-s3-dp.raw.zst— Data-plane VM imageoutput/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:11010 \
EXORDOS_USERNAME=admin \
EXORDOS_PASSWORD=<pass> \
METAPAAS_USERNAME=metapaas \
METAPAAS_PASSWORD=<pass> \
EXORDOS_S3_CP_URL=http://10.20.0.X:8080 \
make functional
Or use the E2E preparation script:
python exordos_paas_s3/tests/functional/prepare_env.py \
--metapaas-dir ../exordos_metapaas \
--project-dir . \
--output-dir /tmp/s3-build \
--endpoint http://10.20.0.2:11010 \
--username admin \
--password <admin-pass> \
--wait-timeout 600
This script:
- Builds s3aas + metapaas elements
- Serves them via HTTP
- Installs both to running exordos_core
- Outputs environment variables for tests
- Waits for all nodes ACTIVE
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
- Add model field to
models.py - Add controller method + permissions to
controllers.py - Add migration in
migrations/with UUID name - 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
- MetaPaaS Platform: ../exordos_metapaas/
- How to Build New PaaS: ../exordos_metapaas/HOW_TO_BUILD_NEW_PAAS.md
- Mail-aas Blueprint: ../exordos_mail/ (reference plugin, simpler than s3aas)
- Exordos Core: https://github.com/exordos/exordos_core
License
Proprietary — Exordos
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file exordos_s3-0.0.5.tar.gz.
File metadata
- Download URL: exordos_s3-0.0.5.tar.gz
- Upload date:
- Size: 190.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d32db79269fd04396a410aedf9ea7d8e73402c8c8caac8f997d64c26581a4790
|
|
| MD5 |
41d406e23a40653af2a08ef12f0619f3
|
|
| BLAKE2b-256 |
347f1967a1d3d25e1f2177edbc472796a2738ede75f1b037a9d6dbe389c229ad
|
Provenance
The following attestation bundles were made for exordos_s3-0.0.5.tar.gz:
Publisher:
publish-to-pypi.yml on exordos/exordos_s3
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
exordos_s3-0.0.5.tar.gz -
Subject digest:
d32db79269fd04396a410aedf9ea7d8e73402c8c8caac8f997d64c26581a4790 - Sigstore transparency entry: 2047445280
- Sigstore integration time:
-
Permalink:
exordos/exordos_s3@0c066a33ef167f425a8069b5fe5a882bf19e9ea6 -
Branch / Tag:
refs/tags/0.0.5 - Owner: https://github.com/exordos
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-to-pypi.yml@0c066a33ef167f425a8069b5fe5a882bf19e9ea6 -
Trigger Event:
push
-
Statement type:
File details
Details for the file exordos_s3-0.0.5-py3-none-any.whl.
File metadata
- Download URL: exordos_s3-0.0.5-py3-none-any.whl
- Upload date:
- Size: 53.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
120dee96c23c6266349d5eba33992ee2c1b5badae29bfb0942e28f42edf0851b
|
|
| MD5 |
e418ac1b7470065cd4a49ef0d712a611
|
|
| BLAKE2b-256 |
012b6d7e5e964ae1ffac192de82d9896d17ae667fcb6fc7c88e38ce85f2cb2e0
|
Provenance
The following attestation bundles were made for exordos_s3-0.0.5-py3-none-any.whl:
Publisher:
publish-to-pypi.yml on exordos/exordos_s3
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
exordos_s3-0.0.5-py3-none-any.whl -
Subject digest:
120dee96c23c6266349d5eba33992ee2c1b5badae29bfb0942e28f42edf0851b - Sigstore transparency entry: 2047445302
- Sigstore integration time:
-
Permalink:
exordos/exordos_s3@0c066a33ef167f425a8069b5fe5a882bf19e9ea6 -
Branch / Tag:
refs/tags/0.0.5 - Owner: https://github.com/exordos
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-to-pypi.yml@0c066a33ef167f425a8069b5fe5a882bf19e9ea6 -
Trigger Event:
push
-
Statement type: