Skip to main content

Python library for provisioning and managing PostgreSQL on AWS RDS.

Project description

pg-provisioner

pg-provisioner is a Python service library for provisioning, managing, and connecting to PostgreSQL databases on AWS RDS.

It wraps boto3 and psycopg2 with a simple, consistent API for:

  • creating, modifying, and deleting RDS instances
  • resizing and downsizing instances (with export/import)
  • snapshot management and S3 export
  • safe connection management (plug-and-play with psycopg2)
  • robust error handling and structured logging

✨ Features

  • 🏗 Provision PostgreSQL on AWS RDS with a single call
  • ⚙️ Resize and downsize instances automatically
  • 🧩 Snapshots and exports to S3 for backup or migration
  • 🔐 Connection management ready for psycopg2
  • 🪶 Typed exceptions with detailed context
  • 🔁 Retries and waiters for long-running AWS operations
  • 🧠 Fully mockable and testable (100% test coverage)
  • 🧾 Structured logging with dynamic verbosity
  • 🌍 Environment-configurable defaults

📦 Installation

pip install pg-provisioner

Or, from source:

git clone https://github.com/your-org/pg-provisioner.git
cd pg-provisioner
pip install -e .

⚙️ Configuration

pg-provisioner uses environment variables for defaults (all optional):

Variable Default Description
PG_DEFAULT_REGION us-west-2 Default AWS region
PG_DEFAULT_CLASS db.t3.micro Default instance type
PG_DEFAULT_ENGINE postgres RDS engine
PG_DEFAULT_STORAGE 20 Allocated storage in GB
PG_DEFAULT_DB_NAME appdb Default DB name
PG_DEFAULT_POLL_INTERVAL 20 Polling interval for status checks
LOG_LEVEL INFO Logging verbosity
LOG_TO_FILE 0 Write logs to file (set 1 to enable)
LOG_FILE pg_provisioner.log Log file path

You can store these in a .env file (optional) or use supported boto3/aws credentials in your system/environment.

# .env
AWS_ACCESS_KEY_ID=...
AWS_SECRET_ACCESS_KEY=...
AWS_DEFAULT_REGION=...

🚀 Quick Start

Synchronous (CLI/Scripts)

For scripts or CLI tools where blocking is acceptable:

from pg_provisioner import PostgresProvisioner

# Initialize the provisioner
prov = PostgresProvisioner(region_name="us-west-2")

# Create a new PostgreSQL instance (waits until available ~6 mins)
db = prov.create_instance(
    identifier="demo-db",
    db_name="myapp",
    master_user="pgadmin",  # Don't use 'admin' (reserved word)
    master_pass="StrongPass123!",
    wait=True  # Blocks until available
)

print("Created DB:", db.identifier, db.endpoint)

# Connect to the database (psycopg2)
conn = prov.connect("demo-db", password="StrongPass123!")
print("Connection established:", conn)
conn.close()

# Resize (change instance class)
prov.resize_instance("demo-db", new_class="db.t3.small")

# Delete instance
prov.delete_instance("demo-db")

Asynchronous (API/Web Services)

For APIs or web services where you need non-blocking operations:

from pg_provisioner import PostgresProvisioner

prov = PostgresProvisioner(region_name="us-west-2")

# Step 1: Initiate provisioning (returns immediately!)
db = prov.create_instance(
    identifier="user-db-123",
    db_name="myapp",
    master_user="pgadmin",
    master_pass="StrongPass123!",
    wait=False  # Non-blocking (default)
)

print(f"Provisioning initiated: {db.status}")  # Usually 'creating'

# Step 2: Poll for status (in separate API requests)
info = prov.describe_instance("user-db-123")
if info.is_available():
    # Step 3: Get connection config when ready
    config = prov.get_connection_info("user-db-123", "StrongPass123!")
    # config is ready for: psycopg2.connect(**config)
    print(f"Ready! Connect to: {config['host']}:{config['port']}")

See API_USAGE.md for complete API integration patterns.


🔒 Error Handling

All exceptions inherit from PGProvisionerError and carry context:

Exception Description
ProvisioningError Failed to create or configure an instance
ResizeError Failed to resize instance
DeletionError Deletion failed
SnapshotError Snapshot or export failed
AWSOperationError AWS API or boto3 error
NetworkError Network or endpoint connectivity issue
ConnectionNotReady Instance not yet available
InvalidCredentialsError Bad PostgreSQL username/password
ConnectionErrorPG psycopg2 connection failed

Example:

