# =============================================================================
# pgpipe Docker Compose — try-it-out stack
# =============================================================================
# Spins up two PostgreSQL 16 databases (a source and a destination) plus
# pgpipe wired between them. Open http://localhost:8080 for the dashboard.
#
# Prerequisites: Docker Desktop (or any Docker engine 20.10+) and Docker
# Compose v2 (bundled with modern Docker Desktop).
#
# Usage:
#   docker compose up -d --build       # bring everything up
#   docker compose logs -f pgpipe      # watch pgpipe
#   docker compose down -v             # tear down + delete data
#
# Try it:
#   1) Open the dashboard at http://localhost:8080 and finish the setup wizard
#      (the YAML in ./pgpipe.yaml is already wired to source-db / dest-db)
#   2) Insert a row in the source:
#        docker compose exec source-db psql -U postgres source_db \
#          -c "INSERT INTO public.greetings(message) VALUES ('hello');"
#   3) See it in the destination:
#        docker compose exec dest-db psql -U postgres dest_db \
#          -c "SELECT * FROM public.greetings;"
#
# Production note: this stack is for evaluation. To run pgpipe against your
# own databases, copy this file, delete the source-db / dest-db services, and
# point pgpipe.yaml at your real hostnames. See the comments inside.
# =============================================================================

services:

  # ---------------------------------------------------------------------------
  # Source PostgreSQL — the database we capture changes FROM.
  # Uses production-safe defaults (no synchronous_commit=off, no
  # full_page_writes=off). Tune for your workload.
  # ---------------------------------------------------------------------------
  source-db:
    image: postgres:16-alpine
    container_name: pgpipe-source
    environment:
      POSTGRES_DB: source_db
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
    command:
      - "postgres"
      - "-c"
      - "wal_level=logical"
      - "-c"
      - "max_replication_slots=4"
      - "-c"
      - "max_wal_senders=4"
      - "-c"
      - "wal_sender_timeout=0"
    ports:
      - "5433:5432"
    volumes:
      - source-data:/var/lib/postgresql/data
      - ./init-source.sql:/docker-entrypoint-initdb.d/init.sql:ro
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres -d source_db"]
      interval: 3s
      timeout: 3s
      retries: 10

  # ---------------------------------------------------------------------------
  # Destination PostgreSQL — the database we replicate TO.
  # ---------------------------------------------------------------------------
  dest-db:
    image: postgres:16-alpine
    container_name: pgpipe-dest
    environment:
      POSTGRES_DB: dest_db
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
    ports:
      - "5434:5432"
    volumes:
      - dest-data:/var/lib/postgresql/data
      - ./init-dest.sql:/docker-entrypoint-initdb.d/init.sql:ro
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres -d dest_db"]
      interval: 3s
      timeout: 3s
      retries: 10

  # ---------------------------------------------------------------------------
  # Config initializer — seeds the named volume once and repairs permissions.
  # The main pgpipe process remains non-root and never needs host-file ownership.
  # ---------------------------------------------------------------------------
  pgpipe-config-init:
    image: alpine:3.23@sha256:fd791d74b68913cbb027c6546007b3f0d3bc45125f797758156952bc2d6daf40
    user: "0:0"
    network_mode: none
    read_only: true
    cap_drop:
      - ALL
    cap_add:
      - CHOWN
      - DAC_OVERRIDE
      - FOWNER
    security_opt:
      - no-new-privileges:true
    volumes:
      - type: bind
        source: ./pgpipe.yaml
        target: /seed/pgpipe.yaml
        read_only: true
        bind:
          create_host_path: false
      - type: volume
        source: pgpipe-config
        target: /config
        volume:
          nocopy: true
    entrypoint:
      - /bin/sh
      - -ec
    command:
      - |
        umask 077
        if [ -L /config/pgpipe.yaml ] ||
           { [ -e /config/pgpipe.yaml ] && [ ! -f /config/pgpipe.yaml ]; }; then
          echo "refusing non-regular config path: /config/pgpipe.yaml" >&2
          exit 1
        fi
        if [ ! -e /config/pgpipe.yaml ]; then
          cp /seed/pgpipe.yaml /config/pgpipe.yaml
        fi
        chown 10001:10001 /config /config/pgpipe.yaml
        chmod 0750 /config
        chmod 0600 /config/pgpipe.yaml
    restart: "no"

  # ---------------------------------------------------------------------------
  # pgpipe — the CDC pipeline itself. Builds the local Dockerfile, which in
  # turn pulls the published binary from pghorizon.com.
  # ---------------------------------------------------------------------------
  pgpipe:
    build:
      context: .
      args:
        PGPIPE_VERSION: "5.1.0"
    container_name: pgpipe
    depends_on:
      pgpipe-config-init:
        condition: service_completed_successfully
      source-db:
        condition: service_healthy
      dest-db:
        condition: service_healthy
    ports:
      - "8080:8080"   # dashboard + REST API
    environment:
      GOMEMLIMIT: "2GiB"
    read_only: true
    tmpfs:
      - /tmp:rw,noexec,nosuid,size=64m
    cap_drop:
      - ALL
    security_opt:
      - no-new-privileges:true
    volumes:
      - type: volume
        source: pgpipe-config
        target: /etc/pgpipe
        volume:
          nocopy: true
      - pgpipe-state:/var/lib/pgpipe
    restart: unless-stopped

volumes:
  source-data:
  dest-data:
  pgpipe-config:
  pgpipe-state:
