Files
spinifex/scripts/images/rds-postgres/rds-init_test.sh
T
Josh Siviour d52245001e feat: Ochre vector store (tenant pgvector, embeddings, ingestion, operator teardown) (#788)
* feat: add /v1/embeddings gateway adapter

* feat: Ochre vector store pgvector backend, KV index registry, orchestration

Adds handlers_ochrevector: the VectorBackend interface + pgx/v5 impl
with per-account schema/role isolation enforced via SET LOCAL ROLE +
search_path inside every transaction, the KV index registry
(CREATING/READY/DELETING/STALE), and the create/delete/list/reconcile
orchestration tying them together with crash-safe rollback and
re-entrant recovery of stuck records.

* feat: singleton appliance-ensure state machine for ochre vector store

Adds the encrypted control-plane credential store and re-entrant
Ensure state machine for the platform pgvector appliance (D2): a
fixed-key KV claim mirroring Registry.Reserve elects exactly one
winner to launch the appliance via an injected ApplianceLauncher,
losers wait or resume a stale crashed provision, and the master
password is generated once, stored only AES-256-GCM encrypted, and
decrypted solely inside Connect to build a pgxBackend.

* feat: Ochre vector store async ingestion (chunker, job store, worker)

Adds Stage 3: a deterministic recursive chunk splitter, a per-account
ingestion job-record store mirroring the index registry, the
transactional per-document replace on the backend, and the
IngestService worker that lists a predastore source, chunks and
embeds each object, and replaces its rows idempotently.

* feat: Ochre vector store similarity query and metadata filter grammar

Adds VectorBackend.Query (cosine kNN, k default/cap, iterative HNSW
scan under a filter) and a Bedrock-KB-shaped metadata Filter AST that
compiles to parameterized SQL over jsonb, closing off arbitrary SQL by
allowlisting jsonb keys and always binding values as params. Also
fixes RunJob reporting READY on a job that ingested nothing regardless
of failure cause, and stamps SourceSpec.Metadata onto ingested rows so
the new filters have data to match against.

* feat: Ochre vector store request/response API, VectorService, and CLI

Adds the daemon-facing VectorService interface (index CRUD, ingest,
describe job, query), its server implementation over the existing
registry/backend/embedder, a NATS-forwarding client, and the
`spx admin ochre vector` operator CLI. Query resolves and embeds
against each index's own pinned model; ingest stamps the same
model/dimension onto every source spec server-side. accountID is
always taken from the caller context, never the request payload.

Daemon subscription wiring and manifest registration are a later
change; this is pure request/response plumbing with no construction
into daemon.go or awsgw.go.

* feat: wire Ochre vector store VectorService into the daemon

Constructs the platform Postgres appliance and VectorService behind a new
OchreVector config section that defaults off, so existing daemons are
unaffected. Adds a real RDS-backed ApplianceLauncher that provisions the
appliance over rds.CreateDBInstance/DescribeDBInstances, registers the six
ochre.vector.* NATS subjects mirroring the bedrock endpoint service pattern,
and degrades to a disabled (nil) service on any boot-time appliance failure
rather than blocking daemon startup.

* fix: fix Ochre vector store boot ordering and self-register subjects

startOchreVector ran before subscribeAll, so its appliance launch fired
rds.CreateDBInstance/DescribeDBInstances requests with no responder yet
registered, timing out the launch on a first-enable boot. It also ran
synchronously, blocking daemon startup up to the appliance launch timeout.

Move it to a background goroutine started after subscribeAll returns, and
have it register its own ochre.vector.* subjects once the VectorService is
actually constructed, via a new registerNatsSubs helper shared with
subscribeAll's boot-time table. Guard d.natsSubscriptions with d.mu, since
this background registration can now race the shutdown path's
unsubscribe-all sweep.

* feat: bake pgvector into the shared rds-postgres image

* fix: reach the ochre appliance over a routed daemon port on its ENI IP

The daemon dialled the RDS appliance over rec.Endpoint, a vanity spx3.net
hostname the control-plane host can neither resolve nor route to, so a healthy
appliance was never reachable and the vector store disabled itself.

Resolve the appliance's customer-ENI private IP read-only via
ec2.DescribeNetworkInterfaces filtered on the spinifex:rds-db-instance tag, dial
that IP for the DSN instead of the hostname, and give the daemon its own routed
ENI in the appliance's subnet bound to an OVS host port so the IP is reachable.
Retry appliance startup with bounded backoff instead of hard-disabling on the
first transient.

* fix: install pgvector at bootstrap for the Ochre vector appliance

The daemon connects to the appliance as its NOSUPERUSER master, and the
image's pgvector is not a trusted extension, so CREATE EXTENSION vector
failed with permission denied. rds-init now installs it via the bootstrap
superuser, gated on the appliance's fixed master name so tenant instances
are unaffected.

* fix: grant the Ochre appliance master CREATE on its database

The daemon's EnsureAccount runs CREATE SCHEMA per account as the NOSUPERUSER
master, which does not own the postgres database, so bootstrap now grants it
CREATE on the database alongside the pgvector install. Both are gated to the
appliance master, so tenant instances are unaffected.

* fix: make the Ochre appliance's pgvector reachable under per-account isolation

The daemon connects as a NOSUPERUSER master and runs every account operation
under SET ROLE with a search_path pinned to the account schema. Two gaps blocked
that against a real non-superuser Postgres: the master could not SET ROLE into
the roles it creates, and the account role could not see the vector type.

Bootstrap now grants the appliance master createrole_self_grant and installs
pgvector into a read-only extensions schema granted to PUBLIC; the backend
carries that schema on each account search_path so the vector type resolves
without weakening per-account write isolation. All appliance-scoped.

* fix: prune stale VPC host port before installing the daemon appliance port

A replaced Ochre appliance is handed the same subnet IP, so a new daemon ENI
mints a fresh vhp- port at an address a superseded port still holds. The old
port survives at the OS/OVS layer even after its ENI record is wiped, so two
interfaces claim one address and the kernel can route to the dead one
(EHOSTUNREACH). installVPCHostPort now prunes any other vhp- port holding the
target address before installing.

* fix: gate VM restore's stale-socket kill on backing-store readiness

On daemon start, a live VM whose NBD sockets fail to dial was force-killed as an
orphan. While predastore/viperblock is still coming up after a restart those
sockets are transiently unreachable, so a healthy VM (the Ochre appliance was
the observed victim) was destroyed and its RDS record flipped to failed. The
kill now only fires when the backing store is confirmed up; otherwise the live
QEMU is kept and QMP reconnected. Genuine orphan reaping under a healthy store
is unchanged.

* fix: drive ochre ingest jobs from a daemon-side sweep scheduler

* feat: add operator teardown verb for the ochre platform appliance

Adds Appliance.Teardown to remove the singleton platform Postgres
appliance (RDS instance, host port, KV record), an admin-only NATS
subject and daemon handler to drive it, and `spx admin ochre appliance
teardown --confirm` to invoke it without exposing the action through
the tenant awsgw surface.

* fix: register ochre appliance teardown before Connect so it survives a broken appliance

* fix: purge ochre index and job metadata on appliance teardown

* test: mark ochre in-package test files for the external-test gate
2026-08-13 15:54:48 +10:00

977 lines
43 KiB
Bash

#!/bin/sh
# Self-contained POSIX test for rds-init: the initialize path (initdb, master
# password, initial database), the idempotent attach path, the data-volume
# guard, TLS install and the parameter include. No PostgreSQL, no root: initdb,
# pg_ctl and psql are stubbed on PATH alongside install/chown/su (the script
# drops privileges and sets file ownership), and every path it touches is
# redirected into a temp dir via its env knobs.
#
# Run: sh scripts/images/rds-postgres/rds-init_test.sh
set -eu
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
SCRIPT="${SCRIPT_DIR}/rds-init"
WORK=$(mktemp -d)
trap 'rm -rf "${WORK}"' EXIT
REAL_LS=$(command -v ls)
REAL_CAT=$(command -v cat)
export REAL_LS REAL_CAT
STUBBIN="${WORK}/bin"
mkdir -p "${STUBBIN}"
# install stub: honours -d and -m, drops -o/-g (the harness is not root).
cat > "${STUBBIN}/install" <<'EOF'
#!/bin/sh
mode=""; dirmode=0; args=""
while [ $# -gt 0 ]; do
case "$1" in
-d) dirmode=1; shift ;;
-m) mode="$2"; shift 2 ;;
-o|-g) shift 2 ;;
*) args="${args} $1"; shift ;;
esac
done
# shellcheck disable=SC2086
set -- ${args}
if [ "${dirmode}" = 1 ]; then
mkdir -p "$@" || exit 1
[ -n "${mode}" ] && chmod "${mode}" "$@"
exit 0
fi
dest=""
for a in "$@"; do dest="${a}"; done
src=""
for a in "$@"; do [ "${a}" = "${dest}" ] || src="${a}"; done
cp "${src}" "${dest}"
[ -n "${mode}" ] && chmod "${mode}" "${dest}"
exit 0
EOF
# chown stub: file ownership is a no-op outside the guest.
cat > "${STUBBIN}/chown" <<'EOF'
#!/bin/sh
exit 0
EOF
# ls stub: delegates normally, but lets the datadir safety test prove that a
# failed enumeration is not mistaken for an empty directory.
cat > "${STUBBIN}/ls" <<'EOF'
#!/bin/sh
[ "${LS_FAIL:-0}" = "1" ] && exit 74
exec "${REAL_LS}" "$@"
EOF
# su stub: `su postgres -c "cmd"` runs cmd in the harness user's shell.
cat > "${STUBBIN}/su" <<'EOF'
#!/bin/sh
while [ $# -gt 0 ]; do
case "$1" in
-c) exec sh -c "$2" ;;
*) shift ;;
esac
done
exit 0
EOF
# initdb stub: records the call and materialises the files a real initdb leaves
# behind (PG_VERSION is what rds-init keys "already initialised" off).
cat > "${STUBBIN}/initdb" <<'EOF'
#!/bin/sh
echo "initdb $*" >> "${INITDB_CALLS}"
[ "${INITDB_FAIL:-0}" = "1" ] && exit 1
for a in "$@"; do
case "$a" in --pgdata=*) pgdata="${a#--pgdata=}" ;; esac
done
mkdir -p "${pgdata}"
echo 18 > "${pgdata}/PG_VERSION"
echo "# stock postgresql.conf" > "${pgdata}/postgresql.conf"
if [ "${PG_HBA_AS_DIR:-0}" = "1" ]; then
mkdir "${pgdata}/pg_hba.conf"
else
echo "local all all peer" > "${pgdata}/pg_hba.conf"
fi
exit 0
EOF
# pg_ctl stub: records start/stop of the bootstrap server. PGCTL_START_FAIL
# simulates a cluster that will not come up, e.g. a parameter the guest cannot
# allocate; PGCTL_STOP_FAIL a postmaster that will not let go of the datadir.
# PGCTL_KILL_PARENT signals rds-init from the start call, which is the window
# between initdb and the master role.
cat > "${STUBBIN}/pg_ctl" <<'EOF'
#!/bin/sh
echo "pg_ctl $*" >> "${PGCTL_CALLS}"
for a in "$@"; do
[ "$a" = "start" ] && [ "${PGCTL_START_FAIL:-0}" = "1" ] && exit 1
[ "$a" = "stop" ] && [ "${PGCTL_STOP_FAIL:-0}" = "1" ] && exit 1
if [ "$a" = "start" ] && [ "${PGCTL_KILL_PARENT:-0}" = "1" ]; then
kill -TERM "$(cat "${KILL_PID_FILE}")"
exit 0
fi
done
exit 0
EOF
# psql stub: records the SQL it was fed plus the credentials it was handed
# through the environment (never through argv).
cat > "${STUBBIN}/psql" <<'EOF'
#!/bin/sh
{
echo "--- psql $* [master=${RDS_MASTER_USERNAME:-} password=${RDS_MASTER_PASSWORD:-} db=${RDS_DB_NAME:-} grp=${RDS_GROUP_ROLE:-}]"
cat
} >> "${PSQL_CALLS}"
# ON_ERROR_STOP makes a real psql exit non-zero on any SQL failure; the stub
# reproduces that, and with it the echo of the failing statement — password and
# all — that the redaction filter exists to catch.
if [ "${PSQL_FAIL:-0}" = "1" ]; then
echo "psql:<stdin>:7: ERROR: syntax error at or near \"${RDS_MASTER_PASSWORD:-}\"" >&2
echo "psql:<stdin>:7: STATEMENT: ALTER ROLE \"m\" WITH LOGIN PASSWORD '${RDS_MASTER_PASSWORD:-}';" >&2
exit 3
fi
# PSQL_STATUS_CORRUPT=absent: psql succeeds, but rds-init cannot create the
# status file it recovers psql's exit code through — the shape a read-only or
# full /run gives it. -h is the bootstrap directory the status file lives in.
if [ "${PSQL_STATUS_CORRUPT:-}" = "absent" ]; then
while [ $# -gt 0 ]; do
[ "$1" = "-h" ] && mkdir -p "$2/psql.status" && break
shift
done
fi
exit 0
EOF
# cat stub: delegates normally, but lets the read-back of psql's exit status
# return what a torn write or an I/O error would. rds-init's other use of cat
# takes no argument (it reads the bootstrap script from stdin) and is untouched.
cat > "${STUBBIN}/cat" <<'EOF'
#!/bin/sh
case "${1:-}" in
*psql.status)
case "${PSQL_STATUS_CORRUPT:-}" in
empty) exit 0 ;;
garbage) echo "not-a-number"; exit 0 ;;
esac
;;
esac
exec "${REAL_CAT}" "$@"
EOF
# sync stub: a no-op, except that SYNC_KILL_PARENT signals rds-init from the
# directory sync that follows the receipt rename. That is the one window where
# an installed receipt and an armed sweep coexist. Matched on the receipt
# directory by name: the engine stamp syncs its directory too, long before the
# traps are armed, and killing there would leave the window untested.
cat > "${STUBBIN}/sync" <<'EOF'
#!/bin/sh
if [ "${SYNC_KILL_PARENT:-0}" = "1" ]; then
case "${1:-}" in
*/bootstrap) kill -TERM "$(cat "${KILL_PID_FILE}")" ;;
esac
fi
exit 0
EOF
chmod +x "${STUBBIN}"/*
PATH="${STUBBIN}:${PATH}"
export PATH
FAILS=0
fail() { echo "FAIL: $*"; FAILS=$((FAILS + 1)); }
pass() { echo "ok: $*"; }
DATA_MOUNT="${WORK}/data"
PGDATA="${DATA_MOUNT}/18/data"
SENTINEL="${PGDATA}/rds-bootstrap-incomplete"
RECEIPT_DIR="${DATA_MOUNT}/.spinifex-rds/bootstrap"
RECEIPT="${RECEIPT_DIR}/receipt.env"
STAMP="${DATA_MOUNT}/.spinifex-rds/engine"
HANDOFF="${WORK}/run/spinifex-rds"
MOUNTS="${WORK}/mounts"
KILL_PID_FILE="${WORK}/rds-init.pid"
export KILL_PID_FILE
INITDB_CALLS="${WORK}/initdb.calls"
PGCTL_CALLS="${WORK}/pg_ctl.calls"
PSQL_CALLS="${WORK}/psql.calls"
export INITDB_CALLS PGCTL_CALLS PSQL_CALLS
# write_handoff <mode> <password> <dbname>: lay down the bootstrap.env rds-agent
# would have written. An empty password omits the field, as `attach` does.
# MASTER_USER overrides the master role name, for the reserved-name cases.
# PENDING/PAYLOAD_ID carry the control plane's "this payload is still staged"
# assertion, which an agent predating the receipt protocol omits entirely.
write_handoff() {
mkdir -p "${HANDOFF}"
{
echo "RDS_MODE=$1"
echo "RDS_DB_INSTANCE_IDENTIFIER=${DB_ID:-db1}"
echo "RDS_MASTER_USERNAME=${MASTER_USER:-mulgamaster}"
# Single-quoted as rds-agent's shellQuote writes it, which is what lets a
# password carrying shell metacharacters — or a newline — survive the
# handoff intact and reach the checks that exist for it.
[ -n "$2" ] && echo "RDS_MASTER_PASSWORD='$2'"
# Quoted as rds-agent's shellQuote writes it, so a name carrying a space
# or a shell metacharacter survives the handoff and reaches the check
# that exists for it rather than being mangled on the way.
[ -n "$3" ] && echo "RDS_DB_NAME='$3'"
[ -n "${PENDING:-}" ] && echo "RDS_BOOTSTRAP_PENDING=${PENDING}"
[ -n "${PAYLOAD_ID:-}" ] && echo "RDS_PAYLOAD_ID=${PAYLOAD_ID}"
# Last, and unconditional: an optional line failing its test would make
# the whole group's status non-zero and `set -e` would end the run here.
echo "RDS_PORT=6543"
echo "RDS_VM_GENERATION=1"
} > "${HANDOFF}/bootstrap.env"
}
write_tls() {
mkdir -p "${HANDOFF}"
echo "CERT" > "${HANDOFF}/server.crt"
echo "KEY" > "${HANDOFF}/server.key"
}
# write_parameters [setting]: the resolved parameter group rds-agent hands over.
write_parameters() {
mkdir -p "${HANDOFF}"
echo "${1:-shared_buffers = 128MB}" > "${HANDOFF}/parameters.conf"
}
# reset_state clears everything the previous case left behind: a fresh datadir,
# an attached data volume, and empty stub-call logs.
reset_state() {
rm -rf "${WORK}/data" "${WORK}/run" "${WORK}/log"
mkdir -p "${DATA_MOUNT}"
printf '/dev/vdb %s ext4 rw,relatime 0 0\n' "${DATA_MOUNT}" > "${MOUNTS}"
: > "${INITDB_CALLS}"
: > "${PGCTL_CALLS}"
: > "${PSQL_CALLS}"
unset INITDB_FAIL PG_HBA_AS_DIR PSQL_FAIL LS_FAIL RDS_ALLOW_LOCAL_DATADIR MASTER_USER || true
unset PENDING PAYLOAD_ID DB_ID RECEIPT_DIR_OVERRIDE PSQL_STATUS_CORRUPT STAMP_OVERRIDE || true
}
# run_env: invoke a command with every path knob pointed into the temp dir.
run_env() {
env RDS_PG_BIN="${STUBBIN}" \
RDS_DATA_MOUNT="${DATA_MOUNT}" \
RDS_HANDOFF_DIR="${HANDOFF}" \
RDS_SOCKET_DIR="${WORK}/run/postgresql" \
RDS_BOOTSTRAP_DIR="${WORK}/run/rds-init" \
RDS_LOG_DIR="${WORK}/log" \
RDS_MOUNTS_FILE="${MOUNTS}" \
RDS_RECEIPT_DIR="${RECEIPT_DIR_OVERRIDE:-${RECEIPT_DIR}}" \
RDS_ENGINE_STAMP="${STAMP_OVERRIDE:-${STAMP}}" \
RDS_ALLOW_LOCAL_DATADIR="${RDS_ALLOW_LOCAL_DATADIR:-0}" \
INITDB_FAIL="${INITDB_FAIL:-0}" \
PG_HBA_AS_DIR="${PG_HBA_AS_DIR:-0}" \
PSQL_FAIL="${PSQL_FAIL:-0}" \
INITDB_CALLS="${INITDB_CALLS}" PGCTL_CALLS="${PGCTL_CALLS}" PSQL_CALLS="${PSQL_CALLS}" \
"$@" </dev/null
}
run() { run_env sh "${SCRIPT}"; }
run_ok() { run > "${WORK}/out" 2>&1 || { fail "$1: non-zero exit: $(cat "${WORK}/out")"; return 1; }; }
run_fails() { run > "${WORK}/out" 2>&1 && fail "$1: expected a non-zero exit" || pass "$1: refused"; }
# run_signalled: run rds-init in the background with its PID published for the
# stub that signals it. The wrapper writes the PID before exec'ing the script,
# so the stub cannot read the file before it exists.
run_signalled() {
run_env sh -c 'echo $$ > "$1"; shift; exec sh "$@"' _ "${KILL_PID_FILE}" "${SCRIPT}" \
> "${WORK}/out" 2>&1 &
_bg=$!
wait "${_bg}"
}
run_signalled_fails() {
run_signalled && fail "$1: expected a non-zero exit" || pass "$1: refused"
}
# --- Case 1: initialize on a fresh data volume ---
reset_state
write_handoff initialize 's3cr3t' appdb
write_tls
write_parameters
if run_ok "initialize"; then
grep -q 'initdb' "${INITDB_CALLS}" && pass "initialize: initdb ran" || fail "initialize: no initdb"
grep -q -- '--data-checksums' "${INITDB_CALLS}" \
&& pass "initialize: data checksums on" || fail "initialize: no --data-checksums"
grep -q 'host all all 0.0.0.0/0 scram-sha-256' "${PGDATA}/pg_hba.conf" \
&& pass "initialize: remote scram hba rule appended" || fail "initialize: no hba rule"
grep -q "^include_dir = 'conf.d'" "${PGDATA}/postgresql.conf" \
&& pass "initialize: include_dir hooked" || fail "initialize: no include_dir"
grep -q '^port = 6543' "${PGDATA}/conf.d/90-rds-init.conf" \
&& pass "initialize: delivered port applied" || fail "initialize: port not applied"
grep -q '^ssl = on' "${PGDATA}/conf.d/90-rds-init.conf" \
&& pass "initialize: ssl enabled" || fail "initialize: ssl not enabled"
grep -q 'shared_buffers = 128MB' "${PGDATA}/conf.d/10-rds-parameters.conf" \
&& pass "initialize: resolved parameters installed" || fail "initialize: no parameter include"
grep -q 'ALTER ROLE :"master" WITH LOGIN NOSUPERUSER CREATEDB CREATEROLE PASSWORD' "${PSQL_CALLS}" \
&& pass "initialize: master password applied" || fail "initialize: no ALTER ROLE"
grep -q 'password=s3cr3t' "${PSQL_CALLS}" \
&& pass "initialize: password passed through the environment" || fail "initialize: password not in psql env"
grep -q 'CREATE DATABASE' "${PSQL_CALLS}" \
&& pass "initialize: initial database created" || fail "initialize: no CREATE DATABASE"
grep -q 'CREATE EXTENSION' "${PSQL_CALLS}" \
&& fail "initialize: a tenant instance installed a platform extension" \
|| pass "initialize: tenant instance installs no platform extension"
grep -q 'GRANT CREATE ON DATABASE' "${PSQL_CALLS}" \
&& fail "initialize: a tenant instance was granted database CREATE" \
|| pass "initialize: tenant instance gets no platform database grant"
grep -q 'createrole_self_grant' "${PSQL_CALLS}" \
&& fail "initialize: a tenant master got createrole_self_grant" \
|| pass "initialize: tenant master gets no createrole_self_grant"
# The master role is administrative but not a PostgreSQL superuser: a
# superuser reaches outside the database (COPY FROM PROGRAM, pg_read_file,
# untrusted languages), so master credentials would be a shell in the DB VM.
grep -qE '(^|[^O])SUPERUSER' "${PSQL_CALLS}" \
&& fail "initialize: bootstrap SQL still grants SUPERUSER" \
|| pass "initialize: master role is not a superuser"
grep -q 'GRANT :"grp" TO :"master" WITH ADMIN OPTION' "${PSQL_CALLS}" \
&& pass "initialize: master holds the administrative group role" \
|| fail "initialize: master never granted the group role"
grep -q 'grp=rds_superuser' "${PSQL_CALLS}" \
&& pass "initialize: the group role is named as on AWS" || fail "initialize: group role name not delivered"
grep -q 'GRANT pg_monitor, pg_signal_backend, pg_checkpoint TO :"grp"' "${PSQL_CALLS}" \
&& pass "initialize: monitoring/signal/checkpoint granted" || fail "initialize: group role has no privileges"
# The three predefined roles that hand back exactly what SUPERUSER gave away.
grep -qE 'pg_(read|write)_server_files|pg_execute_server_program' "${PSQL_CALLS}" \
&& fail "initialize: granted a role that restores file or program access" \
|| pass "initialize: no server-file or program-execution roles granted"
grep -q "listen_addresses=''" "${PGCTL_CALLS}" \
&& pass "initialize: bootstrap server is socket-only" || fail "initialize: bootstrap server listened on TCP"
grep -q 'stop' "${PGCTL_CALLS}" \
&& pass "initialize: bootstrap server stopped" || fail "initialize: bootstrap server left running"
[ -e "${SENTINEL}" ] \
&& fail "initialize: incomplete-bootstrap sentinel left behind" \
|| pass "initialize: sentinel cleared once the master role exists"
[ -e "${WORK}/run/rds-init/pg_hba.conf" ] \
&& fail "initialize: the trust-auth pg_hba outlived the bootstrap window" \
|| pass "initialize: trust-auth pg_hba removed"
fi
# --- Case 2: the master password does not outlive its use ---
grep -q '^RDS_MASTER_PASSWORD=' "${HANDOFF}/bootstrap.env" \
&& fail "consume: password still in the handoff" || pass "consume: password dropped from the handoff"
grep -q '^RDS_MASTER_USERNAME=mulgamaster' "${HANDOFF}/bootstrap.env" \
&& pass "consume: the rest of the config is kept" || fail "consume: non-secret fields lost"
[ -f "${HANDOFF}/server.key" ] \
&& fail "consume: serving key left in the handoff" || pass "consume: serving key moved out of the handoff"
[ -f "${WORK}/run/postgresql/tls/server.key" ] \
&& pass "tls: key installed on tmpfs, not the data volume" || fail "tls: key not installed"
[ -f "${PGDATA}/server.key" ] \
&& fail "tls: key written into the datadir (would land in every snapshot)" || pass "tls: datadir free of the key"
# --- Case 3: second boot (attach) is idempotent ---
: > "${INITDB_CALLS}"; : > "${PSQL_CALLS}"; : > "${PGCTL_CALLS}"
write_handoff attach '' appdb
write_parameters
if run_ok "attach"; then
[ -s "${INITDB_CALLS}" ] \
&& fail "attach: initdb re-ran on an initialised datadir" || pass "attach: initdb skipped"
[ -s "${PSQL_CALLS}" ] \
&& fail "attach: re-applied the master role" || pass "attach: no role/database work"
grep -q '^ssl = on' "${PGDATA}/conf.d/90-rds-init.conf" \
&& pass "attach: TLS survives a handoff without a new cert" || fail "attach: TLS turned off"
fi
# --- Case 3b: the Ochre appliance master installs pgvector ---
# No DBName, as the appliance is launched: create_database is skipped and the
# gated extension install is the only platform-specific bootstrap step.
reset_state
MASTER_USER=ochre_vector_admin
write_handoff initialize 's3cr3t' ''
write_parameters
if run_ok "ochre-appliance"; then
grep -q 'CREATE EXTENSION IF NOT EXISTS vector SCHEMA extensions' "${PSQL_CALLS}" \
&& pass "ochre-appliance: pgvector installed into the extensions schema" \
|| fail "ochre-appliance: pgvector not installed into the extensions schema"
grep -q 'GRANT USAGE ON SCHEMA extensions TO PUBLIC' "${PSQL_CALLS}" \
&& pass "ochre-appliance: extensions schema readable by account roles" \
|| fail "ochre-appliance: extensions schema not granted to PUBLIC"
grep -q 'GRANT CREATE ON DATABASE postgres TO "ochre_vector_admin"' "${PSQL_CALLS}" \
&& pass "ochre-appliance: master granted CREATE on the database" \
|| fail "ochre-appliance: master not granted CREATE on the database"
grep -q 'createrole_self_grant' "${PSQL_CALLS}" \
&& pass "ochre-appliance: master can set into the roles it creates" \
|| fail "ochre-appliance: master missing createrole_self_grant"
fi
unset MASTER_USER
# --- Case 3a: a parameter group cannot log the master password ---
# The customer's parameters are installed before the master role is applied and
# the bootstrap postmaster does not override config_file, so log_statement = all
# is live for the ALTER ROLE carrying the password. The guard is session-local
# and SUSET, so it holds whatever the parameter group says.
reset_state
write_handoff initialize 's3cr3t' appdb
write_parameters "log_statement = all"
if run_ok "log-guard"; then
grep -q '^log_statement = all' "${PGDATA}/conf.d/10-rds-parameters.conf" \
&& pass "log-guard: the customer's logging parameter is installed" \
|| fail "log-guard: the parameter group was not installed, so this is not the reported case"
for setting in "SET log_statement = 'none';" \
"SET log_min_duration_statement = -1;" \
"SET log_min_error_statement = 'panic';"; do
grep -qF "${setting}" "${PSQL_CALLS}" \
&& pass "log-guard: ${setting}" || fail "log-guard: psql never received ${setting}"
done
# Every bootstrap session carries the guard, so a statement added to the
# bootstrap later cannot end up outside it.
# `grep -c` exits non-zero on no match, which `set -e` would turn into an
# aborted run in place of a reported failure.
guard_count=$(grep -cF "SET log_statement = 'none';" "${PSQL_CALLS}" || true)
psql_count=$(grep -c '^--- psql ' "${PSQL_CALLS}" || true)
[ "${guard_count}" = "${psql_count}" ] \
&& pass "log-guard: all ${psql_count} bootstrap sessions guarded" \
|| fail "log-guard: ${guard_count} of ${psql_count} bootstrap sessions guarded"
guard_line=$(grep -nF "SET log_statement = 'none';" "${PSQL_CALLS}" | head -n 1 | cut -d: -f1)
alter_line=$(grep -n 'ALTER ROLE' "${PSQL_CALLS}" | head -n 1 | cut -d: -f1)
{ [ -n "${alter_line}" ] && [ "${guard_line}" -lt "${alter_line}" ]; } \
&& pass "log-guard: the guard precedes the ALTER ROLE" \
|| fail "log-guard: the guard does not precede the ALTER ROLE"
fi
# --- Case 3b: a failing psql must not echo the password to the console ---
# rds-init.initd sends this script's output to /dev/console, which the host
# captures off ttyS0, and psql under ON_ERROR_STOP echoes the statement it
# failed on. The redaction must not swallow the failure: the datadir is cleared
# exactly as in case 6d.
reset_state
write_handoff initialize 's3cr3t' appdb
write_parameters
PSQL_FAIL=1
export PSQL_FAIL
run_fails "console-redaction"
grep -q 's3cr3t' "${WORK}/out" \
&& fail "console-redaction: the console capture carries the master password" \
|| pass "console-redaction: no password on the console"
grep -q '\[REDACTED\]' "${WORK}/out" \
&& pass "console-redaction: the redaction is marked" \
|| fail "console-redaction: psql's stderr never reached the console"
grep -q 'ERROR: syntax error' "${WORK}/out" \
&& pass "console-redaction: the diagnostic itself survives" \
|| fail "console-redaction: the redaction swallowed the diagnostic"
[ -e "${PGDATA}/PG_VERSION" ] \
&& fail "console-redaction: datadir kept after a failed master bootstrap" \
|| pass "console-redaction: the failure still propagates"
unset PSQL_FAIL
# --- Case 3c: psql's status must cross the redaction pipeline or fail closed ---
# The status file is how psql's exit code leaves the subshell, POSIX sh having
# no pipefail. A torn write leaves it created but empty, and `return ""` is
# fatal in the guest's ash: rds-init would die past the datadir sweep and before
# any diagnostic, leaving a trust-authenticated postmaster on a deleted datadir.
for corruption in empty garbage; do
reset_state
write_handoff initialize 's3cr3t' appdb
PSQL_STATUS_CORRUPT="${corruption}"
export PSQL_STATUS_CORRUPT
run_fails "psql-status-${corruption}"
grep -q 'exit status could not be read back' "${WORK}/out" \
&& pass "psql-status-${corruption}: names the unreadable status rather than blaming psql" \
|| fail "psql-status-${corruption}: no diagnostic naming the status file"
grep -q 'Illegal number\|numeric argument required' "${WORK}/out" \
&& fail "psql-status-${corruption}: an unvalidated status reached return" \
|| pass "psql-status-${corruption}: nothing unvalidated reached return"
[ -e "${PGDATA}/PG_VERSION" ] \
&& fail "psql-status-${corruption}: datadir kept after an unrecoverable status" \
|| pass "psql-status-${corruption}: datadir cleared"
grep -q 'stop' "${PGCTL_CALLS}" \
&& pass "psql-status-${corruption}: bootstrap server stopped" \
|| fail "psql-status-${corruption}: bootstrap server left holding the datadir"
unset PSQL_STATUS_CORRUPT
done
# --- Case 3d: a status file that could never be written is not psql's fault ---
# The write can fail outright — a read-only or full /run — while psql itself
# succeeded. Failing closed is right, but the operator must not be sent to a
# PostgreSQL that did exactly what it was asked.
reset_state
write_handoff initialize 's3cr3t' appdb
PSQL_STATUS_CORRUPT=absent
export PSQL_STATUS_CORRUPT
run_fails "psql-status-absent"
grep -q 'exit status was never recorded' "${WORK}/out" \
&& pass "psql-status-absent: names the missing status file" \
|| fail "psql-status-absent: no diagnostic distinguishing it from a psql failure"
[ -e "${PGDATA}/PG_VERSION" ] \
&& fail "psql-status-absent: datadir kept" || pass "psql-status-absent: datadir cleared"
unset PSQL_STATUS_CORRUPT
# --- Case 3e: a multi-line master password is refused before it is spent ---
# redact_stream is line-oriented, so a password spanning a line boundary matches
# neither half and reaches /dev/console in the clear. The control plane rejects
# one; this is the guest asserting that rather than trusting it.
reset_state
write_handoff initialize 'top
secret' appdb
run_fails "multiline-password"
[ -s "${INITDB_CALLS}" ] \
&& fail "multiline-password: initdb ran before the password was refused" \
|| pass "multiline-password: refused before initdb spent it"
grep -q 'spans more than one line' "${WORK}/out" \
&& pass "multiline-password: refusal names the reason" \
|| fail "multiline-password: no refusal message"
# --- Case 4: an empty datadir in attach mode means the data volume is missing ---
reset_state
write_handoff attach '' appdb
run_fails "attach-empty"
[ -s "${INITDB_CALLS}" ] \
&& fail "attach-empty: initialised an empty cluster over missing data" || pass "attach-empty: no initdb"
# --- Case 5: the datadir must be on the attached data volume ---
reset_state
: > "${MOUNTS}"
write_handoff initialize 's3cr3t' appdb
run_fails "no-volume"
[ -s "${INITDB_CALLS}" ] \
&& fail "no-volume: initialised on the boot volume" || pass "no-volume: no initdb"
RDS_ALLOW_LOCAL_DATADIR=1
export RDS_ALLOW_LOCAL_DATADIR
if run_ok "local-datadir"; then
grep -q 'initdb' "${INITDB_CALLS}" \
&& pass "local-datadir: explicit override initialises anyway" || fail "local-datadir: no initdb"
fi
unset RDS_ALLOW_LOCAL_DATADIR
# --- Case 6: a failed initdb leaves nothing that looks initialised ---
reset_state
write_handoff initialize 's3cr3t' appdb
INITDB_FAIL=1
export INITDB_FAIL
run_fails "initdb-fail"
[ -e "${PGDATA}/PG_VERSION" ] \
&& fail "initdb-fail: half-written datadir kept" || pass "initdb-fail: datadir cleared"
unset INITDB_FAIL
# --- Case 6a: a post-initdb failure is covered by the cleanup trap ---
reset_state
write_handoff initialize 's3cr3t' appdb
PG_HBA_AS_DIR=1
export PG_HBA_AS_DIR
run_fails "post-initdb-fail"
[ -e "${PGDATA}/PG_VERSION" ] \
&& fail "post-initdb-fail: datadir kept before the former late trap point" \
|| pass "post-initdb-fail: datadir cleared"
unset PG_HBA_AS_DIR
# --- Case 6b: a failed emptiness probe must abort before initdb ---
# No output from ls can mean either "empty" or "could not enumerate". Only a
# successful probe may authorize cleanup of a datadir this invocation creates.
reset_state
write_handoff initialize 's3cr3t' appdb
mkdir -p "${PGDATA}"
: > "${PGDATA}/PG_VERSION"
echo 'customer table data' > "${PGDATA}/base-relation"
LS_FAIL=1
export LS_FAIL
run_fails "datadir-probe-fail"
unset LS_FAIL
[ -s "${INITDB_CALLS}" ] \
&& fail "datadir-probe-fail: initdb ran after the probe failed" \
|| pass "datadir-probe-fail: initdb not run"
[ -f "${PGDATA}/base-relation" ] \
&& pass "datadir-probe-fail: pre-existing datadir preserved" \
|| fail "datadir-probe-fail: pre-existing datadir deleted"
grep -q 'could not inspect datadir' "${WORK}/out" \
&& pass "datadir-probe-fail: refusal explains why" || fail "datadir-probe-fail: no refusal message"
# --- Case 6c: a failed initdb over a NON-empty datadir must not delete it ---
# A zero-length PG_VERSION over an otherwise intact datadir takes the
# initialise path, and "directory not empty" is one way initdb then fails.
# Clearing there would destroy customer data in response to the one signal
# that it is still present.
reset_state
write_handoff initialize 's3cr3t' appdb
mkdir -p "${PGDATA}"
: > "${PGDATA}/PG_VERSION"
echo 'customer table data' > "${PGDATA}/base-relation"
INITDB_FAIL=1
export INITDB_FAIL
run_fails "initdb-fail-nonempty"
[ -f "${PGDATA}/base-relation" ] \
&& pass "initdb-fail-nonempty: pre-existing datadir preserved" \
|| fail "initdb-fail-nonempty: pre-existing datadir deleted"
grep -q 'refusing to clear' "${WORK}/out" \
&& pass "initdb-fail-nonempty: refusal explains why" || fail "initdb-fail-nonempty: no refusal message"
unset INITDB_FAIL
# --- Case 6d: a failed master bootstrap leaves nothing bootable either ---
# postgresql is in the default runlevel independently of this oneshot, so a
# datadir kept here would start an engine whose master role has no password —
# and the password is one-shot, so the next fetch is `attach` and cannot repair
# it. The datadir must go the same way a failed initdb's does.
reset_state
write_handoff initialize 's3cr3t' appdb
PSQL_FAIL=1
export PSQL_FAIL
run_fails "bootstrap-fail"
[ -e "${PGDATA}/PG_VERSION" ] \
&& fail "bootstrap-fail: datadir kept after a failed master bootstrap" \
|| pass "bootstrap-fail: datadir cleared"
grep -q 'stop' "${PGCTL_CALLS}" \
&& pass "bootstrap-fail: bootstrap server stopped" || fail "bootstrap-fail: bootstrap server left running"
unset PSQL_FAIL
# --- Case 6e: the bootstrap server never starts ---
# `set -e` aborts the moment pg_ctl start fails, so nothing inside
# bootstrap_master runs. The datadir already carries PG_VERSION and a pg_hba
# accepting scram, and the password is spent, so leaving it would attach on the
# next boot and serve a database whose master role has no password.
reset_state
write_handoff initialize 's3cr3t' appdb
PGCTL_START_FAIL=1
export PGCTL_START_FAIL
run_fails "bootstrap-nostart"
[ -e "${PGDATA}/PG_VERSION" ] \
&& fail "bootstrap-nostart: datadir kept after the cluster failed to start" \
|| pass "bootstrap-nostart: datadir cleared"
unset PGCTL_START_FAIL
# --- Case 6f: a pre-existing datadir is never swept by the trap ---
# The clear is scoped to a datadir this invocation created. A torn write that
# loses PG_VERSION over intact data takes the initialise branch, and the sweep
# must not answer that by destroying the customer's data.
reset_state
write_handoff initialize 's3cr3t' appdb
mkdir -p "${PGDATA}"
echo "customer data" > "${PGDATA}/base_survivor"
INITDB_FAIL=1
export INITDB_FAIL
run_fails "preexisting-nosweep"
[ -e "${PGDATA}/base_survivor" ] \
&& pass "preexisting-nosweep: pre-existing datadir untouched" \
|| fail "preexisting-nosweep: swept a datadir it did not create"
unset INITDB_FAIL
# --- Case 6g: a SIGTERM between initdb and the master role clears the datadir ---
# An EXIT trap does not run on a signal in a POSIX shell, so a shutdown, an ACPI
# powerdown from a stop arriving while the instance is still creating, or a
# host-side force-stop would otherwise leave a datadir with no master role.
reset_state
write_handoff initialize 's3cr3t' appdb
PGCTL_KILL_PARENT=1
export PGCTL_KILL_PARENT
run_signalled_fails "sigterm-midbootstrap"
[ -e "${PGDATA}/PG_VERSION" ] \
&& fail "sigterm-midbootstrap: datadir kept after a signal mid-bootstrap" \
|| pass "sigterm-midbootstrap: datadir cleared"
[ -s "${PSQL_CALLS}" ] \
&& fail "sigterm-midbootstrap: the master role work ran after the signal" \
|| pass "sigterm-midbootstrap: stopped before the master role"
unset PGCTL_KILL_PARENT
# --- Case 6h: a datadir carrying the sentinel is refused, not started ---
# No trap survives a crash or a SIGKILL, so the sentinel is the only record that
# the bootstrap did not finish. It says the master role is missing, not that the
# volume is empty, so the refusal must not clear anything.
reset_state
write_handoff initialize 's3cr3t' appdb
if run_ok "sentinel-setup"; then
echo 'customer table data' > "${PGDATA}/base-relation"
: > "${SENTINEL}"
write_handoff attach '' appdb
run_fails "sentinel-refused"
[ -f "${PGDATA}/base-relation" ] \
&& pass "sentinel-refused: datadir preserved" || fail "sentinel-refused: datadir cleared"
grep -q 'recover the data volume out of band' "${WORK}/out" \
&& pass "sentinel-refused: refusal points at out-of-band recovery" \
|| fail "sentinel-refused: no refusal message"
fi
# --- Case 6i: a bootstrap server that will not stop is a failure ---
# It holds the datadir, so the postgresql service cannot start for the life of
# the boot. The master role exists by then, so the datadir itself must survive.
reset_state
write_handoff initialize 's3cr3t' appdb
PGCTL_STOP_FAIL=1
export PGCTL_STOP_FAIL
run_fails "stop-fail"
grep -q 'did not stop' "${WORK}/out" \
&& pass "stop-fail: reported rather than swallowed" || fail "stop-fail: no failure message"
[ -e "${PGDATA}/PG_VERSION" ] \
&& pass "stop-fail: bootstrapped datadir kept" || fail "stop-fail: cleared a datadir with a master role"
[ -e "${SENTINEL}" ] \
&& fail "stop-fail: sentinel kept although the master role exists" \
|| pass "stop-fail: sentinel cleared"
unset PGCTL_STOP_FAIL
# --- Case 6j: a completed bootstrap leaves a receipt proving it ---
# The receipt is what lets rds-agent tell the control plane the staged password
# was durably applied, so the ciphertext can be destroyed. It lives outside
# PGDATA, which the failure traps rm -rf.
reset_state
PENDING=1 PAYLOAD_ID=bp-alpha DB_ID=db-alpha
export PENDING PAYLOAD_ID DB_ID
write_handoff initialize 's3cr3t' appdb
if run_ok "receipt"; then
[ -f "${RECEIPT}" ] \
&& pass "receipt: written on the initialize path" || fail "receipt: no receipt written"
grep -q '^RDS_RECEIPT_PAYLOAD_ID=bp-alpha$' "${RECEIPT}" \
&& pass "receipt: names the payload it applied" || fail "receipt: wrong or missing payload id"
grep -q '^RDS_RECEIPT_DB_INSTANCE_IDENTIFIER=db-alpha$' "${RECEIPT}" \
&& pass "receipt: names its DB instance" || fail "receipt: wrong or missing DB instance"
grep -q 'PASSWORD\|s3cr3t' "${RECEIPT}" \
&& fail "receipt: carries a secret" || pass "receipt: carries no secrets"
case "${RECEIPT}" in
"${PGDATA}"/*) fail "receipt: written inside the datadir the traps clear" ;;
*) pass "receipt: written outside PGDATA" ;;
esac
fi
# --- Case 6k: a receipt that cannot be written is fatal ---
# A successful bootstrap whose receipt never landed would bring up a healthy
# engine while the agent blocked forever on a receipt that never appears —
# a working database that can never take a password change or parameter reload.
reset_state
PENDING=1 PAYLOAD_ID=bp-beta
export PENDING PAYLOAD_ID
write_handoff initialize 's3cr3t' appdb
: > "${WORK}/not-a-dir"
RECEIPT_DIR_OVERRIDE="${WORK}/not-a-dir/bootstrap"
export RECEIPT_DIR_OVERRIDE
run_fails "receipt-fail"
[ -e "${PGDATA}/PG_VERSION" ] \
&& fail "receipt-fail: datadir kept although its bootstrap cannot be proven" \
|| pass "receipt-fail: datadir cleared"
grep -q 'bootstrap receipt' "${WORK}/out" \
&& pass "receipt-fail: reported rather than swallowed" || fail "receipt-fail: no failure message"
unset RECEIPT_DIR_OVERRIDE
# --- Case 6k2: a sweep between the receipt and the retired traps takes both ---
# The receipt is installed while the traps are still armed. A receipt left behind
# by a sweep would be read by rds-agent, acknowledged, and the control plane would
# destroy the only copy of the password this datadir can be initialised with.
reset_state
PENDING=1 PAYLOAD_ID=bp-gamma
export PENDING PAYLOAD_ID
write_handoff initialize 's3cr3t' appdb
SYNC_KILL_PARENT=1
export SYNC_KILL_PARENT
run_signalled_fails "receipt-swept"
[ -e "${PGDATA}/PG_VERSION" ] \
&& fail "receipt-swept: datadir kept after a signal mid-bootstrap" \
|| pass "receipt-swept: datadir cleared"
[ -e "${RECEIPT}" ] \
&& fail "receipt-swept: receipt survived the datadir it vouches for" \
|| pass "receipt-swept: receipt cleared with the datadir"
unset SYNC_KILL_PARENT
# --- Case 6l: pending payload, initialised datadir, no receipt -> fail closed ---
# The datadir alone cannot separate a legacy instance that bootstrapped before
# receipts existed from one that applied the master role and crashed before the
# receipt was durable. Only the control plane's pending flag can.
reset_state
PENDING=1 PAYLOAD_ID=bp-gamma DB_ID=db-gamma
export PENDING PAYLOAD_ID DB_ID
write_handoff initialize 's3cr3t' appdb
if run_ok "pending-setup"; then
echo 'customer table data' > "${PGDATA}/base-relation"
rm -f "${RECEIPT}"
run_fails "pending-no-receipt"
[ -f "${PGDATA}/base-relation" ] \
&& pass "pending-no-receipt: datadir preserved" || fail "pending-no-receipt: datadir cleared"
grep -q 'master role cannot be proven' "${WORK}/out" \
&& pass "pending-no-receipt: refusal names the real reason" \
|| fail "pending-no-receipt: no refusal message"
grep -q 'password is spent' "${WORK}/out" \
&& fail "pending-no-receipt: claims the password is spent, which it is not" \
|| pass "pending-no-receipt: does not claim a spent password"
# --- Case 6m: the same boot with a matching receipt attaches ---
: > "${INITDB_CALLS}"; : > "${PSQL_CALLS}"
mkdir -p "${RECEIPT_DIR}"
printf 'RDS_RECEIPT_PAYLOAD_ID=bp-gamma\nRDS_RECEIPT_DB_INSTANCE_IDENTIFIER=db-gamma\n' > "${RECEIPT}"
if run_ok "pending-with-receipt"; then
[ -s "${INITDB_CALLS}" ] \
&& fail "pending-with-receipt: re-ran initdb" || pass "pending-with-receipt: attached"
fi
# --- Case 6n: a receipt naming another instance is treated as absent ---
# The receipt is on the data volume, so it rides along in every snapshot of
# it and a restored volume carries the source instance's receipt.
printf 'RDS_RECEIPT_PAYLOAD_ID=bp-gamma\nRDS_RECEIPT_DB_INSTANCE_IDENTIFIER=db-somebody-else\n' > "${RECEIPT}"
run_fails "pending-foreign-receipt"
[ -f "${PGDATA}/base-relation" ] \
&& pass "pending-foreign-receipt: datadir preserved" || fail "pending-foreign-receipt: datadir cleared"
fi
unset PENDING PAYLOAD_ID DB_ID
# --- Case 6o: no pending payload leaves the attach path unchanged ---
# An agent predating the receipt protocol sends neither flag, and a legacy
# instance carries no receipt. Neither may be turned into a refusal.
reset_state
write_handoff initialize 's3cr3t' appdb
if run_ok "legacy-setup"; then
rm -rf "${RECEIPT_DIR}"
: > "${INITDB_CALLS}"
write_handoff attach '' appdb
if run_ok "legacy-attach"; then
[ -s "${INITDB_CALLS}" ] \
&& fail "legacy-attach: re-ran initdb" || pass "legacy-attach: attached without a receipt"
fi
fi
# --- Case 7: no serving cert -> TLS off rather than a failed start ---
reset_state
write_handoff initialize 's3cr3t' ''
if run_ok "no-cert"; then
grep -q '^ssl = off' "${PGDATA}/conf.d/90-rds-init.conf" \
&& pass "no-cert: ssl off" || fail "no-cert: ssl not off"
grep -q 'ssl_cert_file' "${PGDATA}/conf.d/90-rds-init.conf" \
&& fail "no-cert: points at a cert that was never delivered" || pass "no-cert: no cert paths"
grep -q 'CREATE DATABASE' "${PSQL_CALLS}" \
&& fail "no-dbname: created a database without a DBName" || pass "no-dbname: no initial database"
fi
# --- Case 8: no handoff at all ---
reset_state
rm -rf "${HANDOFF}"
run_fails "no-handoff"
# --- Case 9: a master username the platform reserves ---
# The master role is created NOSUPERUSER, so one named after the bootstrap
# superuser would leave the cluster without a superuser at all and strip
# rds-agent of the privileged SQL it needs — on a datadir that bootstraps once.
# The set and the case-insensitive matching are the control plane's: a name it
# refuses must not reach initdb here and spend the one-shot password.
for reserved in postgres rds_superuser rdsadmin pg_toast_owner PostGres; do
reset_state
MASTER_USER="${reserved}"
write_handoff initialize 's3cr3t' appdb
run_fails "reserved-master-${reserved}"
[ -s "${INITDB_CALLS}" ] \
&& fail "reserved-master-${reserved}: initdb ran before the name was refused" \
|| pass "reserved-master-${reserved}: refused before initdb"
unset MASTER_USER
done
# --- Case 10: an initial database name the control plane would have refused ---
# The name is interpolated into a CREATE DATABASE, and a failure there is a
# failure after initdb: the datadir is cleared and the one-shot password is
# gone. Refusing before initdb is what keeps the create retryable.
for badname in 'my-db' 'my db' 'my/db' 'my.db' '1db' \
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; do
reset_state
write_handoff initialize 's3cr3t' "${badname}"
run_fails "bad-dbname-${badname}"
[ -s "${INITDB_CALLS}" ] \
&& fail "bad-dbname-${badname}: initdb ran before the name was refused" \
|| pass "bad-dbname-${badname}: refused before initdb"
done
# The rule is a rejection of malformed names, not of every name: the boundary
# length and an underscore have to keep working.
reset_state
write_handoff initialize 's3cr3t' \
'a_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
write_parameters
if run_ok "dbname-at-the-limit"; then
grep -q 'CREATE DATABASE' "${PSQL_CALLS}" \
&& pass "dbname-at-the-limit: accepted" || fail "dbname-at-the-limit: no CREATE DATABASE"
fi
# --- Case 11: the data volume records which engine wrote it ---
# Another engine's datadir mounts cleanly and reads as uninitialised here, so
# without the stamp rds-init would initdb beside the customer's data and serve
# an empty database that passes every health probe while the real data sits
# unreferenced — until the first automated snapshot captures the wrong one.
reset_state
write_handoff initialize 's3cr3t' appdb
if run_ok "stamp-absent-empty"; then
[ "$(cat "${STAMP}" 2>/dev/null)" = "postgres" ] \
&& pass "stamp-absent-empty: fresh volume stamped postgres" \
|| fail "stamp-absent-empty: no stamp written at initialisation"
case "${STAMP}" in
"${PGDATA}"/*) fail "stamp-absent-empty: written inside the datadir the traps clear" ;;
*) pass "stamp-absent-empty: written outside PGDATA" ;;
esac
# --- Case 11a: a matching stamp attaches ---
: > "${INITDB_CALLS}"
write_handoff attach '' appdb
if run_ok "stamp-match"; then
[ -s "${INITDB_CALLS}" ] \
&& fail "stamp-match: re-ran initdb" || pass "stamp-match: attached"
fi
# --- Case 11b: a disagreeing stamp is fatal and touches nothing ---
echo 'customer table data' > "${PGDATA}/base-relation"
printf 'mariadb\n' > "${STAMP}"
: > "${INITDB_CALLS}"
run_fails "stamp-mismatch"
[ -s "${INITDB_CALLS}" ] \
&& fail "stamp-mismatch: initdb ran on another engine's volume" \
|| pass "stamp-mismatch: no initdb"
[ -f "${PGDATA}/base-relation" ] \
&& pass "stamp-mismatch: datadir preserved" || fail "stamp-mismatch: datadir cleared"
[ "$(cat "${STAMP}")" = "mariadb" ] \
&& pass "stamp-mismatch: the other engine's stamp is left as it stands" \
|| fail "stamp-mismatch: stamp overwritten"
grep -q "holds a 'mariadb' datadir" "${WORK}/out" \
&& pass "stamp-mismatch: refusal names both engines" \
|| fail "stamp-mismatch: no refusal naming the stamped engine"
# --- Case 11c: a stamp that cannot be read is not an absent one ---
# Reading it as absent would backfill our own engine over a volume whose
# engine was never established, turning the check into a rubber stamp.
rm -f "${STAMP}"
mkdir "${STAMP}"
run_fails "stamp-unreadable"
grep -q 'could not be read' "${WORK}/out" \
&& pass "stamp-unreadable: refusal names the unreadable stamp" \
|| fail "stamp-unreadable: no refusal message"
rmdir "${STAMP}"
# --- Case 11c2: a zero-length stamp does not read as a match ---
: > "${STAMP}"
run_fails "stamp-empty"
[ -f "${PGDATA}/base-relation" ] \
&& pass "stamp-empty: datadir preserved" || fail "stamp-empty: datadir cleared"
# --- Case 11d: an unstamped datadir with data in it is backfilled ---
# PostgreSQL volumes predate the stamp, so the check becomes total over time
# rather than refusing every instance created before it existed.
rm -f "${STAMP}"
: > "${INITDB_CALLS}"
if run_ok "stamp-absent-nonempty"; then
[ "$(cat "${STAMP}" 2>/dev/null)" = "postgres" ] \
&& pass "stamp-absent-nonempty: existing datadir gains a stamp" \
|| fail "stamp-absent-nonempty: stamp not backfilled"
[ -s "${INITDB_CALLS}" ] \
&& fail "stamp-absent-nonempty: re-ran initdb" || pass "stamp-absent-nonempty: attached"
[ -f "${PGDATA}/base-relation" ] \
&& pass "stamp-absent-nonempty: datadir preserved" \
|| fail "stamp-absent-nonempty: datadir cleared"
fi
fi
# --- Case 11e: a stamp that cannot be written is fatal, before initdb ---
# The refusal is before the one-shot password is spent, so the create is still
# retryable; continuing would leave a volume the next boot cannot identify.
reset_state
write_handoff initialize 's3cr3t' appdb
: > "${WORK}/not-a-dir-stamp"
STAMP_OVERRIDE="${WORK}/not-a-dir-stamp/engine"
export STAMP_OVERRIDE
run_fails "stamp-unwritable"
[ -s "${INITDB_CALLS}" ] \
&& fail "stamp-unwritable: initdb ran before the stamp was recorded" \
|| pass "stamp-unwritable: refused before initdb"
grep -q 'engine stamp' "${WORK}/out" \
&& pass "stamp-unwritable: refusal names the stamp" || fail "stamp-unwritable: no refusal message"
unset STAMP_OVERRIDE
if [ "${FAILS}" -eq 0 ]; then
echo "PASS: all rds-init cases"
exit 0
fi
echo "FAILED: ${FAILS} case(s)"
exit 1