#!/usr/bin/env bash set -euo pipefail repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" cd "${repo_root}" project="minerops-agent-enrollment-migration-$$" compose=( docker compose --project-name "${project}" --env-file .env.dev -f compose.dev.yaml ) cleanup() { "${compose[@]}" down -v --remove-orphans >/dev/null 2>&1 || true } trap cleanup EXIT set -a # shellcheck disable=SC1091 source .env.dev set +a echo "Starting disposable PostgreSQL..." "${compose[@]}" up -d postgres echo "Waiting for PostgreSQL..." for attempt in $(seq 1 60); do if "${compose[@]}" exec -T postgres \ pg_isready -U "${POSTGRES_USER}" -d "${POSTGRES_DB}" >/dev/null 2>&1; then break fi if [[ "${attempt}" -eq 60 ]]; then echo "PostgreSQL did not become ready" >&2 exit 1 fi sleep 1 done echo "Building backend with migration 0017..." "${compose[@]}" build backend >/dev/null run_backend() { "${compose[@]}" run --rm --no-deps backend "$@" } psql_exec() { "${compose[@]}" exec -T postgres \ psql -v ON_ERROR_STOP=1 \ -U "${POSTGRES_USER}" \ -d "${POSTGRES_DB}" "$@" } echo "Testing upgrade through 0016..." run_backend alembic upgrade 0016_agent_zones revision="$( psql_exec -Atc "SELECT version_num FROM alembic_version" )" [[ "${revision}" == "0016_agent_zones" ]] echo "Testing 0016 -> 0017..." run_backend alembic upgrade 0017_agent_enrollment revision="$( psql_exec -Atc "SELECT version_num FROM alembic_version" )" [[ "${revision}" == "0017_agent_enrollment" ]] echo "Checking new Agent credential columns..." for column in \ display_name \ agent_secret_hash \ credential_created_at \ credential_revoked_at do count="$( psql_exec -Atc " SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'minerops_site_agents' AND column_name = '${column}' " )" [[ "${count}" == "1" ]] done echo "Checking enrollment table..." count="$( psql_exec -Atc " SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'agent_enrollments' " )" [[ "${count}" == "1" ]] echo "Checking expected indexes..." for index in \ uq_minerops_site_agents_agent_secret_hash \ uq_agent_enrollments_token_hash \ uq_agent_enrollments_open_zone \ uq_agent_enrollments_enrolled_agent \ idx_agent_enrollments_site_id \ idx_agent_enrollments_created_at do count="$( psql_exec -Atc " SELECT COUNT(*) FROM pg_indexes WHERE schemaname = 'public' AND indexname = '${index}' " )" [[ "${count}" == "1" ]] done echo "Testing immediate legal downgrade..." run_backend alembic downgrade 0016_agent_zones revision="$( psql_exec -Atc "SELECT version_num FROM alembic_version" )" [[ "${revision}" == "0016_agent_zones" ]] count="$( psql_exec -Atc " SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'agent_enrollments' " )" [[ "${count}" == "0" ]] echo "Re-upgrading to 0017..." run_backend alembic upgrade 0017_agent_enrollment echo "Creating isolated Site and Agent Zone fixtures..." psql_exec <<'SQL' INSERT INTO sites (id, name) VALUES ( '00000000-0000-4000-8000-000000001700', 'Enrollment Migration Test Site' ); INSERT INTO agent_zones ( id, site_id, name, description, is_enabled ) VALUES ( '00000000-0000-4000-8000-000000001701', '00000000-0000-4000-8000-000000001700', 'Enrollment Test Zone', 'Disposable migration test', true ); SQL echo "Testing database constraints..." psql_exec <<'SQL' DO $$ BEGIN BEGIN INSERT INTO agent_enrollments ( token_hash, site_id, agent_zone_id, display_name, expires_at ) VALUES ( 'not-a-sha256-hash', '00000000-0000-4000-8000-000000001700', '00000000-0000-4000-8000-000000001701', 'Invalid Hash', now() + interval '30 minutes' ); RAISE EXCEPTION 'invalid token hash was accepted'; EXCEPTION WHEN check_violation THEN NULL; END; END $$; SQL psql_exec <<'SQL' INSERT INTO agent_enrollments ( id, token_hash, site_id, agent_zone_id, display_name, expires_at ) VALUES ( '00000000-0000-4000-8000-000000001710', repeat('a', 64), '00000000-0000-4000-8000-000000001700', '00000000-0000-4000-8000-000000001701', 'Enrollment Test Agent', now() + interval '30 minutes' ); DO $$ BEGIN BEGIN INSERT INTO agent_enrollments ( token_hash, site_id, agent_zone_id, display_name, expires_at ) VALUES ( repeat('b', 64), '00000000-0000-4000-8000-000000001700', '00000000-0000-4000-8000-000000001701', 'Second Open Enrollment', now() + interval '30 minutes' ); RAISE EXCEPTION 'second open enrollment was accepted'; EXCEPTION WHEN unique_violation THEN NULL; END; END $$; SQL echo "Testing credential hash integrity and uniqueness..." psql_exec <<'SQL' INSERT INTO minerops_site_agents ( agent_id, site_id, agent_zone_id, site_name, display_name, agent_secret_hash, credential_created_at ) VALUES ( 'agent-enrollment-test', '00000000-0000-4000-8000-000000001700', '00000000-0000-4000-8000-000000001701', 'Enrollment Migration Test Site', 'Enrollment Test Agent', repeat('c', 64), now() ); DO $$ BEGIN BEGIN INSERT INTO minerops_site_agents ( agent_id, site_name, display_name, agent_secret_hash, credential_created_at ) VALUES ( 'agent-enrollment-duplicate-secret', 'Enrollment Migration Test Site', 'Duplicate Secret', repeat('c', 64), now() ); RAISE EXCEPTION 'duplicate Agent secret hash was accepted'; EXCEPTION WHEN unique_violation THEN NULL; END; END $$; SQL echo "Testing downgrade refusal after feature use..." set +e downgrade_output="$( run_backend alembic downgrade 0016_agent_zones 2>&1 )" downgrade_status=$? set -e if [[ "${downgrade_status}" -eq 0 ]]; then echo "Downgrade unexpectedly succeeded after enrollment/credential use" >&2 exit 1 fi grep -Eq \ 'Cannot downgrade 0017: Agent enrollment history exists|Cannot downgrade 0017: per-Agent credentials exist' \ <<<"${downgrade_output}" revision="$( psql_exec -Atc "SELECT version_num FROM alembic_version" )" [[ "${revision}" == "0017_agent_enrollment" ]] echo "Confirming refusal happened before destructive schema changes..." count="$( psql_exec -Atc " SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'agent_enrollments' " )" [[ "${count}" == "1" ]] count="$( psql_exec -Atc " SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'minerops_site_agents' AND column_name = 'agent_secret_hash' " )" [[ "${count}" == "1" ]] echo "Agent enrollment migration lifecycle checks passed."