Run apply/rollback in background via systemd-run and add CLI modes

This commit is contained in:
Aaron
2025-12-10 19:55:44 -05:00
parent f9297b68e3
commit b2307e0b0b

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import json, os, subprocess, socket, shutil, pathlib, datetime, tarfile import json, os, subprocess, socket, shutil, pathlib, datetime, tarfile, sys, argparse
from http.server import BaseHTTPRequestHandler, HTTPServer from http.server import BaseHTTPRequestHandler, HTTPServer
import re import re
import urllib.request import urllib.request
@@ -741,6 +741,28 @@ def rollback_update_stub():
return state return state
def start_background_task(mode: str):
"""
Kick off a background update/rollback via systemd-run so nginx/API restarts
do not break the caller connection.
mode: "apply" or "rollback"
"""
assert mode in ("apply", "rollback"), "invalid mode"
unit = f"pikit-update-{mode}"
flag = f"--{mode}-update"
cmd = [
"systemd-run",
"--unit",
unit,
"--quiet",
"/usr/bin/env",
"python3",
str(API_PATH),
flag,
]
subprocess.run(cmd, check=False)
def acquire_lock(): def acquire_lock():
try: try:
ensure_dir(UPDATE_LOCK.parent) ensure_dir(UPDATE_LOCK.parent)
@@ -899,11 +921,20 @@ class Handler(BaseHTTPRequestHandler):
state = check_for_update() state = check_for_update()
return self._send(200, state) return self._send(200, state)
if self.path.startswith("/api/update/apply"): if self.path.startswith("/api/update/apply"):
state = apply_update_stub() # Start background apply to avoid breaking caller during service restart
return self._send(200, state) start_background_task("apply")
state = load_update_state()
state["status"] = "in_progress"
state["message"] = "Starting background apply"
save_update_state(state)
return self._send(202, state)
if self.path.startswith("/api/update/rollback"): if self.path.startswith("/api/update/rollback"):
state = rollback_update_stub() start_background_task("rollback")
return self._send(200, state) state = load_update_state()
state["status"] = "in_progress"
state["message"] = "Starting rollback"
save_update_state(state)
return self._send(202, state)
if self.path.startswith("/api/update/auto"): if self.path.startswith("/api/update/auto"):
state = load_update_state() state = load_update_state()
state["auto_check"] = bool(payload.get("enable")) state["auto_check"] = bool(payload.get("enable"))
@@ -1043,4 +1074,20 @@ def main():
if __name__ == "__main__": if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Pi-Kit API / updater")
parser.add_argument("--apply-update", action="store_true", help="Apply latest release (non-HTTP mode)")
parser.add_argument("--check-update", action="store_true", help="Check for latest release (non-HTTP mode)")
parser.add_argument("--rollback-update", action="store_true", help="Rollback to last backup (non-HTTP mode)")
args = parser.parse_args()
if args.apply_update:
apply_update_stub()
sys.exit(0)
if args.check_update:
check_for_update()
sys.exit(0)
if args.rollback_update:
rollback_update_stub()
sys.exit(0)
main() main()