Smooth admin-triggered updates

This commit is contained in:
cyph3rasi
2026-03-07 22:29:49 -08:00
parent 830c062fa1
commit a3a5b83f04
3 changed files with 52 additions and 19 deletions
+34 -17
View File
@@ -6,6 +6,7 @@ import signal
import socketserver
import subprocess
import threading
import time
from datetime import datetime, timezone
from http import HTTPStatus
from http.server import BaseHTTPRequestHandler
@@ -67,7 +68,10 @@ def is_authorized(headers):
def watch_process(process):
global current_process
exit_code = process.wait()
with status_lock:
current_process = None
update_status(
status="success" if exit_code == 0 else "error",
message="Synapsis update completed." if exit_code == 0 else "Synapsis update failed.",
@@ -78,6 +82,32 @@ def watch_process(process):
)
def start_update_process():
global current_process
# Give the app enough time to return the 202 response before the container restarts.
time.sleep(2)
with status_lock:
log_handle = open(LOG_FILE, "a", encoding="utf-8")
current_process = subprocess.Popen(
[UPDATE_SCRIPT],
cwd=INSTALL_DIR,
stdout=log_handle,
stderr=subprocess.STDOUT,
start_new_session=True,
env={
**os.environ,
"INSTALL_DIR": INSTALL_DIR,
},
)
update_status(pid=current_process.pid)
thread = threading.Thread(target=watch_process, args=(current_process,), daemon=True)
thread.start()
class ThreadedUnixServer(socketserver.ThreadingMixIn, socketserver.UnixStreamServer):
daemon_threads = True
@@ -133,30 +163,17 @@ class Handler(BaseHTTPRequestHandler):
)
return
log_handle = open(LOG_FILE, "a", encoding="utf-8")
current_process = subprocess.Popen(
[UPDATE_SCRIPT],
cwd=INSTALL_DIR,
stdout=log_handle,
stderr=subprocess.STDOUT,
start_new_session=True,
env={
**os.environ,
"INSTALL_DIR": INSTALL_DIR,
},
)
update_status(
status="updating",
message="Synapsis update started.",
message="Synapsis update scheduled.",
lastStartedAt=now_iso(),
lastFinishedAt=None,
lastExitCode=None,
lastError=None,
pid=current_process.pid,
pid=None,
)
thread = threading.Thread(target=watch_process, args=(current_process,), daemon=True)
thread = threading.Thread(target=start_update_process, daemon=True)
thread.start()
self.send_json(
@@ -164,7 +181,7 @@ class Handler(BaseHTTPRequestHandler):
{
"ok": True,
"status": "updating",
"message": "Synapsis update started.",
"message": "Synapsis update scheduled. The node will restart shortly.",
},
)
+8
View File
@@ -63,11 +63,19 @@ case "${ACTIVE_PROXY}" in
;;
esac
download_file "docker/host-updater.py" "${INSTALL_DIR}/host-updater.py"
download_file "docker/update-local.sh" "${INSTALL_DIR}/update-local.sh"
chmod 755 "${INSTALL_DIR}/host-updater.py" "${INSTALL_DIR}/update-local.sh"
echo "🐳 Pulling latest Synapsis image"
compose_cmd pull
echo "🚀 Restarting Synapsis"
compose_cmd up -d --remove-orphans
if command -v systemctl >/dev/null 2>&1 && systemctl list-unit-files synapsis-updater.service >/dev/null 2>&1; then
systemctl restart synapsis-updater.service >/dev/null 2>&1 || true
fi
echo ""
echo "✅ Synapsis has been updated to the latest published image."
+10 -2
View File
@@ -231,7 +231,7 @@ export default function AdminPage() {
const handleTriggerUpdate = async () => {
setTriggeringUpdate(true);
try {
const res = await fetch('/api/admin/update', { method: 'POST' });
const res = await fetch('/api/admin/update', { method: 'POST', keepalive: true });
const data = await res.json().catch(() => ({}));
if (!res.ok) {
@@ -241,7 +241,15 @@ export default function AdminPage() {
showToast(data.message || 'Update started. Synapsis will restart shortly.', 'success');
await loadUpdateStatus();
} catch (error) {
showToast(error instanceof Error ? error.message : 'Failed to start update', 'error');
const message = error instanceof Error ? error.message : 'Failed to start update';
if (message.toLowerCase().includes('fetch') || message.toLowerCase().includes('network')) {
showToast('Update likely started. The node is restarting, so this page may disconnect briefly.', 'success');
window.setTimeout(() => {
window.location.reload();
}, 5000);
} else {
showToast(message, 'error');
}
} finally {
setTriggeringUpdate(false);
}