mirror of
https://github.com/mulgadc/spinifex.git
synced 2026-10-19 10:52:04 +00:00
343 lines
13 KiB
Bash
343 lines
13 KiB
Bash
#!/bin/sh
|
|
# Self-contained POSIX tests for exact RDS data-volume selection and formatting
|
|
# authorization. Fake sysfs and command stubs avoid root and block devices.
|
|
set -eu
|
|
|
|
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
|
|
SCRIPT="${SCRIPT_DIR}/rds-datadir"
|
|
WORK=$(mktemp -d)
|
|
trap 'rm -rf "${WORK}"' EXIT
|
|
STUBBIN="${WORK}/bin"
|
|
mkdir -p "${STUBBIN}"
|
|
|
|
cat > "${STUBBIN}/blkid" <<'EOF'
|
|
#!/bin/sh
|
|
[ "$1" = "-p" ] && [ "$2" = "-o" ] && [ "$3" = "export" ] || exit 4
|
|
dev="$4"
|
|
kind=$(awk -v d="${dev}" '$1 == d { print $2; exit }' "${PROBE_TABLE}" 2>/dev/null)
|
|
value=$(awk -v d="${dev}" '$1 == d { print $3; exit }' "${PROBE_TABLE}" 2>/dev/null)
|
|
case "${kind}" in
|
|
fs) printf 'DEVNAME=%s\nTYPE=%s\n' "${dev}" "${value}" ;;
|
|
pttable) printf 'DEVNAME=%s\nPTTYPE=%s\n' "${dev}" "${value:-gpt}" ;;
|
|
fspt) printf 'DEVNAME=%s\nTYPE=%s\nPTTYPE=gpt\n' "${dev}" "${value}" ;;
|
|
ambiguous) exit 8 ;;
|
|
unreadable) echo "read error" >&2; exit 2 ;;
|
|
error) exit 4 ;;
|
|
*) exit 2 ;;
|
|
esac
|
|
EOF
|
|
|
|
cat > "${STUBBIN}/mkfs.ext4" <<'EOF'
|
|
#!/bin/sh
|
|
echo "mkfs.ext4 $*" >> "${MKFS_CALLS}"
|
|
[ "${MKFS_FAIL:-0}" = "1" ] && exit 1
|
|
dev=""
|
|
for arg in "$@"; do dev="${arg}"; done
|
|
printf '%s fs ext4\n' "${dev}" >> "${PROBE_TABLE}"
|
|
EOF
|
|
|
|
cat > "${STUBBIN}/mount" <<'EOF'
|
|
#!/bin/sh
|
|
echo "mount $*" >> "${MOUNT_CALLS}"
|
|
exit "${MOUNT_RC:-0}"
|
|
EOF
|
|
|
|
cat > "${STUBBIN}/chown" <<'EOF'
|
|
#!/bin/sh
|
|
echo "chown $*" >> "${CHOWN_CALLS}"
|
|
EOF
|
|
|
|
chmod +x "${STUBBIN}"/*
|
|
PATH="${STUBBIN}:${PATH}"
|
|
export PATH
|
|
|
|
FAILS=0
|
|
fail() { echo "FAIL: $*"; FAILS=$((FAILS + 1)); }
|
|
pass() { echo "ok: $*"; }
|
|
|
|
DATA_MOUNT="${WORK}/data"
|
|
ENGINE_USER="dbengine"
|
|
SYS_BLOCK="${WORK}/sys/block"
|
|
DEV_DIR="${WORK}/dev"
|
|
MOUNTS="${WORK}/mounts"
|
|
HANDOFF="${WORK}/handoff/bootstrap.env"
|
|
PROBE_TABLE="${WORK}/probe.table"
|
|
MKFS_CALLS="${WORK}/mkfs.calls"
|
|
MOUNT_CALLS="${WORK}/mount.calls"
|
|
CHOWN_CALLS="${WORK}/chown.calls"
|
|
export PROBE_TABLE MKFS_CALLS MOUNT_CALLS CHOWN_CALLS
|
|
|
|
add_disk() {
|
|
mkdir -p "${SYS_BLOCK}/$1" "${DEV_DIR}"
|
|
: > "${DEV_DIR}/$1"
|
|
[ -n "${2:-}" ] && printf '%s\n' "$2" > "${SYS_BLOCK}/$1/serial"
|
|
return 0
|
|
}
|
|
|
|
add_partition() { mkdir -p "${SYS_BLOCK}/$1/$1$2"; }
|
|
|
|
write_handoff() {
|
|
mkdir -p "$(dirname "${HANDOFF}")"
|
|
cat > "${HANDOFF}" <<EOF
|
|
RDS_MODE='initialize'
|
|
RDS_MASTER_USERNAME='master'
|
|
RDS_MASTER_PASSWORD='still-needed-by-rds-init'
|
|
RDS_DATA_VOLUME_ID='${1:-vol-expected}'
|
|
RDS_DATA_VOLUME_SERIAL='${2:-volexpected}'
|
|
RDS_VM_GENERATION='${3:-1}'
|
|
RDS_FORMAT_AUTHORIZED='${4:-false}'
|
|
RDS_PORT='5432'
|
|
EOF
|
|
chmod 0600 "${HANDOFF}"
|
|
}
|
|
|
|
reset_state() {
|
|
DATA_MOUNT="${WORK}/data"
|
|
ENGINE_USER="dbengine"
|
|
rm -rf "${WORK}/sys" "${DEV_DIR}" "${DATA_MOUNT}" "$(dirname "${HANDOFF}")"
|
|
mkdir -p "${SYS_BLOCK}" "${DEV_DIR}" "${DATA_MOUNT}"
|
|
printf '/dev/vda1 / ext4 rw,relatime 0 0\n' > "${MOUNTS}"
|
|
: > "${PROBE_TABLE}"
|
|
: > "${MKFS_CALLS}"
|
|
: > "${MOUNT_CALLS}"
|
|
: > "${CHOWN_CALLS}"
|
|
unset MKFS_FAIL MOUNT_RC || true
|
|
write_handoff vol-expected volexpected 1 false
|
|
}
|
|
|
|
run() {
|
|
env RDS_DATA_MOUNT="${DATA_MOUNT}" \
|
|
RDS_ENGINE_USER="${ENGINE_USER}" \
|
|
RDS_SYS_BLOCK="${SYS_BLOCK}" \
|
|
RDS_DEV_DIR="${DEV_DIR}" \
|
|
RDS_MOUNTS_FILE="${MOUNTS}" \
|
|
RDS_HANDOFF_ENV="${HANDOFF}" \
|
|
RDS_DATA_VOLUME_WAIT=1 \
|
|
MKFS_FAIL="${MKFS_FAIL:-0}" \
|
|
MOUNT_RC="${MOUNT_RC:-0}" \
|
|
sh "${SCRIPT}" </dev/null
|
|
}
|
|
|
|
run_ok() {
|
|
if run > "${WORK}/out" 2>&1; then
|
|
pass "$1"
|
|
return 0
|
|
fi
|
|
fail "$1: non-zero exit: $(cat "${WORK}/out")"
|
|
return 1
|
|
}
|
|
|
|
# A refusal must be diagnosable on the serial console, so the expected reason is
|
|
# asserted too: a non-zero exit alone also matches a script that died silently.
|
|
# Always returns 0; a non-zero status here would abort the suite under set -e.
|
|
run_fails() {
|
|
if run > "${WORK}/out" 2>&1; then
|
|
fail "$1: expected a non-zero exit"
|
|
elif ! grep -q "\[rds-datadir\] ERROR: .*$2" "${WORK}/out"; then
|
|
fail "$1: refused without reporting '$2': $(cat "${WORK}/out")"
|
|
else
|
|
pass "$1: refused with a reported reason"
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
nothing_formatted() {
|
|
if grep -q . "${MKFS_CALLS}"; then fail "$1: unexpectedly ran mkfs"; else pass "$1: no mkfs"; fi
|
|
}
|
|
|
|
# A matching create grant formats only the exact disk and consumes only the
|
|
# authorization line. An unrelated generic volume is ignored.
|
|
reset_state
|
|
add_disk vda
|
|
add_disk vdb volunrelated
|
|
add_disk vdc volexpected
|
|
write_handoff vol-expected volexpected 1 true
|
|
if run_ok "authorized blank exact volume"; then
|
|
grep -q "mkfs.ext4 .*${DEV_DIR}/vdc" "${MKFS_CALLS}" && pass "authorized: exact disk formatted" || fail "authorized: exact disk not formatted"
|
|
grep -q -- '-m 0' "${MKFS_CALLS}" && pass "authorized: no reserved blocks" || fail "authorized: reserved blocks left on"
|
|
grep -q "mount -t ext4 ${DEV_DIR}/vdc ${DATA_MOUNT}" "${MOUNT_CALLS}" && pass "authorized: mounted" || fail "authorized: not mounted"
|
|
# The engine reads its own datadir, so the mount is handed to the preset's
|
|
# user rather than to whichever engine this script was first written for.
|
|
grep -q "chown ${ENGINE_USER}:${ENGINE_USER} ${DATA_MOUNT}" "${CHOWN_CALLS}" && pass "authorized: handed to the engine user" || fail "authorized: not handed to ${ENGINE_USER}"
|
|
grep -q '^RDS_MASTER_PASSWORD=' "${HANDOFF}" && pass "authorized: bootstrap material preserved" || fail "authorized: bootstrap material removed"
|
|
if grep -q '^RDS_FORMAT_AUTHORIZED=' "${HANDOFF}"; then fail "authorized: grant retained"; else pass "authorized: grant consumed"; fi
|
|
fi
|
|
|
|
# A lone vol* disk with the wrong serial never becomes a fallback candidate, and
|
|
# the absent expected volume is reported as an attach timeout.
|
|
reset_state
|
|
add_disk vda
|
|
add_disk vdb volwrong
|
|
write_handoff vol-expected volexpected 1 true
|
|
run_fails "serial mismatch" "volume vol-expected with serial volexpected was not attached"
|
|
nothing_formatted "serial mismatch"
|
|
|
|
# Selection must not depend on sysfs enumeration order: the expected disk is
|
|
# found even when an unrelated vol* disk sorts after it.
|
|
reset_state
|
|
add_disk vda
|
|
add_disk vdb volexpected
|
|
add_disk vdc volunrelated
|
|
write_handoff vol-expected volexpected 1 true
|
|
if run_ok "unrelated disk sorts last"; then
|
|
grep -q "mkfs.ext4 .*${DEV_DIR}/vdb" "${MKFS_CALLS}" && pass "sort order: expected disk formatted" || fail "sort order: expected disk not formatted"
|
|
grep -q "mount -t ext4 ${DEV_DIR}/vdb ${DATA_MOUNT}" "${MOUNT_CALLS}" && pass "sort order: mounted" || fail "sort order: not mounted"
|
|
fi
|
|
|
|
# Duplicate exact serials are ambiguous even if both name generic data disks.
|
|
reset_state
|
|
add_disk vdb volexpected
|
|
add_disk vdc volexpected
|
|
write_handoff vol-expected volexpected 1 true
|
|
run_fails "duplicate expected serial" "refusing to choose one"
|
|
nothing_formatted "duplicate expected serial"
|
|
|
|
# Existing supported filesystems mount without any grant.
|
|
for fs in ext4 xfs; do
|
|
reset_state
|
|
add_disk vdb volexpected
|
|
printf '%s fs %s\n' "${DEV_DIR}/vdb" "${fs}" > "${PROBE_TABLE}"
|
|
if run_ok "existing ${fs}"; then
|
|
nothing_formatted "existing ${fs}"
|
|
grep -q "mount -t ${fs} ${DEV_DIR}/vdb ${DATA_MOUNT}" "${MOUNT_CALLS}" && pass "existing ${fs}: mounted by type" || fail "existing ${fs}: not mounted"
|
|
fi
|
|
done
|
|
|
|
# Blank restore/start/replacement volumes have no grant and fail closed.
|
|
reset_state
|
|
add_disk vdb volexpected
|
|
run_fails "blank without grant" "no recognizable filesystem and this boot has no matching format authorization"
|
|
nothing_formatted "blank without grant"
|
|
|
|
# Partition tables, visible partitions, unsupported filesystems, ambiguity, and
|
|
# operational probe errors all fail without formatting.
|
|
for kind in pttable fspt unsupported ambiguous unreadable error; do
|
|
reset_state
|
|
add_disk vdb volexpected
|
|
case "${kind}" in
|
|
pttable)
|
|
printf '%s pttable gpt\n' "${DEV_DIR}/vdb" > "${PROBE_TABLE}"
|
|
reason="carries a partition table or partitions" ;;
|
|
fspt)
|
|
printf '%s fspt ext4\n' "${DEV_DIR}/vdb" > "${PROBE_TABLE}"
|
|
reason="carries a partition table or partitions" ;;
|
|
unsupported)
|
|
printf '%s fs btrfs\n' "${DEV_DIR}/vdb" > "${PROBE_TABLE}"
|
|
reason="holds unsupported filesystem btrfs" ;;
|
|
ambiguous)
|
|
printf '%s ambiguous x\n' "${DEV_DIR}/vdb" > "${PROBE_TABLE}"
|
|
reason="ambiguous or conflicting signatures" ;;
|
|
unreadable)
|
|
printf '%s unreadable x\n' "${DEV_DIR}/vdb" > "${PROBE_TABLE}"
|
|
reason="refusing to classify it as blank" ;;
|
|
error)
|
|
printf '%s error x\n' "${DEV_DIR}/vdb" > "${PROBE_TABLE}"
|
|
reason="could not probe" ;;
|
|
esac
|
|
write_handoff vol-expected volexpected 1 true
|
|
run_fails "probe ${kind}" "${reason}"
|
|
nothing_formatted "probe ${kind}"
|
|
done
|
|
|
|
# An uncreatable probe capture leaves blkid unrun, and some shells report that
|
|
# as the blank-device exit 2. Root ignores the directory mode, so it cannot run
|
|
# the case at all.
|
|
if [ "$(id -u)" -ne 0 ]; then
|
|
reset_state
|
|
add_disk vdb volexpected
|
|
write_handoff vol-expected volexpected 1 true
|
|
chmod 0500 "$(dirname "${HANDOFF}")"
|
|
run_fails "unwritable probe capture" "could not create the probe error capture"
|
|
nothing_formatted "unwritable probe capture"
|
|
chmod 0700 "$(dirname "${HANDOFF}")"
|
|
fi
|
|
|
|
reset_state
|
|
add_disk vdb volexpected
|
|
add_partition vdb 1
|
|
printf '%s fs ext4\n' "${DEV_DIR}/vdb" > "${PROBE_TABLE}"
|
|
write_handoff vol-expected volexpected 1 true
|
|
run_fails "sysfs partition plus filesystem" "carries a partition table or partitions"
|
|
nothing_formatted "sysfs partition plus filesystem"
|
|
|
|
# Missing identity and invalid generation cannot authorize even when true.
|
|
for field in id serial generation; do
|
|
reset_state
|
|
add_disk vdb volexpected
|
|
write_handoff vol-expected volexpected 1 true
|
|
case "${field}" in
|
|
id)
|
|
sed -i "s/RDS_DATA_VOLUME_ID='vol-expected'/RDS_DATA_VOLUME_ID=''/" "${HANDOFF}"
|
|
reason="has no RDS_DATA_VOLUME_ID" ;;
|
|
serial)
|
|
sed -i "s/RDS_DATA_VOLUME_SERIAL='volexpected'/RDS_DATA_VOLUME_SERIAL=''/" "${HANDOFF}"
|
|
reason="has no RDS_DATA_VOLUME_SERIAL" ;;
|
|
generation)
|
|
sed -i "s/RDS_VM_GENERATION='1'/RDS_VM_GENERATION='0'/" "${HANDOFF}"
|
|
reason="invalid RDS_VM_GENERATION" ;;
|
|
esac
|
|
run_fails "invalid ${field}" "${reason}"
|
|
nothing_formatted "invalid ${field}"
|
|
done
|
|
|
|
# A failed format retains the one boot's grant for a legitimate retry.
|
|
reset_state
|
|
add_disk vdb volexpected
|
|
write_handoff vol-expected volexpected 1 true
|
|
MKFS_FAIL=1 run_fails "mkfs failure" "mkfs.ext4 failed on"
|
|
grep -q '^RDS_FORMAT_AUTHORIZED=' "${HANDOFF}" && pass "mkfs failure: grant retained" || fail "mkfs failure: grant lost"
|
|
[ ! -s "${MOUNT_CALLS}" ] && pass "mkfs failure: not mounted" || fail "mkfs failure: mounted raw disk"
|
|
|
|
# If mkfs succeeded but the guest died before consuming the line, observing the
|
|
# supported filesystem consumes it without formatting again.
|
|
reset_state
|
|
add_disk vdb volexpected
|
|
printf '%s fs ext4\n' "${DEV_DIR}/vdb" > "${PROBE_TABLE}"
|
|
write_handoff vol-expected volexpected 1 true
|
|
if run_ok "lingering grant on filesystem"; then
|
|
nothing_formatted "lingering grant"
|
|
if grep -q '^RDS_FORMAT_AUTHORIZED=' "${HANDOFF}"; then fail "lingering grant: retained"; else pass "lingering grant: consumed"; fi
|
|
fi
|
|
|
|
# A service restart verifies the mounted source is the expected exact device and
|
|
# removes any lingering authorization.
|
|
reset_state
|
|
add_disk vdb volexpected
|
|
printf '%s %s ext4 rw 0 0\n' "${DEV_DIR}/vdb" "${DATA_MOUNT}" >> "${MOUNTS}"
|
|
write_handoff vol-expected volexpected 1 true
|
|
if run_ok "already mounted exact device"; then
|
|
[ ! -s "${MOUNT_CALLS}" ] && pass "already mounted: no duplicate mount" || fail "already mounted: mounted twice"
|
|
if grep -q '^RDS_FORMAT_AUTHORIZED=' "${HANDOFF}"; then fail "already mounted: grant retained"; else pass "already mounted: grant consumed"; fi
|
|
fi
|
|
|
|
reset_state
|
|
add_disk vdb volexpected
|
|
printf '%s %s ext4 rw 0 0\n' "${DEV_DIR}/wrong" "${DATA_MOUNT}" >> "${MOUNTS}"
|
|
run_fails "already mounted wrong device" "not expected device"
|
|
nothing_formatted "already mounted wrong device"
|
|
|
|
# An image whose service states no layout must not fall back to another engine's
|
|
# paths: a volume mounted where the engine is not looking reads as an empty
|
|
# datadir, and the bootstrap that follows serves an empty database beside the
|
|
# customer's data. Both refusals come before the handoff is even read.
|
|
reset_state
|
|
add_disk vdb volexpected
|
|
write_handoff vol-expected volexpected 1 true
|
|
DATA_MOUNT=""
|
|
run_fails "no mount point" "RDS_DATA_MOUNT is not set"
|
|
nothing_formatted "no mount point"
|
|
|
|
reset_state
|
|
add_disk vdb volexpected
|
|
write_handoff vol-expected volexpected 1 true
|
|
ENGINE_USER=""
|
|
run_fails "no engine user" "RDS_ENGINE_USER is not set"
|
|
nothing_formatted "no engine user"
|
|
|
|
if [ "${FAILS}" -eq 0 ]; then
|
|
echo "PASS: all rds-datadir cases"
|
|
exit 0
|
|
fi
|
|
echo "FAILED: ${FAILS} case(s)"
|
|
exit 1
|