All checks were successful
CI/CD / detect-changes (pull_request) Successful in 21s
CI/CD / test-frontend (pull_request) Successful in 5s
CI/CD / test-go-pg (pull_request) Successful in 2m58s
CI/CD / test-sqlite (pull_request) Successful in 3m2s
CI/CD / build-and-deploy (pull_request) Successful in 1m15s
PG-backed cluster registry for horizontal scaling — zero new infrastructure (no etcd/Consul/Redis). MVP convergence point. - node_registry UNLOGGED table (migration 013) - Self-registration, heartbeat tick (10s), stale sweep (30s), self-eviction (os.Exit on 0 rows → K8s restarts) - GET /api/v1/admin/cluster returns nodes with runtime stats - Health endpoints include node_id + cluster size/peers - cluster-dashboard admin surface with auto-refresh - 3-replica E2E test (docker-compose-e2e.yml + ci/e2e-cluster-test.sh) - chat + cluster-dashboard added to curated default bundle - 5 new tests (3 unit + 2 handler), all passing Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
128 lines
3.8 KiB
YAML
128 lines
3.8 KiB
YAML
# docker-compose-e2e.yml — Multi-replica E2E testing (Postgres)
|
|
#
|
|
# Three Switchboard replicas behind an nginx load balancer,
|
|
# sharing a single Postgres instance. Tests cross-replica
|
|
# broadcast via pg_notify and cluster registry (v0.6.0).
|
|
#
|
|
# Usage:
|
|
# docker compose -f docker-compose-e2e.yml up --build -d
|
|
# ./ci/e2e-chat-test.sh # chat E2E
|
|
# ./ci/e2e-cluster-test.sh # cluster registry E2E
|
|
# docker compose -f docker-compose-e2e.yml down -v
|
|
|
|
services:
|
|
postgres:
|
|
image: postgres:16-alpine
|
|
environment:
|
|
POSTGRES_DB: switchboard_e2e
|
|
POSTGRES_USER: switchboard
|
|
POSTGRES_PASSWORD: e2e-password
|
|
ports:
|
|
- "5432:5432"
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pg_isready -U switchboard -d switchboard_e2e"]
|
|
interval: 2s
|
|
timeout: 5s
|
|
retries: 10
|
|
|
|
switchboard-1:
|
|
build:
|
|
context: .
|
|
dockerfile: Dockerfile
|
|
environment:
|
|
PORT: "8080"
|
|
BASE_PATH: ""
|
|
DB_DRIVER: postgres
|
|
DATABASE_URL: postgres://switchboard:e2e-password@postgres:5432/switchboard_e2e?sslmode=disable
|
|
JWT_SECRET: e2e-jwt-secret
|
|
ENCRYPTION_KEY: e2e-encryption-key-32chars!!!!!
|
|
SWITCHBOARD_ADMIN_USERNAME: admin
|
|
SWITCHBOARD_ADMIN_PASSWORD: admin
|
|
STORAGE_BACKEND: pvc
|
|
STORAGE_PATH: /data/storage
|
|
CORS_ALLOWED_ORIGINS: "*"
|
|
EXT_ALLOW_PRIVATE_IPS: "true"
|
|
LOG_FORMAT: text
|
|
LOG_LEVEL: info
|
|
SEED_USERS: "alice:password123:user,bob:password456:user,charlie:password789:user"
|
|
CLUSTER_NODE_ID: "node-1"
|
|
CLUSTER_HEARTBEAT_INTERVAL: "5s"
|
|
CLUSTER_STALE_THRESHOLD: "15s"
|
|
CLUSTER_ENDPOINT: "http://switchboard-1:8080"
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
ports:
|
|
- "8081:80"
|
|
|
|
switchboard-2:
|
|
build:
|
|
context: .
|
|
dockerfile: Dockerfile
|
|
environment:
|
|
PORT: "8080"
|
|
BASE_PATH: ""
|
|
DB_DRIVER: postgres
|
|
DATABASE_URL: postgres://switchboard:e2e-password@postgres:5432/switchboard_e2e?sslmode=disable
|
|
JWT_SECRET: e2e-jwt-secret
|
|
ENCRYPTION_KEY: e2e-encryption-key-32chars!!!!!
|
|
SWITCHBOARD_ADMIN_USERNAME: admin
|
|
SWITCHBOARD_ADMIN_PASSWORD: admin
|
|
STORAGE_BACKEND: pvc
|
|
STORAGE_PATH: /data/storage
|
|
CORS_ALLOWED_ORIGINS: "*"
|
|
EXT_ALLOW_PRIVATE_IPS: "true"
|
|
LOG_FORMAT: text
|
|
LOG_LEVEL: info
|
|
SEED_USERS: "alice:password123:user,bob:password456:user,charlie:password789:user"
|
|
CLUSTER_NODE_ID: "node-2"
|
|
CLUSTER_HEARTBEAT_INTERVAL: "5s"
|
|
CLUSTER_STALE_THRESHOLD: "15s"
|
|
CLUSTER_ENDPOINT: "http://switchboard-2:8080"
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
ports:
|
|
- "8082:80"
|
|
|
|
switchboard-3:
|
|
build:
|
|
context: .
|
|
dockerfile: Dockerfile
|
|
environment:
|
|
PORT: "8080"
|
|
BASE_PATH: ""
|
|
DB_DRIVER: postgres
|
|
DATABASE_URL: postgres://switchboard:e2e-password@postgres:5432/switchboard_e2e?sslmode=disable
|
|
JWT_SECRET: e2e-jwt-secret
|
|
ENCRYPTION_KEY: e2e-encryption-key-32chars!!!!!
|
|
SWITCHBOARD_ADMIN_USERNAME: admin
|
|
SWITCHBOARD_ADMIN_PASSWORD: admin
|
|
STORAGE_BACKEND: pvc
|
|
STORAGE_PATH: /data/storage
|
|
CORS_ALLOWED_ORIGINS: "*"
|
|
EXT_ALLOW_PRIVATE_IPS: "true"
|
|
LOG_FORMAT: text
|
|
LOG_LEVEL: info
|
|
SEED_USERS: "alice:password123:user,bob:password456:user,charlie:password789:user"
|
|
CLUSTER_NODE_ID: "node-3"
|
|
CLUSTER_HEARTBEAT_INTERVAL: "5s"
|
|
CLUSTER_STALE_THRESHOLD: "15s"
|
|
CLUSTER_ENDPOINT: "http://switchboard-3:8080"
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
ports:
|
|
- "8083:80"
|
|
|
|
lb:
|
|
image: nginx:alpine
|
|
volumes:
|
|
- ./ci/e2e-nginx.conf:/etc/nginx/nginx.conf:ro
|
|
ports:
|
|
- "3000:80"
|
|
depends_on:
|
|
- switchboard-1
|
|
- switchboard-2
|
|
- switchboard-3
|