Files

69 lines
3.1 KiB
Bash

# Temporarily deploy BFF/frontend and shorten the role lifetime. Always restore it.
rotation() (
exec 9> "$STATE/bootstrap.lock"
flock -n 9 || fail 'Another local bootstrap or rotation check is running'
vault database/roles/bff | jq '.data' > "$WORK/original-role"
compose ps --services --status running > "$WORK/running"
workers=''
restore() {
touch "$WORK/stop"
for pid in $workers; do wait "$pid" || true; done
restore_failed=0
vault database/roles/bff POST < "$WORK/original-role" >/dev/null || { echo 'ERROR: restore BFF role TTL manually' >&2; restore_failed=1; }
restore_services='frontend bff'
if [ -f "$STATE/sendico/settings.env" ]; then restore_services='frontend sendico bff'; fi
for service in $restore_services; do
if ! grep -qx "$service" "$WORK/running"; then compose stop "$service"; compose rm -f "$service"; fi
done
return "$restore_failed"
}
trap restore EXIT
trap 'exit 130' INT
trap 'exit 143' TERM
jq '.+{default_ttl:"12s",max_ttl:"24s"}' "$WORK/original-role" | vault database/roles/bff POST >/dev/null
recreate_services 90 bff frontend
export_ca
https -f https://localhost:5126/ > "$WORK/index"
grep -q flutter_bootstrap "$WORK/index" || fail 'Frontend bundle is not served'
https -f https://localhost:5126/settings > "$WORK/route"
cmp -s "$WORK/index" "$WORK/route" || fail 'Frontend route fallback failed'
https -f https://localhost:5126/api/v1/health >/dev/null
process=$(compose exec -T bff pidof service)
for worker in 1 2 3 4; do
(
while [ ! -f "$WORK/stop" ]; do
# 43 characters passes syntax validation, executes a Mongo transaction/read,
# and returns invalid-token without creating users or sending email.
token=$(openssl rand -hex 22 | cut -c1-43)
code=$(printf '{"token":"%s"}' "$token" | https -o "$WORK/body-$worker" -w '%{http_code}' \
-H 'Content-Type: application/json' -H 'Origin: https://localhost:5126' \
-H 'X-Auth-Transport: cookie' -H 'X-CSRF-Protection: 1' --data-binary @- \
https://localhost:5126/api/v1/accounts/verification/confirm) || code=0
if [ "$code" = 400 ] && grep -q 'Verification is invalid or already used' "$WORK/body-$worker"; then
echo ok >> "$WORK/results-$worker"
else echo fail >> "$WORK/results-$worker"; fi
sleep 0.03
done
) &
workers="$workers $!"
done
count=0; attempt=0
while [ "$attempt" -lt 40 ]; do
sleep 1
[ "$(compose exec -T bff pidof service)" = "$process" ] || fail 'BFF restarted during rotation'
compose logs --no-color bff > "$WORK/logs"
count=$(grep -c 'Database pool replaced without restarting service' "$WORK/logs" || true)
[ "$count" -lt 3 ] || break
attempt=$((attempt+1))
done
touch "$WORK/stop"
for pid in $workers; do wait "$pid"; done
workers=''
[ "$count" -ge 3 ] || fail 'Three pool rotations were not observed'
cat "$WORK"/results-* > "$WORK/results"
! grep -q fail "$WORK/results" || fail 'Unexpected HTTP responses during rotation'
count=$(wc -l < "$WORK/results")
[ "$count" -ge 100 ] || fail 'Insufficient rotation traffic'
echo "PASS: three real Vault rotations, unchanged BFF PID, $count transaction-backed HTTP requests, zero failures"
)