checkpoint: complete recoverable V2 pre-separation baseline
Complete the selective V2 checkpoint with its minimal AgentBus, object-storage, replay persistence, and validated-workbench shared dependency closure.
This commit is contained in:
1 parent
7330ac853b
commit
694c4317a3
627 files changed
+130467
-885
No files matched your search
@@ -1,116 +1,52 @@
|
||||
#!/usr/bin/env bash
|
||||
# Validate the contracts-v1 PostgreSQL migration without touching any schema
|
||||
# other than th_hotel_booking. This script is safe by default: it refuses a
|
||||
# schema containing objects outside the booking_contract_* namespace.
|
||||
# Run the opt-in CP4 PostgreSQL infrastructure smoke through the same Flyway
|
||||
# and Spring repository code used by the application. The JUnit gate validates
|
||||
# the approved legacy V001 coexistence shape before any write, migrates V1-V14,
|
||||
# restarts independent application contexts, verifies durable claim/fencing plus
|
||||
# the controlled-Provider Layer 3 path, and removes all synthetic source-rooted rows.
|
||||
set -euo pipefail
|
||||
|
||||
readonly BOOKING_SCHEMA='th_hotel_booking'
|
||||
readonly MIGRATION_FILES=(
|
||||
'server/src/main/resources/db/booking-postgresql/migration/V1__booking_contracts_v1.sql'
|
||||
'server/src/main/resources/db/booking-postgresql/migration/V2__booking_sender_mapping_direction.sql'
|
||||
required_variables=(
|
||||
TH_HOTEL_BOOKING_PG_URL
|
||||
TH_HOTEL_BOOKING_PG_USERNAME
|
||||
TH_HOTEL_BOOKING_PG_PASSWORD
|
||||
TH_HOTEL_BOOKING_PG_SMOKE_DATABASE
|
||||
)
|
||||
for variable_name in "${required_variables[@]}"; do
|
||||
if [[ -z "${!variable_name:-}" ]]; then
|
||||
echo "${variable_name} is required through the deployment Secret environment." >&2
|
||||
exit 64
|
||||
fi
|
||||
done
|
||||
|
||||
database_url="${BOOKING_PG_VERIFY_URL:-${TH_HOTEL_BOOKING_PG_URL:-}}"
|
||||
if [[ -z "${database_url}" ]]; then
|
||||
echo 'BOOKING_PG_VERIFY_URL (or TH_HOTEL_BOOKING_PG_URL) is required.' >&2
|
||||
exit 64
|
||||
if [[ "${TH_HOTEL_BOOKING_PG_SMOKE_APPROVED:-false}" != 'true' ]]; then
|
||||
echo 'Set TH_HOTEL_BOOKING_PG_SMOKE_APPROVED=true only for an explicitly approved non-production database.' >&2
|
||||
exit 65
|
||||
fi
|
||||
if [[ "${BOOKING_PG_VERIFY_SCHEMA:-${BOOKING_SCHEMA}}" != "${BOOKING_SCHEMA}" ]]; then
|
||||
echo "Only ${BOOKING_SCHEMA} may be verified by this script." >&2
|
||||
exit 64
|
||||
fi
|
||||
for migration_file in "${MIGRATION_FILES[@]}"; do
|
||||
if [[ ! -f "${migration_file}" ]]; then
|
||||
echo "Canonical booking migration was not found: ${migration_file}" >&2
|
||||
|
||||
case "${TH_HOTEL_BOOKING_PG_URL}" in
|
||||
jdbc:postgresql://*) ;;
|
||||
postgresql://*)
|
||||
export TH_HOTEL_BOOKING_PG_URL="jdbc:${TH_HOTEL_BOOKING_PG_URL}"
|
||||
;;
|
||||
postgres://*)
|
||||
export TH_HOTEL_BOOKING_PG_URL="jdbc:postgresql://${TH_HOTEL_BOOKING_PG_URL#postgres://}"
|
||||
;;
|
||||
*)
|
||||
echo 'TH_HOTEL_BOOKING_PG_URL must be a PostgreSQL JDBC or PostgreSQL connection URL.' >&2
|
||||
exit 64
|
||||
;;
|
||||
esac
|
||||
|
||||
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
repository_root="$(cd "${script_dir}/.." && pwd)"
|
||||
for migration_version in 1 2 3 4 5 6 7 8 9 10 11 12 13 14; do
|
||||
if ! compgen -G "${repository_root}/server/src/main/resources/db/booking-postgresql/migration/V${migration_version}__*.sql" >/dev/null; then
|
||||
echo "Canonical Booking PostgreSQL V${migration_version} migration is missing." >&2
|
||||
exit 66
|
||||
fi
|
||||
done
|
||||
if ! command -v psql >/dev/null 2>&1; then
|
||||
echo 'psql is required to run PostgreSQL migration verification.' >&2
|
||||
exit 69
|
||||
fi
|
||||
|
||||
psql_query() {
|
||||
psql -X -qAt -v ON_ERROR_STOP=1 -d "${database_url}" -c "$1"
|
||||
}
|
||||
|
||||
schema_exists="$(psql_query "SELECT EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = '${BOOKING_SCHEMA}');")"
|
||||
if [[ "${schema_exists}" == 't' ]]; then
|
||||
unexpected_objects="$(psql_query "
|
||||
SELECT count(*)
|
||||
FROM pg_class relation
|
||||
JOIN pg_namespace namespace ON namespace.oid = relation.relnamespace
|
||||
WHERE namespace.nspname = '${BOOKING_SCHEMA}'
|
||||
AND relation.relkind IN ('r', 'p', 'S', 'v', 'm')
|
||||
AND relation.relname NOT LIKE 'booking_contract_%'
|
||||
AND relation.relname <> 'flyway_schema_history';")"
|
||||
if [[ "${unexpected_objects}" != '0' ]]; then
|
||||
echo "Refusing to use ${BOOKING_SCHEMA}: it contains ${unexpected_objects} non-contract object(s)." >&2
|
||||
exit 65
|
||||
fi
|
||||
fi
|
||||
|
||||
# Count user objects outside the project schema. The migration is allowed to
|
||||
# create only th_hotel_booking objects; a count change is a hard failure.
|
||||
other_objects_before="$(psql_query "
|
||||
SELECT count(*)
|
||||
FROM pg_class relation
|
||||
JOIN pg_namespace namespace ON namespace.oid = relation.relnamespace
|
||||
WHERE namespace.nspname NOT IN ('pg_catalog', 'information_schema', '${BOOKING_SCHEMA}')
|
||||
AND namespace.nspname NOT LIKE 'pg_toast%'
|
||||
AND relation.relkind IN ('r', 'p', 'S', 'v', 'm');")"
|
||||
|
||||
psql -X -q --single-transaction -v ON_ERROR_STOP=1 -d "${database_url}" \
|
||||
-f "${MIGRATION_FILES[0]}" \
|
||||
-f "${MIGRATION_FILES[1]}"
|
||||
|
||||
other_objects_after="$(psql_query "
|
||||
SELECT count(*)
|
||||
FROM pg_class relation
|
||||
JOIN pg_namespace namespace ON namespace.oid = relation.relnamespace
|
||||
WHERE namespace.nspname NOT IN ('pg_catalog', 'information_schema', '${BOOKING_SCHEMA}')
|
||||
AND namespace.nspname NOT LIKE 'pg_toast%'
|
||||
AND relation.relkind IN ('r', 'p', 'S', 'v', 'm');")"
|
||||
if [[ "${other_objects_before}" != "${other_objects_after}" ]]; then
|
||||
echo 'PostgreSQL migration verification detected a non-booking schema object change.' >&2
|
||||
exit 70
|
||||
fi
|
||||
|
||||
required_tables=(
|
||||
booking_contract_source_message
|
||||
booking_contract_source_attachment
|
||||
booking_contract_processing_run
|
||||
booking_contract_processing_attempt
|
||||
booking_contract_evidence_reference
|
||||
booking_contract_artifact
|
||||
booking_contract_parsed_fact
|
||||
booking_contract_candidate_target
|
||||
booking_contract_confirmation_action
|
||||
booking_contract_channel_profile
|
||||
booking_contract_sender_mapping
|
||||
booking_contract_rate_code_mapping
|
||||
)
|
||||
for table_name in "${required_tables[@]}"; do
|
||||
present="$(psql_query "SELECT to_regclass('${BOOKING_SCHEMA}.${table_name}') IS NOT NULL;")"
|
||||
if [[ "${present}" != 't' ]]; then
|
||||
echo "Missing required booking table: ${BOOKING_SCHEMA}.${table_name}" >&2
|
||||
exit 71
|
||||
fi
|
||||
done
|
||||
|
||||
cross_schema_fk_count="$(psql_query "
|
||||
SELECT count(*)
|
||||
FROM pg_constraint constraint
|
||||
JOIN pg_class source_relation ON source_relation.oid = constraint.conrelid
|
||||
JOIN pg_namespace source_namespace ON source_namespace.oid = source_relation.relnamespace
|
||||
JOIN pg_class target_relation ON target_relation.oid = constraint.confrelid
|
||||
JOIN pg_namespace target_namespace ON target_namespace.oid = target_relation.relnamespace
|
||||
WHERE constraint.contype = 'f'
|
||||
AND (source_namespace.nspname = '${BOOKING_SCHEMA}' OR target_namespace.nspname = '${BOOKING_SCHEMA}')
|
||||
AND source_namespace.nspname <> target_namespace.nspname;")"
|
||||
if [[ "${cross_schema_fk_count}" != '0' ]]; then
|
||||
echo 'Booking PostgreSQL migration created or found a cross-schema foreign key.' >&2
|
||||
exit 72
|
||||
fi
|
||||
|
||||
echo "Booking PostgreSQL migration verified in ${BOOKING_SCHEMA}; other schema object count unchanged (${other_objects_after})."
|
||||
export TH_HOTEL_BOOKING_PG_SMOKE_ENABLED=true
|
||||
cd "${repository_root}/server"
|
||||
exec ./mvnw -Dtest=BookingPostgresCp4InfrastructureSmokeTest test
|
||||
Reference in new issue
Block a user