feat(store): api/reaper/offsite/migrate 按版本集合校验 schema,库比二进制新时拒绝启动;bootstrap 迁移前失败回滚宿主二进制
This commit is contained in:
17 files changed
+390
-38
No files matched your search
+45
-1
@@ -331,6 +331,12 @@ PANEL_TLS_CERT="${STATE_DIR}/panel-tls.crt"
|
||||
PANEL_TLS_KEY="${STATE_DIR}/panel-tls.key"
|
||||
SRC_DIR="/opt/felis/src"
|
||||
HOST_BIN="/usr/local/bin/felis"
|
||||
# The binary this run replaced (keep_previous_host_binary), and whether the new one has
|
||||
# been put to use: once migrations start, or the nano service restarts onto it, the old
|
||||
# one no longer matches what is running and a failed run keeps the new one.
|
||||
HOST_BIN_PREV=""
|
||||
HOST_BIN_KEPT=0
|
||||
HOST_BIN_IN_USE=0
|
||||
# Felis's own build toolchain, not /usr/local/go: install_go_toolchain replaces whatever
|
||||
# version sits here, and an operator's Go at the conventional path is not ours to swap.
|
||||
GOROOT_DIR="/opt/felis/go"
|
||||
@@ -437,7 +443,8 @@ on_error() {
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
local id path unit
|
||||
local status=$? id path unit
|
||||
restore_previous_host_binary "$status"
|
||||
for unit in "${PKG_TIMERS_TO_RESTORE[@]-}"; do
|
||||
[ -n "$unit" ] || continue
|
||||
systemctl start "$unit" >/dev/null 2>&1 || true
|
||||
@@ -455,6 +462,36 @@ cleanup() {
|
||||
rm -f -- "$WATCHDOG_QUIET_FILE" 2>/dev/null || true
|
||||
}
|
||||
|
||||
# keep_previous_host_binary copies the felis binary this run is about to replace, once. A
|
||||
# run that fails before the new binary is in use puts it back (restore_previous_host_binary):
|
||||
# until then the old binary, the old cluster and the unmigrated database still agree, while
|
||||
# a new binary left behind runs the host timers against a schema it was not built for, and
|
||||
# `felis setup` with it would migrate the database under the old control plane.
|
||||
keep_previous_host_binary() {
|
||||
[ "$HOST_BIN_KEPT" = 0 ] || return 0
|
||||
HOST_BIN_KEPT=1
|
||||
[ -x "$HOST_BIN" ] || return 0
|
||||
HOST_BIN_PREV="${HOST_BIN}.prev"
|
||||
rm -f "$HOST_BIN_PREV"
|
||||
cp "$HOST_BIN" "$HOST_BIN_PREV"
|
||||
}
|
||||
|
||||
restore_previous_host_binary() { # exit-status
|
||||
[ -n "$HOST_BIN_PREV" ] && [ -f "$HOST_BIN_PREV" ] || return 0
|
||||
if [ "$1" -ne 0 ] && [ "$HOST_BIN_IN_USE" != 1 ]; then
|
||||
# install(1) onto a fresh file, as everywhere else HOST_BIN is written (SELinux label).
|
||||
rm -f "$HOST_BIN"
|
||||
if install -m 0755 "$HOST_BIN_PREV" "$HOST_BIN"; then
|
||||
command -v restorecon >/dev/null 2>&1 && restorecon "$HOST_BIN" >/dev/null 2>&1 || true
|
||||
warn "restored the previous felis binary at ${HOST_BIN}; the database was not migrated, so rerunning the installer picks up where this run stopped"
|
||||
else
|
||||
warn "could not restore the previous felis binary; it is at ${HOST_BIN_PREV}"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
rm -f -- "$HOST_BIN_PREV"
|
||||
}
|
||||
|
||||
remember_temp() { TEMP_PATHS+=("$1"); }
|
||||
remember_container() { DOCKER_CONTAINERS+=("$1"); }
|
||||
|
||||
@@ -1518,6 +1555,7 @@ download_release_binary() {
|
||||
# would carry the source SELinux label instead of type-transitioning to bin_t — see
|
||||
# build_nano_binary for the 203/EXEC this shape avoids. Same-directory staging does not
|
||||
# change that: install(1) still creates the destination and copies.
|
||||
keep_previous_host_binary
|
||||
rm -f "$HOST_BIN"
|
||||
install -m 0755 "$tmp" "$HOST_BIN"
|
||||
rm -f "$tmp"
|
||||
@@ -1652,6 +1690,7 @@ install_embedded_binary() {
|
||||
mkdir -p "$(dirname "$HOST_BIN")"
|
||||
if [ "$(readlink -f "$src")" != "$(readlink -f "$HOST_BIN" 2>/dev/null || true)" ]; then
|
||||
log "installing current felis binary onto the host (${HOST_BIN})"
|
||||
keep_previous_host_binary
|
||||
install -m 0755 "$src" "$HOST_BIN"
|
||||
else
|
||||
ok "host binary already installed at ${HOST_BIN}"
|
||||
@@ -1702,6 +1741,7 @@ build_image_from_source() {
|
||||
local cid
|
||||
cid="$(docker create "$FELIS_IMAGE")"
|
||||
remember_container "$cid"
|
||||
keep_previous_host_binary
|
||||
docker cp "${cid}:/usr/local/bin/felis" "$HOST_BIN"
|
||||
docker rm "$cid" >/dev/null
|
||||
chmod 0755 "$HOST_BIN"
|
||||
@@ -3095,6 +3135,8 @@ run_migrations() {
|
||||
# binary bundles the database into FELIS_DB_BACKUP_DIR first and refuses to migrate
|
||||
# if that fails; a fresh database has nothing to protect and is migrated directly.
|
||||
log "running database migrations (host binary -> 127.0.0.1)"
|
||||
# From here the database may move forward, and the binary that moved it stays.
|
||||
HOST_BIN_IN_USE=1
|
||||
"$HOST_BIN" migrate up -config "${STATE_DIR}/felis.host.toml" "${backup_flags[@]}"
|
||||
ok "migrations applied"
|
||||
}
|
||||
@@ -3814,6 +3856,7 @@ build_nano_binary() {
|
||||
# cannot exec it and felis-nano dies with 203/EXEC. Creating the file fresh at the
|
||||
# destination lets the policy's type transition label it bin_t; restorecon is the belt.
|
||||
mkdir -p "$(dirname "$HOST_BIN")"
|
||||
keep_previous_host_binary
|
||||
rm -f "$HOST_BIN"
|
||||
install -m 0755 "$staged" "$HOST_BIN"
|
||||
rm -f "$staged"
|
||||
@@ -3956,6 +3999,7 @@ EOF
|
||||
systemctl enable felis-nano
|
||||
# restart, not `enable --now`: on a re-run the service is already active and --now would
|
||||
# leave the OLD binary running against the NEW unit. Converge means converge.
|
||||
HOST_BIN_IN_USE=1
|
||||
systemctl restart felis-nano
|
||||
# restart returns as soon as the process is forked. A config the new binary rejects, or a
|
||||
# file it cannot open, only shows once it has exited and the unit sits in auto-restart.
|
||||
|
||||
@@ -2005,6 +2005,37 @@ else
|
||||
echo "FAIL BUILDX_NO_DEFAULT_ATTESTATIONS=1 must be exported before the first docker build"; fails=$((fails + 1))
|
||||
fi
|
||||
|
||||
# --- a failed run puts the previous host binary back until the new one is in use ----------
|
||||
hbdir="$(mktemp -d)"
|
||||
run_host_bin() { # exit-status in-use [no-previous]
|
||||
rm -f "$hbdir"/felis*
|
||||
[ -n "${3:-}" ] || { printf 'old\n' > "$hbdir/felis"; chmod 0755 "$hbdir/felis"; }
|
||||
HOST_BIN="$hbdir/felis" bash -c '
|
||||
set -e
|
||||
warn() { printf "WARN: %s\n" "$*"; }
|
||||
HOST_BIN_PREV=""; HOST_BIN_KEPT=0; HOST_BIN_IN_USE='"$2"'
|
||||
'"$(awk '/^keep_previous_host_binary\(\) \{/,/^}/' "$BS")"'
|
||||
'"$(awk '/^restore_previous_host_binary\(\) \{/,/^}/' "$BS")"'
|
||||
keep_previous_host_binary
|
||||
rm -f "$HOST_BIN"; printf "new\n" > "$HOST_BIN"; chmod 0755 "$HOST_BIN"
|
||||
keep_previous_host_binary # a second replacement keeps the first original
|
||||
restore_previous_host_binary '"$1"'
|
||||
printf "BIN: %s\n" "$(cat "$HOST_BIN")"
|
||||
[ -e "$HOST_BIN.prev" ] && echo "PREV LEFT" || true'
|
||||
}
|
||||
out="$(run_host_bin 1 0)"
|
||||
expect "a run that fails before the new binary is used restores the old one" "BIN: old" "$out"
|
||||
expect "the restore says so" "WARN: restored the previous felis binary" "$out"
|
||||
out="$(run_host_bin 1 1)"
|
||||
expect "a run that fails after migrations keeps the new binary" "BIN: new" "$out"
|
||||
out="$(run_host_bin 0 0)"
|
||||
expect "a successful run keeps the new binary" "BIN: new" "$out"
|
||||
case "$out" in *"PREV LEFT"*) echo "FAIL: the previous binary copy must be removed"; fails=$((fails + 1)) ;; esac
|
||||
out="$(run_host_bin 1 0 fresh)"
|
||||
expect "a first install has nothing to restore" "BIN: new" "$out"
|
||||
case "$out" in *WARN:*) echo "FAIL: a first install must not restore the binary it just installed"; fails=$((fails + 1)) ;; esac
|
||||
rm -rf "$hbdir"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
if [ "$fails" -eq 0 ]; then
|
||||
|
||||
+1
-1
@@ -312,7 +312,7 @@ remove_host_files() {
|
||||
userdel "$VELOCITY_USER" >/dev/null 2>&1 || warn "could not remove the ${VELOCITY_USER} user"
|
||||
fi
|
||||
rm -rf "$OPT_DIR"
|
||||
rm -f "$HOST_BIN" "${HOST_BIN}.new"
|
||||
rm -f "$HOST_BIN" "${HOST_BIN}.new" "${HOST_BIN}.prev"
|
||||
remove_cloudflared_binary
|
||||
if [ "$PURGE" = 0 ]; then
|
||||
# What describes the removed install goes; what a reinstall reuses stays. Without
|
||||
|
||||
Reference in new issue
Block a user