Skip to content

feat(lab-06): PostgreSQL production -- HA replication (bitnami), PgBo… #13

feat(lab-06): PostgreSQL production -- HA replication (bitnami), PgBo…

feat(lab-06): PostgreSQL production -- HA replication (bitnami), PgBo… #13

Workflow file for this run

name: CI
on:
push:
branches: [main, develop, 'feature/**', 'bugfix/**']
pull_request:
branches: [main, develop]
permissions:
contents: read
security-events: write
jobs:
validate:
name: Validate Configuration
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Validate Docker Compose files
run: |
# Strictly validate the standalone lab (fully built out)
echo "Validating: docker/docker-compose.standalone.yml"
docker compose -f docker/docker-compose.standalone.yml config -q
echo "OK: docker/docker-compose.standalone.yml"
# Strictly validate lan.yml (Lab 02 — fully built out)
echo "Validating: docker/docker-compose.lan.yml"
docker compose -f docker/docker-compose.lan.yml config -q
echo "OK: docker/docker-compose.lan.yml"
# Parse-check remaining scaffold files
echo "Validating: docker/docker-compose.advanced.yml"
docker compose -f docker/docker-compose.advanced.yml config -q
echo "OK: docker/docker-compose.advanced.yml"
echo "Validating: docker/docker-compose.sso.yml"
docker compose -f docker/docker-compose.sso.yml config -q
echo "OK: docker/docker-compose.sso.yml"
echo "Validating: docker/docker-compose.integration.yml"
docker compose -f docker/docker-compose.integration.yml config -q
echo "OK: docker/docker-compose.integration.yml"
echo "Validating: docker/docker-compose.production.yml"
docker compose -f docker/docker-compose.production.yml config -q
echo "OK: docker/docker-compose.production.yml"
- name: ShellCheck — lab test scripts
run: |
sudo apt-get install -y shellcheck -qq
shellcheck tests/labs/*.sh
- name: Validate module manifest
run: |
python3 -c "
import sys, re
with open('it-stack-postgresql.yml') as f:
content = f.read()
required = ['module:', 'version:', 'phase:', 'category:', 'ports:']
missing = [k for k in required if k not in content]
if missing:
print('Missing fields:', missing); sys.exit(1)
print('Manifest valid')
"
security-scan:
name: Security Scan
runs-on: ubuntu-latest
needs: validate
steps:
- uses: actions/checkout@v4
- name: Trivy — scan Dockerfile
uses: aquasecurity/trivy-action@0.28.0
with:
scan-type: config
scan-ref: .
exit-code: '0'
severity: CRITICAL,HIGH
- name: Trivy — SARIF output
uses: aquasecurity/trivy-action@0.28.0
with:
scan-type: config
scan-ref: .
format: sarif
output: trivy-results.sarif
- name: Upload SARIF to GitHub Security
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: trivy-results.sarif
lab-01-smoke:
name: Lab 01 — Smoke Test
runs-on: ubuntu-latest
needs: validate
continue-on-error: true # scaffold stubs; full lab runs on real VMs
steps:
- uses: actions/checkout@v4
- name: Install PostgreSQL client tools
run: sudo apt-get install -y postgresql-client netcat-openbsd
- name: Start standalone stack
run: docker compose -f docker/docker-compose.standalone.yml up -d
- name: Wait for PostgreSQL to be ready
run: |
echo "Waiting for PostgreSQL..."
timeout 120 bash -c 'until pg_isready -h localhost -p 5432 -U labadmin; do sleep 3; done'
docker compose -f docker/docker-compose.standalone.yml ps
- name: Run Lab 03-01 test script
env:
PGPASSWORD: "Lab01Password!"
run: bash tests/labs/test-lab-03-01.sh
- name: Collect logs on failure
if: failure()
run: docker compose -f docker/docker-compose.standalone.yml logs
- name: Cleanup
if: always()
run: docker compose -f docker/docker-compose.standalone.yml down -v
lab-02-smoke:
name: Lab 02 — Streaming Replication
runs-on: ubuntu-latest
needs: validate
continue-on-error: true
steps:
- uses: actions/checkout@v4
- name: Install PostgreSQL client tools
run: sudo apt-get install -y postgresql-client netcat-openbsd curl
- name: Start LAN stack (primary + replica + pgAdmin)
run: docker compose -f docker/docker-compose.lan.yml up -d
- name: Wait for primary PostgreSQL
run: |
echo "Waiting for primary..."
timeout 120 bash -c 'until pg_isready -h localhost -p 5432 -U labadmin; do sleep 3; done'
- name: Wait for replica PostgreSQL
run: |
echo "Waiting for replica (base backup ~30s)..."
timeout 180 bash -c 'until pg_isready -h localhost -p 5433 -U labadmin; do echo -n "."; sleep 5; done'
echo ""
- name: Run Lab 03-02 test script
env:
ADMIN_PASS: "Lab02Password!"
run: bash tests/labs/test-lab-03-02.sh
- name: Collect logs on failure
if: failure()
run: docker compose -f docker/docker-compose.lan.yml logs
- name: Cleanup
if: always()
run: docker compose -f docker/docker-compose.lan.yml down -v
lab-03-smoke:
name: Lab 03 — PgBouncer + pg_stat_statements + Metrics
runs-on: ubuntu-latest
needs: validate
continue-on-error: true
steps:
- uses: actions/checkout@v4
- name: Install tools
run: sudo apt-get install -y postgresql-client netcat-openbsd curl
- name: Start advanced stack (primary + replica + PgBouncer + pg-exporter)
run: docker compose -f docker/docker-compose.advanced.yml up -d
- name: Wait for primary
run: timeout 120 bash -c 'until pg_isready -h localhost -p 5432 -U labadmin; do sleep 3; done'
- name: Wait for replica
run: timeout 180 bash -c 'until pg_isready -h localhost -p 5433 -U labadmin; do sleep 5; done'
- name: Wait for PgBouncer
run: timeout 60 bash -c 'until pg_isready -h localhost -p 5434 -U labadmin; do sleep 3; done'
- name: Run Lab 03-03 test script
env:
ADMIN_PASS: "Lab03Password!"
run: bash tests/labs/test-lab-03-03.sh
- name: Collect logs on failure
if: failure()
run: docker compose -f docker/docker-compose.advanced.yml logs
- name: Cleanup
if: always()
run: docker compose -f docker/docker-compose.advanced.yml down -v
lab-04-smoke:
name: Lab 04 — Keycloak OIDC SSO + pgAdmin oauth2-proxy
runs-on: ubuntu-latest
needs: validate
continue-on-error: true
steps:
- uses: actions/checkout@v4
- name: Install tools
run: sudo apt-get install -y postgresql-client netcat-openbsd curl
- name: Start SSO stack (KC + pg-primary + pgAdmin + oauth2-proxy)
run: docker compose -f docker/docker-compose.sso.yml up -d
- name: Wait for Keycloak
run: timeout 200 bash -c 'until curl -sf http://localhost:8080/health/ready | grep -q UP; do sleep 5; done'
- name: Wait for PostgreSQL
run: timeout 60 bash -c 'until pg_isready -h localhost -p 5432 -U labadmin; do sleep 3; done'
- name: Run Lab 03-04 test script
env:
KC_PASS: "Lab04Password!"
run: bash tests/labs/test-lab-03-04.sh
- name: Collect logs on failure
if: failure()
run: docker compose -f docker/docker-compose.sso.yml logs
- name: Cleanup
if: always()
run: docker compose -f docker/docker-compose.sso.yml down -v
lab-05-smoke:
name: Lab 05 -- PG multi-DB + Keycloak + Redis + Traefik integration
runs-on: ubuntu-latest
needs: validate
continue-on-error: true
steps:
- uses: actions/checkout@v4
- name: Install tools
run: sudo apt-get install -y postgresql-client redis-tools curl netcat-openbsd
- name: Start integration stack
run: docker compose -f docker/docker-compose.integration.yml up -d
- name: Wait for Keycloak
run: timeout 200 bash -c 'until curl -sf http://localhost:8080/health/ready | grep -q UP; do sleep 5; done'
- name: Wait for PostgreSQL
run: timeout 60 bash -c 'until pg_isready -h localhost -p 5432 -U postgres; do sleep 3; done'
- name: Run Lab 03-05 test script
env:
PG_PASS: "Lab05Password!"
KC_PASS: "Lab05Password!"
run: bash tests/labs/test-lab-03-05.sh
- name: Collect logs on failure
if: failure()
run: docker compose -f docker/docker-compose.integration.yml logs
- name: Cleanup
if: always()
run: docker compose -f docker/docker-compose.integration.yml down -v
lab-06-smoke:
name: Lab 06 — PostgreSQL Production HA (primary+replica+PgBouncer+exporter)
runs-on: ubuntu-latest
needs: validate
continue-on-error: true
steps:
- uses: actions/checkout@v4
- name: Install tools
run: sudo apt-get install -y postgresql-client curl
- name: Start production stack
run: docker compose -f docker/docker-compose.production.yml up -d
- name: Wait for PG primary
run: timeout 120 bash -c 'until pg_isready -h localhost -p 5432 -U labadmin; do sleep 3; done'
- name: Wait for PG replica
run: timeout 60 bash -c 'until pg_isready -h localhost -p 5433 -U labadmin; do sleep 3; done'
- name: Wait for PgBouncer
run: timeout 60 bash -c 'until pg_isready -h localhost -p 6432 -U labadmin; do sleep 3; done'
- name: Run Lab 03-06 test script
env:
PG_PASS: "Lab06Password!"
run: bash tests/labs/test-lab-03-06.sh
- name: Collect logs on failure
if: failure()
run: docker compose -f docker/docker-compose.production.yml logs
- name: Cleanup
if: always()
run: docker compose -f docker/docker-compose.production.yml down -v