From 48fed18078265156e4d5396fd733e7e62f2e02d4 Mon Sep 17 00:00:00 2001 From: Celes Renata Date: Sun, 19 Apr 2026 23:17:22 +0000 Subject: [PATCH] feat: per-stage PostgreSQL users for database isolation (stonks_beta, stonks_paper) --- infra/helm/stonks-oracle/values-beta.yaml | 1 + infra/helm/stonks-oracle/values-paper.yaml | 1 + pipelines/runmefirst.sh | 62 ++++++++++++++++------ 3 files changed, 48 insertions(+), 16 deletions(-) diff --git a/infra/helm/stonks-oracle/values-beta.yaml b/infra/helm/stonks-oracle/values-beta.yaml index 710347f..9ee86f3 100644 --- a/infra/helm/stonks-oracle/values-beta.yaml +++ b/infra/helm/stonks-oracle/values-beta.yaml @@ -15,6 +15,7 @@ config: POSTGRES_DB: "stonks_beta" REDIS_DB: "1" DEPLOY_STAGE: "beta" + POSTGRES_USER: "stonks_beta" ## All services pinned to 1 replica with lighter resource limits services: diff --git a/infra/helm/stonks-oracle/values-paper.yaml b/infra/helm/stonks-oracle/values-paper.yaml index 6778bba..0460c45 100644 --- a/infra/helm/stonks-oracle/values-paper.yaml +++ b/infra/helm/stonks-oracle/values-paper.yaml @@ -15,6 +15,7 @@ config: POSTGRES_DB: "stonks_paper" REDIS_DB: "2" DEPLOY_STAGE: "paper" + POSTGRES_USER: "stonks_paper" ## Secrets override: Alpaca paper trading API endpoint secrets: diff --git a/pipelines/runmefirst.sh b/pipelines/runmefirst.sh index d97ff7b..5d9231e 100755 --- a/pipelines/runmefirst.sh +++ b/pipelines/runmefirst.sh @@ -272,6 +272,12 @@ ALPACA_URL=$(cat /root/sources/celesrenata/stonks-oracle/alpaca.url 2>/dev/null POLYGON_KEY=$(cat /root/sources/celesrenata/stonks-oracle/polygon.io.key 2>/dev/null || echo "") for ns in stonks-beta stonks-paper stonks-oracle; do + # Determine the correct Postgres user for this namespace + case "$ns" in + stonks-beta) PG_USER="stonks_beta" ;; + stonks-paper) PG_USER="stonks_paper" ;; + *) PG_USER="stonks" ;; + esac kubectl create secret generic stonks-core-secrets \ --from-literal=POSTGRES_PASSWORD='St0nks0racl3!' \ --from-literal=REDIS_PASSWORD="$REDIS_PW" \ @@ -305,25 +311,47 @@ echo "" echo "--- Step 6d: Creating stage-isolated databases ---" PG_POD=$(kubectl get pods -n postgresql-service -l cnpg.io/cluster=postgresql,role=primary -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || echo "postgresql-1") -# Create databases (idempotent) -for db in stonks_beta stonks_paper; do - kubectl exec -n postgresql-service "$PG_POD" -c postgres -- \ - psql -U postgres -tc "SELECT 1 FROM pg_database WHERE datname='$db'" | grep -q 1 || \ - kubectl exec -n postgresql-service "$PG_POD" -c postgres -- \ - psql -U postgres -c "CREATE DATABASE $db OWNER stonks;" - echo " ✓ Database $db exists" +# Create databases and per-stage users (idempotent) +for pair in "stonks_beta:stonks_beta" "stonks_paper:stonks_paper"; do + DB="${pair%%:*}" + USER="${pair##*:}" + kubectl exec -i -n postgresql-service "$PG_POD" -c postgres -- psql -U postgres << SQL +DO \$\$ BEGIN + IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = '$USER') THEN + CREATE USER $USER WITH PASSWORD 'St0nks0racl3!'; + END IF; +END \$\$; +SELECT 'CREATE DATABASE $DB OWNER $USER' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname='$DB')\gexec +GRANT ALL PRIVILEGES ON DATABASE $DB TO $USER; +SQL + # Ensure schema permissions + kubectl exec -i -n postgresql-service "$PG_POD" -c postgres -- psql -U postgres -d "$DB" << SQL +GRANT ALL ON SCHEMA public TO $USER; +ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO $USER; +ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO $USER; +SQL + echo " ✓ Database $DB with user $USER" done -# Run migrations on all three databases (idempotent — IF NOT EXISTS in DDL) +# Live DB stays as 'stonks' user (already exists) +kubectl exec -i -n postgresql-service "$PG_POD" -c postgres -- \ + psql -U postgres -tc "SELECT 1 FROM pg_database WHERE datname='stonks'" | grep -q 1 || \ + kubectl exec -i -n postgresql-service "$PG_POD" -c postgres -- \ + psql -U postgres -c "CREATE DATABASE stonks OWNER stonks;" +echo " ✓ Database stonks with user stonks" + +# Run migrations on all three databases as their respective users REPO_ROOT="${SCRIPT_DIR}/../stonks-oracle" if [ -d "$REPO_ROOT/infra/migrations" ]; then - for db in stonks stonks_beta stonks_paper; do - echo " Running migrations on $db..." + for pair in "stonks:stonks" "stonks_beta:stonks_beta" "stonks_paper:stonks_paper"; do + DB="${pair%%:*}" + USER="${pair##*:}" + echo " Running migrations on $DB as $USER..." for migration in $(ls "$REPO_ROOT/infra/migrations/"*.sql 2>/dev/null | sort); do kubectl exec -i -n postgresql-service "$PG_POD" -c postgres -- \ - psql -U postgres -d "$db" < "$migration" > /dev/null 2>&1 || true + psql -U "$USER" -d "$DB" < "$migration" > /dev/null 2>&1 || true done - echo " ✓ Migrations applied to $db" + echo " ✓ Migrations applied to $DB" done else echo " ⚠ Migrations directory not found at $REPO_ROOT/infra/migrations — skipping" @@ -331,12 +359,14 @@ fi # Seed symbol registry data on all three databases if [ -d "$REPO_ROOT/services/symbol_registry" ]; then - for db in stonks stonks_beta stonks_paper; do - echo " Seeding $db..." + for pair in "stonks:stonks" "stonks_beta:stonks_beta" "stonks_paper:stonks_paper"; do + DB="${pair%%:*}" + USER="${pair##*:}" + echo " Seeding $DB..." POSTGRES_HOST=postgresql-rw.postgresql-service.svc.cluster.local \ - POSTGRES_PASSWORD='St0nks0racl3!' POSTGRES_USER=stonks POSTGRES_DB="$db" \ + POSTGRES_PASSWORD='St0nks0racl3!' POSTGRES_USER="$USER" POSTGRES_DB="$DB" \ python3 -m services.symbol_registry.seed 2>/dev/null || true - echo " ✓ Seed data applied to $db" + echo " ✓ Seed data applied to $DB" done fi echo ""