fix(release): protect backend recovery races
This commit is contained in:
@@ -21,6 +21,94 @@ for remote_block in \
|
||||
' SQ="'" "$SCRIPT" | bash -n
|
||||
done
|
||||
|
||||
extract_remote_block() {
|
||||
local marker="$1"
|
||||
awk -v marker="$marker" '
|
||||
index($0, "<<" SQ marker SQ) { capture = 1; next }
|
||||
capture && $0 == marker { exit }
|
||||
capture { print }
|
||||
' SQ="'" "$SCRIPT"
|
||||
}
|
||||
|
||||
run_remote_block_locally() {
|
||||
local marker="$1"
|
||||
shift
|
||||
{
|
||||
cat <<'FAKE_SYSTEMD'
|
||||
systemctl() {
|
||||
case "$1" in
|
||||
restart) return 0 ;;
|
||||
is-active) printf 'active\n'; return 0 ;;
|
||||
status) return 0 ;;
|
||||
*) return 0 ;;
|
||||
esac
|
||||
}
|
||||
FAKE_SYSTEMD
|
||||
extract_remote_block "$marker"
|
||||
} | bash -s -- "$@"
|
||||
}
|
||||
|
||||
test_tmp_base="${TMPDIR:-/tmp}"
|
||||
test_tmp="$(mktemp -d "$test_tmp_base/release-backend-test.XXXXXX")"
|
||||
test_tmp="$(cd "$test_tmp" && pwd -P)"
|
||||
case "$test_tmp" in
|
||||
"$test_tmp_base"/release-backend-test.*) ;;
|
||||
*)
|
||||
echo "FAIL: unsafe test temp directory: $test_tmp" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
trap 'rm -rf -- "$test_tmp"' EXIT
|
||||
|
||||
mkdir -p "$test_tmp/app" "$test_tmp/backups"
|
||||
target="$test_tmp/app/ruoyi-admin.jar"
|
||||
printf 'old-backend\n' > "$target"
|
||||
old_sha="$(sha256sum "$target" | awk '{print $1}')"
|
||||
candidate="$test_tmp/app/candidate.jar"
|
||||
printf 'candidate-backend\n' > "$candidate"
|
||||
candidate_sha="$(sha256sum "$candidate" | awk '{print $1}')"
|
||||
|
||||
backup_output="$(
|
||||
run_remote_block_locally REMOTE_BACKUP \
|
||||
"$target" "$test_tmp/backups" "wygj-aihr.service" \
|
||||
"$old_sha" "$candidate_sha" \
|
||||
"0000000000000000000000000000000000000000"
|
||||
)"
|
||||
backup_dir="$(sed -n 's/^backup_dir=//p' <<<"$backup_output")"
|
||||
[[ "$backup_dir" == "$test_tmp"/backups/backend-* ]]
|
||||
[[ "$(sha256sum "$backup_dir/ruoyi-admin.jar" | awk '{print $1}')" == "$old_sha" ]]
|
||||
[[ -f "$backup_dir/release-manifest.txt" ]]
|
||||
|
||||
run_remote_block_locally REMOTE_ACTIVATE \
|
||||
"$target" "$candidate" "wygj-aihr.service" "$old_sha" "$candidate_sha"
|
||||
[[ "$(sha256sum "$target" | awk '{print $1}')" == "$candidate_sha" ]]
|
||||
[[ ! -e "$candidate" ]]
|
||||
|
||||
run_remote_block_locally REMOTE_RESTORE \
|
||||
"$backup_dir/ruoyi-admin.jar" "$target" "wygj-aihr.service" \
|
||||
"$old_sha" "$candidate_sha"
|
||||
[[ "$(sha256sum "$target" | awk '{print $1}')" == "$old_sha" ]]
|
||||
|
||||
printf 'concurrent-backend\n' > "$target"
|
||||
concurrent_sha="$(sha256sum "$target" | awk '{print $1}')"
|
||||
if run_remote_block_locally REMOTE_RESTORE \
|
||||
"$backup_dir/ruoyi-admin.jar" "$target" "wygj-aihr.service" \
|
||||
"$old_sha" "$candidate_sha"; then
|
||||
echo 'FAIL: restore accepted a concurrently changed target' >&2
|
||||
exit 1
|
||||
fi
|
||||
[[ "$(sha256sum "$target" | awk '{print $1}')" == "$concurrent_sha" ]]
|
||||
|
||||
safety_output="$(
|
||||
run_remote_block_locally REMOTE_ROLLBACK_SAFETY \
|
||||
"$target" "$test_tmp/backups" "wygj-aihr.service" \
|
||||
"$concurrent_sha" "$old_sha"
|
||||
)"
|
||||
safety_dir="$(sed -n 's/^safety_dir=//p' <<<"$safety_output")"
|
||||
[[ "$safety_dir" == "$test_tmp"/backups/backend-rollback-safety-* ]]
|
||||
[[ "$(sha256sum "$safety_dir/ruoyi-admin.jar" | awk '{print $1}')" == "$concurrent_sha" ]]
|
||||
[[ -f "$safety_dir/rollback-manifest.txt" ]]
|
||||
|
||||
grep -Fq 'REMOTE_SSH="YCWY"' "$SCRIPT"
|
||||
grep -Fq 'REMOTE_PATH="/opt/wygj/app/ruoyi-admin.jar"' "$SCRIPT"
|
||||
grep -Fq 'REMOTE_SERVICE="wygj-aihr.service"' "$SCRIPT"
|
||||
|
||||
Reference in New Issue
Block a user