install: make recovery ladder server-only by default
This commit is contained in:
parent
4bc5264513
commit
0f1fe89138
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -1652,7 +1652,7 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lesavka_client"
|
name = "lesavka_client"
|
||||||
version = "0.22.3"
|
version = "0.22.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-stream",
|
"async-stream",
|
||||||
@ -1686,7 +1686,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lesavka_common"
|
name = "lesavka_common"
|
||||||
version = "0.22.3"
|
version = "0.22.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64",
|
"base64",
|
||||||
@ -1698,7 +1698,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lesavka_server"
|
name = "lesavka_server"
|
||||||
version = "0.22.3"
|
version = "0.22.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64",
|
"base64",
|
||||||
|
|||||||
@ -4,7 +4,7 @@ path = "src/main.rs"
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "lesavka_client"
|
name = "lesavka_client"
|
||||||
version = "0.22.3"
|
version = "0.22.4"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "lesavka_common"
|
name = "lesavka_common"
|
||||||
version = "0.22.3"
|
version = "0.22.4"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,7 @@ ACTION=${1:-recover}
|
|||||||
LOG_PATH=${LESAVKA_RECOVERY_LOG:-/var/log/lesavka/recovery-ladder.log}
|
LOG_PATH=${LESAVKA_RECOVERY_LOG:-/var/log/lesavka/recovery-ladder.log}
|
||||||
LAST_GOOD_DIR=${LESAVKA_RECOVERY_LAST_GOOD_DIR:-/var/lib/lesavka/recovery/last-good}
|
LAST_GOOD_DIR=${LESAVKA_RECOVERY_LAST_GOOD_DIR:-/var/lib/lesavka/recovery/last-good}
|
||||||
CHECK_TIMEOUT_SECONDS=${LESAVKA_RECOVERY_TIMEOUT_SECONDS:-60}
|
CHECK_TIMEOUT_SECONDS=${LESAVKA_RECOVERY_TIMEOUT_SECONDS:-60}
|
||||||
|
ALLOW_UVC_RESTART=${LESAVKA_RECOVERY_ALLOW_UVC_RESTART:-0}
|
||||||
ALLOW_CORE_RESTART=${LESAVKA_RECOVERY_ALLOW_CORE_RESTART:-0}
|
ALLOW_CORE_RESTART=${LESAVKA_RECOVERY_ALLOW_CORE_RESTART:-0}
|
||||||
ALLOW_REBOOT=${LESAVKA_RECOVERY_ALLOW_REBOOT:-0}
|
ALLOW_REBOOT=${LESAVKA_RECOVERY_ALLOW_REBOOT:-0}
|
||||||
SERVER_BIND_ADDR=${LESAVKA_SERVER_BIND_ADDR:-0.0.0.0:50051}
|
SERVER_BIND_ADDR=${LESAVKA_SERVER_BIND_ADDR:-0.0.0.0:50051}
|
||||||
@ -116,6 +117,10 @@ restart_server_only() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
restart_uvc_and_server() {
|
restart_uvc_and_server() {
|
||||||
|
if [[ $ALLOW_UVC_RESTART == 0 || $ALLOW_UVC_RESTART == false || $ALLOW_UVC_RESTART == no ]]; then
|
||||||
|
log "step 2: UVC helper restart disabled; preserving attached USB gadget"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
log "step 2: restarting UVC helper and server"
|
log "step 2: restarting UVC helper and server"
|
||||||
systemctl reset-failed lesavka-uvc.service lesavka-server.service >/dev/null 2>&1 || true
|
systemctl reset-failed lesavka-uvc.service lesavka-server.service >/dev/null 2>&1 || true
|
||||||
systemctl restart lesavka-uvc.service
|
systemctl restart lesavka-uvc.service
|
||||||
@ -171,6 +176,11 @@ recover() {
|
|||||||
|
|
||||||
log "step 3: restoring last-known-good entrypoints"
|
log "step 3: restoring last-known-good entrypoints"
|
||||||
if restore_last_good; then
|
if restore_last_good; then
|
||||||
|
restart_server_only || true
|
||||||
|
if wait_for_health; then
|
||||||
|
snapshot_last_good
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
restart_uvc_and_server || true
|
restart_uvc_and_server || true
|
||||||
if wait_for_health; then
|
if wait_for_health; then
|
||||||
snapshot_last_good
|
snapshot_last_good
|
||||||
|
|||||||
@ -1046,6 +1046,45 @@ install_verified_executable() {
|
|||||||
sudo chmod 0755 "$dest"
|
sudo chmod 0755 "$dest"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pause_recovery_ladder_timer() {
|
||||||
|
sudo systemctl stop lesavka-recovery-ladder.timer >/dev/null 2>&1 || true
|
||||||
|
}
|
||||||
|
|
||||||
|
install_recovery_ladder_units() {
|
||||||
|
cat <<'UNIT' | sudo tee /etc/systemd/system/lesavka-recovery-ladder.service >/dev/null
|
||||||
|
[Unit]
|
||||||
|
Description=lesavka soft recovery ladder
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=/usr/local/bin/lesavka-recovery-ladder recover
|
||||||
|
Environment=LESAVKA_RECOVERY_TIMEOUT_SECONDS=60
|
||||||
|
Environment=LESAVKA_RECOVERY_ALLOW_UVC_RESTART=0
|
||||||
|
Environment=LESAVKA_RECOVERY_ALLOW_CORE_RESTART=0
|
||||||
|
Environment=LESAVKA_RECOVERY_ALLOW_REBOOT=0
|
||||||
|
EnvironmentFile=-/etc/lesavka/server.env
|
||||||
|
EnvironmentFile=-/etc/lesavka/uvc.env
|
||||||
|
UNIT
|
||||||
|
|
||||||
|
cat <<'UNIT' | sudo tee /etc/systemd/system/lesavka-recovery-ladder.timer >/dev/null
|
||||||
|
[Unit]
|
||||||
|
Description=periodically check and softly recover lesavka services
|
||||||
|
|
||||||
|
[Timer]
|
||||||
|
OnBootSec=90s
|
||||||
|
OnUnitActiveSec=60s
|
||||||
|
AccuracySec=10s
|
||||||
|
Persistent=true
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=timers.target
|
||||||
|
UNIT
|
||||||
|
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
sudo systemctl enable lesavka-recovery-ladder.timer
|
||||||
|
}
|
||||||
|
|
||||||
CAPTURE_DISCOVERY_RELAY_PRESENT=0
|
CAPTURE_DISCOVERY_RELAY_PRESENT=0
|
||||||
CAPTURE_DISCOVERY_RELAY_WAS_ACTIVE=0
|
CAPTURE_DISCOVERY_RELAY_WAS_ACTIVE=0
|
||||||
CAPTURE_DISCOVERY_POWER_BORROWED=0
|
CAPTURE_DISCOVERY_POWER_BORROWED=0
|
||||||
@ -1094,6 +1133,7 @@ while [[ $# -gt 0 ]]; do
|
|||||||
done
|
done
|
||||||
echo "==> Using git ref: $REF"
|
echo "==> Using git ref: $REF"
|
||||||
mkdir -p "$TMPDIR"
|
mkdir -p "$TMPDIR"
|
||||||
|
pause_recovery_ladder_timer
|
||||||
|
|
||||||
if [[ -z $REPO_URL ]] && [[ -d $SCRIPT_REPO_ROOT/.git ]]; then
|
if [[ -z $REPO_URL ]] && [[ -d $SCRIPT_REPO_ROOT/.git ]]; then
|
||||||
REPO_URL=$(git -C "$SCRIPT_REPO_ROOT" config --get remote.origin.url || true)
|
REPO_URL=$(git -C "$SCRIPT_REPO_ROOT" config --get remote.origin.url || true)
|
||||||
@ -1236,6 +1276,7 @@ install_verified_executable "$SRC_DIR/scripts/daemon/lesavka-core.sh" /usr/local
|
|||||||
install_verified_executable "$SRC_DIR/scripts/daemon/lesavka-uvc.sh" /usr/local/bin/lesavka-uvc.sh "lesavka-uvc.sh"
|
install_verified_executable "$SRC_DIR/scripts/daemon/lesavka-uvc.sh" /usr/local/bin/lesavka-uvc.sh "lesavka-uvc.sh"
|
||||||
install_verified_executable "$SRC_DIR/scripts/daemon/lesavka-recovery-ladder.sh" /usr/local/bin/lesavka-recovery-ladder "lesavka-recovery-ladder"
|
install_verified_executable "$SRC_DIR/scripts/daemon/lesavka-recovery-ladder.sh" /usr/local/bin/lesavka-recovery-ladder "lesavka-recovery-ladder"
|
||||||
install_verified_executable "$SRC_DIR/scripts/manual/run_uac_output_sanity.sh" /usr/local/bin/lesavka-uac-sanity "lesavka-uac-sanity"
|
install_verified_executable "$SRC_DIR/scripts/manual/run_uac_output_sanity.sh" /usr/local/bin/lesavka-uac-sanity "lesavka-uac-sanity"
|
||||||
|
install_recovery_ladder_units
|
||||||
|
|
||||||
echo "==> 5b. Runtime environment defaults"
|
echo "==> 5b. Runtime environment defaults"
|
||||||
sudo install -d -m 0755 /etc/lesavka
|
sudo install -d -m 0755 /etc/lesavka
|
||||||
@ -1358,8 +1399,10 @@ if [[ "$ATTACHED_UVC_CHANGE_DEFERRED" == "1" ]]; then
|
|||||||
rm -f "$SERVER_ENV_TMP" "$UVC_ENV_TMP"
|
rm -f "$SERVER_ENV_TMP" "$UVC_ENV_TMP"
|
||||||
INSTALLED_VERSION=$(manifest_package_version "$SRC_DIR/server/Cargo.toml" 2>/dev/null || true)
|
INSTALLED_VERSION=$(manifest_package_version "$SRC_DIR/server/Cargo.toml" 2>/dev/null || true)
|
||||||
INSTALLED_SHA=$(git -C "$SCRIPT_REPO_ROOT" rev-parse --short HEAD 2>/dev/null || true)
|
INSTALLED_SHA=$(git -C "$SCRIPT_REPO_ROOT" rev-parse --short HEAD 2>/dev/null || true)
|
||||||
|
sudo systemctl start lesavka-recovery-ladder.timer >/dev/null 2>&1 || true
|
||||||
echo "✅ lesavka-server binaries installed; live service restart deferred for attached-gadget safety."
|
echo "✅ lesavka-server binaries installed; live service restart deferred for attached-gadget safety."
|
||||||
echo "➡️ Installed binaries: lesavka-server ${INSTALLED_VERSION:-unknown}${INSTALLED_SHA:+ ($INSTALLED_SHA)}"
|
echo "➡️ Installed binaries: lesavka-server ${INSTALLED_VERSION:-unknown}${INSTALLED_SHA:+ ($INSTALLED_SHA)}"
|
||||||
|
echo "➡️ Recovery ladder: installed, enabled, and started in server-only mode"
|
||||||
echo "➡️ Deferred UVC codec request: ${INSTALL_UVC_CODEC}"
|
echo "➡️ Deferred UVC codec request: ${INSTALL_UVC_CODEC}"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
@ -1372,8 +1415,10 @@ if [[ "$ATTACHED_UVC_RESTART_DEFERRED" == "1" ]]; then
|
|||||||
echo " Rerun the installer with the same UVC settings to restart services after the env is stable, or use LESAVKA_ALLOW_GADGET_RESET=1 LESAVKA_FORCE_GADGET_REBUILD=1 during a maintenance window." >&2
|
echo " Rerun the installer with the same UVC settings to restart services after the env is stable, or use LESAVKA_ALLOW_GADGET_RESET=1 LESAVKA_FORCE_GADGET_REBUILD=1 during a maintenance window." >&2
|
||||||
INSTALLED_VERSION=$(manifest_package_version "$SRC_DIR/server/Cargo.toml" 2>/dev/null || true)
|
INSTALLED_VERSION=$(manifest_package_version "$SRC_DIR/server/Cargo.toml" 2>/dev/null || true)
|
||||||
INSTALLED_SHA=$(git -C "$SCRIPT_REPO_ROOT" rev-parse --short HEAD 2>/dev/null || true)
|
INSTALLED_SHA=$(git -C "$SCRIPT_REPO_ROOT" rev-parse --short HEAD 2>/dev/null || true)
|
||||||
|
sudo systemctl start lesavka-recovery-ladder.timer >/dev/null 2>&1 || true
|
||||||
echo "✅ lesavka-server binaries and runtime env installed; live service restart deferred for attached-gadget safety."
|
echo "✅ lesavka-server binaries and runtime env installed; live service restart deferred for attached-gadget safety."
|
||||||
echo "➡️ Installed binaries: lesavka-server ${INSTALLED_VERSION:-unknown}${INSTALLED_SHA:+ ($INSTALLED_SHA)}"
|
echo "➡️ Installed binaries: lesavka-server ${INSTALLED_VERSION:-unknown}${INSTALLED_SHA:+ ($INSTALLED_SHA)}"
|
||||||
|
echo "➡️ Recovery ladder: installed, enabled, and started in server-only mode"
|
||||||
echo "➡️ Deferred restart UVC codec: ${INSTALL_UVC_CODEC}"
|
echo "➡️ Deferred restart UVC codec: ${INSTALL_UVC_CODEC}"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
@ -1532,37 +1577,7 @@ sudo rm -f /etc/systemd/system/lesavka-watchdog.timer \
|
|||||||
/etc/lesavka/watchdog.touch
|
/etc/lesavka/watchdog.touch
|
||||||
|
|
||||||
echo "==> 6e. Systemd units - recovery ladder"
|
echo "==> 6e. Systemd units - recovery ladder"
|
||||||
cat <<'UNIT' | sudo tee /etc/systemd/system/lesavka-recovery-ladder.service >/dev/null
|
echo "✅ recovery ladder unit files installed and timer enabled; activation waits for successful service verification."
|
||||||
[Unit]
|
|
||||||
Description=lesavka soft recovery ladder
|
|
||||||
After=network.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=oneshot
|
|
||||||
ExecStart=/usr/local/bin/lesavka-recovery-ladder recover
|
|
||||||
Environment=LESAVKA_RECOVERY_TIMEOUT_SECONDS=60
|
|
||||||
Environment=LESAVKA_RECOVERY_ALLOW_CORE_RESTART=0
|
|
||||||
Environment=LESAVKA_RECOVERY_ALLOW_REBOOT=0
|
|
||||||
EnvironmentFile=-/etc/lesavka/server.env
|
|
||||||
EnvironmentFile=-/etc/lesavka/uvc.env
|
|
||||||
UNIT
|
|
||||||
|
|
||||||
cat <<'UNIT' | sudo tee /etc/systemd/system/lesavka-recovery-ladder.timer >/dev/null
|
|
||||||
[Unit]
|
|
||||||
Description=periodically check and softly recover lesavka services
|
|
||||||
|
|
||||||
[Timer]
|
|
||||||
OnBootSec=90s
|
|
||||||
OnUnitActiveSec=60s
|
|
||||||
AccuracySec=10s
|
|
||||||
Persistent=true
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=timers.target
|
|
||||||
UNIT
|
|
||||||
|
|
||||||
sudo systemctl daemon-reload
|
|
||||||
sudo systemctl enable lesavka-recovery-ladder.timer
|
|
||||||
|
|
||||||
if [[ "$UVC_ENV_CHANGED" == "1" ]] && systemctl is-active --quiet lesavka-uvc; then
|
if [[ "$UVC_ENV_CHANGED" == "1" ]] && systemctl is-active --quiet lesavka-uvc; then
|
||||||
sudo systemctl restart lesavka-uvc
|
sudo systemctl restart lesavka-uvc
|
||||||
|
|||||||
@ -10,7 +10,7 @@ bench = false
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "lesavka_server"
|
name = "lesavka_server"
|
||||||
version = "0.22.3"
|
version = "0.22.4"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
autobins = false
|
autobins = false
|
||||||
|
|
||||||
|
|||||||
@ -126,8 +126,10 @@ fn recovery_ladder_restores_before_rebooting_or_touching_the_core_gadget() {
|
|||||||
"restore_last_good",
|
"restore_last_good",
|
||||||
"restart_server_only",
|
"restart_server_only",
|
||||||
"restart_uvc_and_server",
|
"restart_uvc_and_server",
|
||||||
|
"LESAVKA_RECOVERY_ALLOW_UVC_RESTART:-0",
|
||||||
"LESAVKA_RECOVERY_ALLOW_CORE_RESTART:-0",
|
"LESAVKA_RECOVERY_ALLOW_CORE_RESTART:-0",
|
||||||
"LESAVKA_RECOVERY_ALLOW_REBOOT:-0",
|
"LESAVKA_RECOVERY_ALLOW_REBOOT:-0",
|
||||||
|
"UVC helper restart disabled; preserving attached USB gadget",
|
||||||
"core restart disabled; preserving attached USB gadget",
|
"core restart disabled; preserving attached USB gadget",
|
||||||
"reboot disabled; leaving host online for operator inspection",
|
"reboot disabled; leaving host online for operator inspection",
|
||||||
] {
|
] {
|
||||||
|
|||||||
@ -213,6 +213,10 @@ fn server_install_pins_hdmi_camera_and_display_defaults() {
|
|||||||
SERVER_INSTALL.contains("ATTACHED_UVC_CHANGE_DEFERRED=1"),
|
SERVER_INSTALL.contains("ATTACHED_UVC_CHANGE_DEFERRED=1"),
|
||||||
"attached UVC descriptor/runtime changes should be deferred instead of half-applied"
|
"attached UVC descriptor/runtime changes should be deferred instead of half-applied"
|
||||||
);
|
);
|
||||||
|
assert!(
|
||||||
|
SERVER_INSTALL.contains("pause_recovery_ladder_timer"),
|
||||||
|
"server installs should stop any stale recovery timer before a long build/install cycle"
|
||||||
|
);
|
||||||
assert!(
|
assert!(
|
||||||
SERVER_INSTALL
|
SERVER_INSTALL
|
||||||
.contains("Leaving /etc/lesavka/server.env and /etc/lesavka/uvc.env unchanged"),
|
.contains("Leaving /etc/lesavka/server.env and /etc/lesavka/uvc.env unchanged"),
|
||||||
@ -222,6 +226,11 @@ fn server_install_pins_hdmi_camera_and_display_defaults() {
|
|||||||
SERVER_INSTALL.contains("binaries were installed but running services were left untouched"),
|
SERVER_INSTALL.contains("binaries were installed but running services were left untouched"),
|
||||||
"deferred attached-gadget installs must not restart live services"
|
"deferred attached-gadget installs must not restart live services"
|
||||||
);
|
);
|
||||||
|
assert!(
|
||||||
|
SERVER_INSTALL
|
||||||
|
.contains("Recovery ladder: installed, enabled, and started in server-only mode"),
|
||||||
|
"deferred attached-gadget installs should leave a safe recovery ladder in place"
|
||||||
|
);
|
||||||
assert!(
|
assert!(
|
||||||
SERVER_INSTALL.contains("LIVE_UVC_DESCRIPTOR_MISMATCH"),
|
SERVER_INSTALL.contains("LIVE_UVC_DESCRIPTOR_MISMATCH"),
|
||||||
"installer should catch partial previous runs where env already changed but live descriptors did not"
|
"installer should catch partial previous runs where env already changed but live descriptors did not"
|
||||||
@ -438,6 +447,7 @@ fn server_install_provisions_non_rebooting_recovery_ladder() {
|
|||||||
"lesavka-recovery-ladder.service",
|
"lesavka-recovery-ladder.service",
|
||||||
"lesavka-recovery-ladder.timer",
|
"lesavka-recovery-ladder.timer",
|
||||||
"ExecStart=/usr/local/bin/lesavka-recovery-ladder recover",
|
"ExecStart=/usr/local/bin/lesavka-recovery-ladder recover",
|
||||||
|
"Environment=LESAVKA_RECOVERY_ALLOW_UVC_RESTART=0",
|
||||||
"Environment=LESAVKA_RECOVERY_ALLOW_CORE_RESTART=0",
|
"Environment=LESAVKA_RECOVERY_ALLOW_CORE_RESTART=0",
|
||||||
"Environment=LESAVKA_RECOVERY_ALLOW_REBOOT=0",
|
"Environment=LESAVKA_RECOVERY_ALLOW_REBOOT=0",
|
||||||
"OnUnitActiveSec=60s",
|
"OnUnitActiveSec=60s",
|
||||||
@ -456,6 +466,15 @@ fn server_install_provisions_non_rebooting_recovery_ladder() {
|
|||||||
.unwrap(),
|
.unwrap(),
|
||||||
"last-known-good snapshots should only be refreshed after the installed server is verified"
|
"last-known-good snapshots should only be refreshed after the installed server is verified"
|
||||||
);
|
);
|
||||||
|
assert!(
|
||||||
|
SERVER_INSTALL
|
||||||
|
.find("install_recovery_ladder_units")
|
||||||
|
.unwrap()
|
||||||
|
< SERVER_INSTALL
|
||||||
|
.find("ATTACHED_UVC_CHANGE_DEFERRED=0")
|
||||||
|
.unwrap(),
|
||||||
|
"recovery ladder units should be installed before any attached-gadget deferral can exit"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user