dbpulse 0.9.1

command line tool to monitor that database is available for read & write
Documentation
---
name: Test

on:
  workflow_call:

env:
  CARGO_TERM_COLOR: always
  RUST_BACKTRACE: 1

jobs:
  format:
    name: Format
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: llvm-tools-preview

      - name: Format
        run: cargo fmt --all -- --check

  lint:
    name: Clippy
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: dtolnay/rust-toolchain@stable

      - name: Clippy
        run: cargo clippy --all-targets --all-features

  check:
    name: Check
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: dtolnay/rust-toolchain@stable

      - name: Check
        run: cargo check

  test:
    name: Test
    strategy:
      matrix:
        os:
          - ubuntu-latest
          - macOS-latest
        rust:
          - stable
    runs-on: ${{ matrix.os }}
    needs:
      - format
      - lint
      - check
    steps:
      - uses: actions/checkout@v6
      - uses: dtolnay/rust-toolchain@stable

      - name: test
        run: cargo test

  integration-test:
    name: Integration Tests
    runs-on: ubuntu-latest
    needs:
      - format
      - lint
      - check

    services:
      postgres:
        image: postgres:18
        env:
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: secret
          POSTGRES_DB: testdb
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

      mariadb:
        image: mariadb:12
        env:
          MARIADB_USER: dbpulse
          MARIADB_PASSWORD: secret
          MARIADB_ROOT_PASSWORD: secret
          MARIADB_DATABASE: testdb
        ports:
          - 3306:3306
        options: >-
          --health-cmd "mariadb-admin ping -h localhost -u root -psecret"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:
      - uses: actions/checkout@v6
      - uses: dtolnay/rust-toolchain@stable

      - name: Install database clients
        run: |
          sudo apt-get update
          sudo apt-get install -y postgresql-client mariadb-client

      - name: Wait for databases
        run: |
          echo "Waiting for PostgreSQL..."
          for i in {1..30}; do
            if pg_isready -h localhost -p 5432 -U postgres; then
              echo "PostgreSQL is ready!"
              break
            fi
            echo "PostgreSQL not ready, waiting... ($i/30)"
            sleep 2
          done

          echo "Waiting for MariaDB..."
          for i in {1..30}; do
            if mariadb -h 127.0.0.1 -u dbpulse -psecret -D testdb -e "SELECT 1" &>/dev/null; then
              echo "MariaDB is ready!"
              break
            fi
            echo "MariaDB not ready, waiting... ($i/30)"
            sleep 2
          done

      - name: Run PostgreSQL integration tests
        run: cargo test --test postgres_test -- --ignored --nocapture
        env:
          RUST_BACKTRACE: 1

      - name: Run MariaDB integration tests
        run: cargo test --test mariadb_test -- --ignored --nocapture
        env:
          RUST_BACKTRACE: 1

  failover-transition-test:
    name: Failover Transition Tests
    runs-on: ubuntu-latest
    needs:
      - format
      - lint
      - check

    steps:
      - uses: actions/checkout@v6
      - uses: dtolnay/rust-toolchain@stable

      - name: Install database clients
        run: |
          sudo apt-get update
          sudo apt-get install -y postgresql-client mariadb-client

      - name: Start failover test databases
        run: |
          docker rm -f dbpulse-postgres dbpulse-mariadb 2>/dev/null || true

          docker run -d --name dbpulse-postgres \
            -e POSTGRES_USER=postgres \
            -e POSTGRES_PASSWORD=secret \
            -e POSTGRES_DB=testdb \
            -p 5432:5432 postgres:18

          docker run -d --name dbpulse-mariadb \
            -e MARIADB_USER=dbpulse \
            -e MARIADB_PASSWORD=secret \
            -e MARIADB_ROOT_PASSWORD=secret \
            -e MARIADB_DATABASE=testdb \
            -p 3306:3306 mariadb:12

      - name: Wait for failover test databases
        run: |
          echo "Waiting for PostgreSQL..."
          for i in {1..30}; do
            if docker exec dbpulse-postgres pg_isready -U postgres > /dev/null 2>&1; then
              echo "PostgreSQL is ready!"
              break
            fi
            if [ "$i" -eq 30 ]; then
              echo "PostgreSQL did not become ready in time"
              exit 1
            fi
            sleep 2
          done

          echo "Verifying PostgreSQL from host..."
          for i in {1..30}; do
            if PGPASSWORD=secret psql -h 127.0.0.1 -p 5432 -U postgres -d testdb -c "SELECT 1" > /dev/null 2>&1; then
              echo "PostgreSQL host access is ready!"
              break
            fi
            if [ "$i" -eq 30 ]; then
              echo "PostgreSQL host access did not become ready in time"
              exit 1
            fi
            sleep 1
          done

          echo "Waiting for MariaDB..."
          for i in {1..30}; do
            if docker exec dbpulse-mariadb mariadb -u dbpulse -psecret -D testdb -e "SELECT 1" > /dev/null 2>&1; then
              echo "MariaDB is ready!"
              break
            fi
            if [ "$i" -eq 30 ]; then
              echo "MariaDB did not become ready in time"
              exit 1
            fi
            sleep 2
          done

      - name: Run PostgreSQL failover transition test
        run: cargo test --test postgres_test test_postgres_pulse_transition_stop_start
          -- --ignored --nocapture --test-threads=1
        env:
          RUN_FAILOVER_TRANSITION_TESTS: '1'
          RUST_BACKTRACE: 1

      - name: Run MariaDB failover transition test
        run: cargo test --test mariadb_test test_mariadb_pulse_transition_stop_start
          -- --ignored --nocapture --test-threads=1
        env:
          RUN_FAILOVER_TRANSITION_TESTS: '1'
          RUST_BACKTRACE: 1

      - name: Show database logs on failure
        if: failure()
        run: |
          echo "==> PostgreSQL logs:"
          docker logs dbpulse-postgres || true
          echo ""
          echo "==> MariaDB logs:"
          docker logs dbpulse-mariadb || true

      - name: Cleanup failover test databases
        if: always()
        run: |
          docker stop dbpulse-postgres dbpulse-mariadb || true
          docker rm dbpulse-postgres dbpulse-mariadb || true

  integration-test-tls:
    name: Integration Tests with TLS
    runs-on: ubuntu-latest
    needs:
      - format
      - lint
      - check

    steps:
      - uses: actions/checkout@v6
      - uses: dtolnay/rust-toolchain@stable

      - name: Install dependencies
        run: |
          sudo apt-get update
          sudo apt-get install -y postgresql-client mariadb-client openssl

      - name: Generate TLS certificates
        run: |
          ./scripts/gen-certs.sh
          # Make MariaDB key readable
          chmod 644 .certs/mariadb/server.key

      - name: Prepare PostgreSQL with TLS
        run: |
          # Create custom PostgreSQL image with certificates
          cat > Dockerfile.postgres-tls <<EOF
          FROM postgres:18-alpine
          COPY .certs/postgres/server.crt /var/lib/postgresql/server.crt
          COPY .certs/postgres/server.key /var/lib/postgresql/server.key
          COPY .certs/postgres/ca.crt /var/lib/postgresql/ca.crt
          RUN chown postgres:postgres /var/lib/postgresql/server.* /var/lib/postgresql/ca.crt && \
              chmod 600 /var/lib/postgresql/server.key && \
              chmod 644 /var/lib/postgresql/server.crt /var/lib/postgresql/ca.crt
          EOF
          docker build -t postgres-tls:test -f Dockerfile.postgres-tls .

      - name: Start PostgreSQL with TLS
        run: |
          docker run -d \
            --name postgres-tls \
            -e POSTGRES_USER=postgres \
            -e POSTGRES_PASSWORD=secret \
            -e POSTGRES_DB=testdb \
            -p 5432:5432 \
            postgres-tls:test \
            -c ssl=on \
            -c ssl_cert_file=/var/lib/postgresql/server.crt \
            -c ssl_key_file=/var/lib/postgresql/server.key \
            -c ssl_ca_file=/var/lib/postgresql/ca.crt \
            -c ssl_min_protocol_version=TLSv1.2

          # Wait for PostgreSQL
          for i in {1..30}; do
            if docker exec postgres-tls pg_isready -U postgres 2>/dev/null; then
              echo "PostgreSQL is ready!"
              break
            fi
            echo "Waiting for PostgreSQL... ($i/30)"
            sleep 2
          done

      - name: Start MariaDB with TLS
        run: |
          docker run -d \
            --name mariadb-tls \
            -e MARIADB_USER=dbpulse \
            -e MARIADB_PASSWORD=secret \
            -e MARIADB_ROOT_PASSWORD=secret \
            -e MARIADB_DATABASE=testdb \
            -p 3306:3306 \
            -v $PWD/.certs/mariadb/server.crt:/etc/mysql/ssl/server.crt:ro \
            -v $PWD/.certs/mariadb/server.key:/etc/mysql/ssl/server.key:ro \
            -v $PWD/.certs/mariadb/ca.crt:/etc/mysql/ssl/ca.crt:ro \
            mariadb:12 \
            --ssl-cert=/etc/mysql/ssl/server.crt \
            --ssl-key=/etc/mysql/ssl/server.key \
            --ssl-ca=/etc/mysql/ssl/ca.crt \
            --require-secure-transport=OFF \
            --tls-version=TLSv1.2,TLSv1.3

          # Wait for MariaDB
          for i in {1..30}; do
            if docker exec mariadb-tls mariadb -u dbpulse -psecret -D testdb -e "SELECT 1" &>/dev/null; then
              echo "MariaDB is ready!"
              break
            fi
            echo "Waiting for MariaDB... ($i/30)"
            sleep 2
          done

      - name: Verify TLS is enabled (PostgreSQL)
        run: |
          docker exec postgres-tls psql -U postgres -d testdb -c "SHOW ssl;"
          PGSSLMODE=require psql "postgresql://postgres:secret@localhost:5432/testdb" \
            -c "SELECT ssl, version, cipher FROM pg_stat_ssl WHERE pid = pg_backend_pid();"

      - name: Verify TLS is enabled (MariaDB)
        run: |
          docker exec mariadb-tls mariadb -u root -psecret -e "SHOW VARIABLES LIKE 'have_ssl';"
          docker exec mariadb-tls mariadb -h 127.0.0.1 -u dbpulse -psecret -D testdb --ssl \
            -e "SHOW STATUS LIKE 'Ssl_cipher';"

      - name: Run PostgreSQL TLS tests
        run: cargo test --test postgres_tls_test -- --ignored --nocapture
        env:
          POSTGRES_CA_CERT: ${{ github.workspace }}/.certs/postgres/ca.crt
          RUST_BACKTRACE: 1

      - name: Run MariaDB TLS tests
        run: cargo test --test mariadb_tls_test -- --ignored --nocapture
        env:
          MARIADB_CA_CERT: ${{ github.workspace }}/.certs/mariadb/ca.crt
          RUST_BACKTRACE: 1

      - name: Show logs on failure
        if: failure()
        run: |
          echo "==> PostgreSQL logs:"
          docker logs postgres-tls || true
          echo ""
          echo "==> MariaDB logs:"
          docker logs mariadb-tls || true

      - name: Cleanup
        if: always()
        run: |-
          docker stop postgres-tls mariadb-tls || true
          docker rm postgres-tls mariadb-tls || true