from pg_provisioner import PostgresProvisioner
from pg_provisioner.exceptions import ProvisioningError

prov = PostgresProvisioner()

try:
    prov.create_instance("test-db")
except ProvisioningError as e:
    print(f"Failed to provision DB: {e}")

🧾 Logging

By default, logs are printed to stdout:

[2025-11-05 12:00:00] [INFO] [pg_provisioner.provisioner] Creating instance demo-db...
[2025-11-05 12:01:23] [INFO] [pg_provisioner.utils] [demo-db] reached state: available

Enable file logging with:

export LOG_TO_FILE=1
export LOG_FILE=/var/log/pg_provisioner.log

🎯 Usage Patterns

For Web APIs & Services

pg_provisioner is designed for async/polling workflows where long-running operations can't block API requests:

# API Endpoint 1: Initiate provisioning (returns immediately)
@app.post("/provision")
def initiate_provisioning(request):
    prov = PostgresProvisioner()
    db = prov.create_instance(
        identifier=f"user-{request.user_id}-db",
        wait=False  # Returns immediately!
    )
    return {"instance_id": db.identifier, "status": db.status.value}

# API Endpoint 2: Check status (poll this every 10-30 seconds)
@app.get("/provision/{instance_id}/status")
def check_status(instance_id):
    prov = PostgresProvisioner()
    info = prov.describe_instance(instance_id)
    return {"status": info.status.value, "is_available": info.is_available()}

# API Endpoint 3: Get connection config (when ready)
@app.get("/provision/{instance_id}/config")
def get_config(instance_id, password):
    prov = PostgresProvisioner()
    config = prov.get_connection_info(instance_id, password)
    return {"config": config}  # User can now migrate their data

Typical provisioning time: 5-8 minutes

For complete API integration examples, see API_USAGE.md.


🧪 Testing

pip install -e '.[dev]'
pytest --cov=src --cov-report=term-missing --cov-fail-under=75

Functional tests (mock AWS):

pytest tests/test_functional.py -v

These tests use moto to simulate AWS RDS and S3 locally.

Real AWS Integration Tests:

export RUN_REAL_AWS=1
pytest tests/test_real_aws_rds.py -v

⚠️ WARNING: This creates real RDS instances on AWS and incurs costs. The test includes:

  • test_real_rds_lifecycle - Full lifecycle with wait=True
  • test_real_rds_async_workflow - Demonstrates async polling pattern for APIs

Both tests clean up resources automatically.


🧱 Project Structure

src/
├── aws_client.py          # AWS SDK wrapper with retries & consistent errors
├── constants.py           # Default config and environment overrides
├── exceptions.py          # Typed exceptions for all failure modes
├── logging_config.py      # Centralized structured logging
├── models.py              # DBInstanceInfo dataclass for RDS metadata
├── provisioner.py         # High-level provisioning logic
├── utils.py               # Waiters and helpers
└── py.typed               # Type hint marker

🤝 Contributing

  1. Fork and clone the repo
  2. Create a new branch: git checkout -b feature/awesome
  3. Write or update tests (100% coverage required)
  4. Run tests: pytest --cov=src --cov-fail-under=75
  5. Submit a PR 🎉

🧩 License

MIT License © 2025 — Causum™ Analytics

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pg_provisioner-1.0.0.tar.gz (11.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pg_provisioner-1.0.0-py3-none-any.whl (5.7 kB view details)

Uploaded Python 3

File details

Details for the file pg_provisioner-1.0.0.tar.gz.

File metadata

  • Download URL: pg_provisioner-1.0.0.tar.gz
  • Upload date:
  • Size: 11.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.3

File hashes

Hashes for pg_provisioner-1.0.0.tar.gz
Algorithm Hash digest
SHA256 e8333f2fc5cd206c2c0ec07fff8eb2ee521d404ce97bf6fb27cdbce45baecde3
MD5 a0846ba8f6cb6982699ceef2e218f9ec
BLAKE2b-256 fb5fd15d8eae12e5736488fa03ae987a0a3f62c38d6bf3a59c3e8a3da8dddb38

See more details on using hashes here.

File details

Details for the file pg_provisioner-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: pg_provisioner-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 5.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.3

File hashes

Hashes for pg_provisioner-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 62f282a0eafb7f5de80137b1c5b71e07e94f837237a5d36701e36986fcc83d9a
MD5 4379924e90422590e1d15f43aeb929a0
BLAKE2b-256 c83e4a4ec570442c383aea71d927c601048987fafba5e9be0e893ad95d42f5c3

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page