mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-19 10:33:29 +00:00
Verify Pulse restarts after an interactive update; don't leave it silently stopped (#1323)
Finishes the deferred half of the v5->v6 parity fix for #1323 (the
pulse-auto-update.sh half landed in 672e81985). The interactive install.sh
update/reinstall flow stopped a running Pulse then called start_pulse, which
tolerates a silent start failure (common on unprivileged LXC) by printing a note
and returning 0 — leaving Pulse stopped under an "installation completed!" message.
- stop_pulse_for_update records whether Pulse was running before the update.
- start_pulse, only when Pulse was running before (PULSE_WAS_ACTIVE), no longer
accepts a silent start failure: it verifies the service became active
(wait_for_service_active, 20s), retries one explicit start, and surfaces a clear
error + diagnostics if it still will not come up. Fresh installs are unchanged
(the flag stays false, so the reassuring container note is kept).
- Wired into all three update/reinstall sites; added a BASH_SOURCE guard so the
installer's functions can be unit-tested without running the installer.
Scope: fixes the #1323 'restart silently failed' case. Does NOT add a binary
rollback (download_pulse deletes bin/pulse.old right after the swap) — a
bad-release rollback is a separate concern.
Test: scripts/tests/test-install-update-resilience.sh (sources install.sh, stubs
systemctl, asserts was-active capture + retry + clear error). Go installtests +
bash -n confirm the guard does not change installer execution.
This commit is contained in:
parent
5321c8b7a2
commit
1a42011724
2 changed files with 141 additions and 5 deletions
75
install.sh
75
install.sh
|
|
@ -4038,6 +4038,61 @@ EOF
|
||||||
safe_systemctl daemon-reload
|
safe_systemctl daemon-reload
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Tracks whether Pulse was running before an update-time stop, so start_pulse can
|
||||||
|
# guarantee it comes back up afterward instead of silently leaving it stopped
|
||||||
|
# (#1323: on unprivileged LXC the installer's restart can silently fail).
|
||||||
|
PULSE_WAS_ACTIVE="false"
|
||||||
|
|
||||||
|
# Poll until the service is active, up to timeout_seconds (default 20).
|
||||||
|
wait_for_service_active() {
|
||||||
|
local service_name=$1
|
||||||
|
local timeout_seconds="${2:-20}"
|
||||||
|
local elapsed=0
|
||||||
|
while (( elapsed < timeout_seconds )); do
|
||||||
|
if timeout 5 systemctl is-active --quiet "$service_name" 2>/dev/null; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
((elapsed += 1))
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Stop Pulse for an update, remembering whether it was running so start_pulse can
|
||||||
|
# guarantee it comes back up afterward (#1323).
|
||||||
|
stop_pulse_for_update() {
|
||||||
|
PULSE_WAS_ACTIVE="false"
|
||||||
|
if timeout 5 systemctl is-active --quiet "$SERVICE_NAME" 2>/dev/null; then
|
||||||
|
PULSE_WAS_ACTIVE="true"
|
||||||
|
fi
|
||||||
|
systemctl stop "$SERVICE_NAME" 2>/dev/null || true
|
||||||
|
}
|
||||||
|
|
||||||
|
# After an update, if Pulse was running beforehand, make sure it is running again:
|
||||||
|
# retry one explicit start, and surface a clear error if it still will not come up
|
||||||
|
# — rather than printing a success completion over a stopped service (#1323).
|
||||||
|
ensure_pulse_running_after_update() {
|
||||||
|
[[ "$PULSE_WAS_ACTIVE" == "true" ]] || return 0
|
||||||
|
PULSE_WAS_ACTIVE="false"
|
||||||
|
|
||||||
|
if wait_for_service_active "$SERVICE_NAME" 20; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_warn "Pulse did not come back up after the update; retrying start..."
|
||||||
|
safe_systemctl start "$SERVICE_NAME" || true
|
||||||
|
if wait_for_service_active "$SERVICE_NAME" 20; then
|
||||||
|
print_success "Pulse is running again."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_error "Pulse was running before the update but did not come back up (#1323)."
|
||||||
|
print_info "The new binary is installed; investigate why the service will not start:"
|
||||||
|
print_info " systemctl status $SERVICE_NAME"
|
||||||
|
print_info " journalctl -u $SERVICE_NAME -n 50"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
start_pulse() {
|
start_pulse() {
|
||||||
print_info "Starting Pulse..."
|
print_info "Starting Pulse..."
|
||||||
|
|
||||||
|
|
@ -4048,8 +4103,12 @@ start_pulse() {
|
||||||
|
|
||||||
if ! safe_systemctl start $SERVICE_NAME; then
|
if ! safe_systemctl start $SERVICE_NAME; then
|
||||||
print_info "Note: systemctl start failed (common in unprivileged containers)"
|
print_info "Note: systemctl start failed (common in unprivileged containers)"
|
||||||
print_info "The service will start automatically when the container starts"
|
if [[ "$PULSE_WAS_ACTIVE" != "true" ]]; then
|
||||||
return 0
|
print_info "The service will start automatically when the container starts"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
# An update stopped a running Pulse; do not accept a silent start failure
|
||||||
|
# here — fall through to the active-state verification below (#1323).
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Wait for service to start
|
# Wait for service to start
|
||||||
|
|
@ -4063,6 +4122,8 @@ start_pulse() {
|
||||||
# Don't exit, just warn
|
# Don't exit, just warn
|
||||||
print_info "Service may not be running. You might need to start it manually."
|
print_info "Service may not be running. You might need to start it manually."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
ensure_pulse_running_after_update
|
||||||
}
|
}
|
||||||
|
|
||||||
create_marker_file() {
|
create_marker_file() {
|
||||||
|
|
@ -4284,7 +4345,7 @@ main() {
|
||||||
fi
|
fi
|
||||||
|
|
||||||
backup_existing
|
backup_existing
|
||||||
systemctl stop $SERVICE_NAME || true
|
stop_pulse_for_update
|
||||||
create_user
|
create_user
|
||||||
download_pulse
|
download_pulse
|
||||||
setup_update_command
|
setup_update_command
|
||||||
|
|
@ -4484,7 +4545,7 @@ main() {
|
||||||
fi
|
fi
|
||||||
|
|
||||||
backup_existing
|
backup_existing
|
||||||
systemctl stop $SERVICE_NAME || true
|
stop_pulse_for_update
|
||||||
create_user
|
create_user
|
||||||
download_pulse
|
download_pulse
|
||||||
setup_update_command
|
setup_update_command
|
||||||
|
|
@ -4539,7 +4600,7 @@ main() {
|
||||||
fi
|
fi
|
||||||
|
|
||||||
backup_existing
|
backup_existing
|
||||||
systemctl stop $SERVICE_NAME || true
|
stop_pulse_for_update
|
||||||
create_user
|
create_user
|
||||||
download_pulse
|
download_pulse
|
||||||
setup_directories
|
setup_directories
|
||||||
|
|
@ -4766,6 +4827,10 @@ reset_pulse() {
|
||||||
exit 0
|
exit 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# When sourced (e.g. by tests) rather than executed, define the functions above
|
||||||
|
# but do not run the installer.
|
||||||
|
[[ "${BASH_SOURCE[0]}" == "$0" ]] || return 0
|
||||||
|
|
||||||
# Parse command line arguments
|
# Parse command line arguments
|
||||||
while [[ $# -gt 0 ]]; do
|
while [[ $# -gt 0 ]]; do
|
||||||
case $1 in
|
case $1 in
|
||||||
|
|
|
||||||
71
scripts/tests/test-install-update-resilience.sh
Executable file
71
scripts/tests/test-install-update-resilience.sh
Executable file
|
|
@ -0,0 +1,71 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# Tests the interactive-update service-resilience helpers in install.sh (#1323):
|
||||||
|
# after an update that stopped a running Pulse, the installer must verify the
|
||||||
|
# service came back up, retry one explicit start, and surface a clear error
|
||||||
|
# instead of silently leaving Pulse stopped (common on unprivileged LXC where
|
||||||
|
# the installer's restart silently fails).
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
INSTALL_SCRIPT="${ROOT_DIR}/install.sh"
|
||||||
|
|
||||||
|
# Sourcing relies on the BASH_SOURCE guard so main() does not run on import.
|
||||||
|
source "${INSTALL_SCRIPT}"
|
||||||
|
set +e # this test drives helpers that intentionally return non-zero
|
||||||
|
|
||||||
|
SERVICE_NAME="pulse"
|
||||||
|
|
||||||
|
# --- Deterministic stubs: no real sleeps, no real systemctl/timeout ---------
|
||||||
|
sleep() { :; }
|
||||||
|
timeout() { shift; "$@"; } # drop the duration; run the wrapped (stubbed) command
|
||||||
|
|
||||||
|
SYSTEMCTL_ACTIVE="no"
|
||||||
|
START_ATTEMPTS=0
|
||||||
|
systemctl() {
|
||||||
|
case "$1" in
|
||||||
|
is-active) [[ "$SYSTEMCTL_ACTIVE" == "yes" ]] && return 0 || return 1 ;;
|
||||||
|
stop) SYSTEMCTL_ACTIVE="no"; return 0 ;;
|
||||||
|
start) START_ATTEMPTS=$((START_ATTEMPTS + 1)); return 0 ;;
|
||||||
|
*) return 0 ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
safe_systemctl() { systemctl "$@"; }
|
||||||
|
|
||||||
|
PRINT_BUF=""
|
||||||
|
print_info() { PRINT_BUF+="INFO:$*"$'\n'; }
|
||||||
|
print_warn() { PRINT_BUF+="WARN:$*"$'\n'; }
|
||||||
|
print_error() { PRINT_BUF+="ERROR:$*"$'\n'; }
|
||||||
|
print_success() { PRINT_BUF+="OK:$*"$'\n'; }
|
||||||
|
|
||||||
|
fail() { echo "FAIL: $*" >&2; exit 1; }
|
||||||
|
|
||||||
|
# --- Case 1: stop_pulse_for_update records whether Pulse was running --------
|
||||||
|
SYSTEMCTL_ACTIVE="yes"; PULSE_WAS_ACTIVE="false"
|
||||||
|
stop_pulse_for_update
|
||||||
|
[[ "$PULSE_WAS_ACTIVE" == "true" ]] || fail "should record was-active=true when running"
|
||||||
|
[[ "$SYSTEMCTL_ACTIVE" == "no" ]] || fail "should stop the service"
|
||||||
|
|
||||||
|
SYSTEMCTL_ACTIVE="no"; PULSE_WAS_ACTIVE="true"
|
||||||
|
stop_pulse_for_update
|
||||||
|
[[ "$PULSE_WAS_ACTIVE" == "false" ]] || fail "should record was-active=false when not running"
|
||||||
|
|
||||||
|
# --- Case 2: no-op when Pulse was not running before the update ------------
|
||||||
|
PULSE_WAS_ACTIVE="false"; PRINT_BUF=""
|
||||||
|
ensure_pulse_running_after_update || fail "should no-op (succeed) when was-active=false"
|
||||||
|
[[ -z "$PRINT_BUF" ]] || fail "should be silent when was-active=false, got: $PRINT_BUF"
|
||||||
|
|
||||||
|
# --- Case 3: service comes back up -> success, flag consumed ----------------
|
||||||
|
PULSE_WAS_ACTIVE="true"; SYSTEMCTL_ACTIVE="yes"; PRINT_BUF=""
|
||||||
|
ensure_pulse_running_after_update || fail "should succeed when the service is active"
|
||||||
|
[[ "$PULSE_WAS_ACTIVE" == "false" ]] || fail "should consume the was-active flag"
|
||||||
|
|
||||||
|
# --- Case 4: service stays down -> retries once, then a clear error ---------
|
||||||
|
PULSE_WAS_ACTIVE="true"; SYSTEMCTL_ACTIVE="no"; START_ATTEMPTS=0; PRINT_BUF=""
|
||||||
|
if ensure_pulse_running_after_update; then
|
||||||
|
fail "should return non-zero when the service will not come up"
|
||||||
|
fi
|
||||||
|
[[ "$START_ATTEMPTS" -ge 1 ]] || fail "should attempt an explicit restart, got $START_ATTEMPTS"
|
||||||
|
[[ "$PRINT_BUF" == *"did not come back up"* ]] || fail "should surface a clear error, got: $PRINT_BUF"
|
||||||
|
|
||||||
|
echo "PASS: install.sh update-resilience helpers (#1323)"
|
||||||
Loading…
Add table
Add a link
Reference in a new issue