install.sh (#14): after enrolling it now BLOCKS until an admin approves, printing "Please have an admin approve node '<id>' at <app>/settings/nodes to continue", polling GET /nodes/{id}/credentials with the pickup secret (no default timeout; Ctrl-C safe; re-run resumes from configs/pickup_secret). A re-run with a key on disk re-checks it against C2 and replaces a stale (reissued) key. credentials.json is merged atomically so the dashboard auth salt/secret survive. edge-node is ALWAYS force-recreated at the end, so a key written after the container started is loaded. --wait-approval N caps the wait; 0 = don't wait. git fetch uses --force so a re-cut v1 tag is accepted. edge-node (#14): with no key at startup, or on a broker auth refusal (CONNACK 4/5/134/135), mqtt_manager re-reads credentials.json, else fetches the key over HTTPS (key_pickup.py, pickup secret from /configs), persists it and rebuilds the paho client in place. Single-flight, 30s..15min backoff. A key pushed over MQTT while connected is applied for the next reconnect. OTA (#15): node_update no longer os._exit()s. It drops configs/update-requested; install.sh installs scripts/install-updater.sh's drb-update.{service,timer,path}. The path unit fires on the sentinel, the timer (~15 min) does an HTTPS check-in (POST /nodes/{id}/update-check, server-26 PR) so offline/locked-out nodes still update. drb-update.sh only installs the repo/ref/registry in root-owned /etc/drb/updater.conf; C2's answer is just a timestamp; origin and registry mismatches abort; creds go to curl via a 0600 header file. Tests: test_credential_recovery.py, test_host_updater.py (runs the real updater script with stub git/docker/curl). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
6bf2c9dd93
commit
c2c349043b
+10
-11
@@ -170,17 +170,15 @@ async def on_command(payload: dict):
|
||||
await asyncio.sleep(2)
|
||||
await op25_client.start()
|
||||
elif action == "node_update":
|
||||
# TODO: Full OTA update — register a host-level systemd service (e.g. drb-update.service)
|
||||
# that stops all DRB containers, runs `docker compose pull`, then `docker compose up -d`.
|
||||
# The C2 server triggers it by sending this MQTT command; the host service watches for the
|
||||
# restart signal (e.g. via a Unix socket, a sentinel file, or a lightweight webhook).
|
||||
# Not implemented yet — for now, just restart the container so any pre-pulled image
|
||||
# is picked up (requires a prior `docker compose pull` on the host).
|
||||
logger.info("Node update requested — restarting container to pick up latest image.")
|
||||
await mqtt_manager.publish_status("offline")
|
||||
await asyncio.sleep(1)
|
||||
import os
|
||||
os._exit(0) # Docker restart=unless-stopped will bring the container back up
|
||||
# node-26#15: the container cannot (and must not) update itself. It
|
||||
# drops a sentinel in /configs; the host's drb-update.path unit
|
||||
# (installed by install.sh) sees it and runs drb-update.service, which
|
||||
# pulls the node's own pinned repo ref + images and recreates the
|
||||
# stack. The sentinel's content is ignored by the host — C2 cannot
|
||||
# choose what gets installed. Offline/locked-out nodes get the same
|
||||
# request over HTTPS via the updater's timer instead.
|
||||
from app.internal.update_request import request_host_update
|
||||
request_host_update()
|
||||
else:
|
||||
logger.warning(f"Unknown command: {action}")
|
||||
|
||||
@@ -189,6 +187,7 @@ async def on_api_key(payload: dict):
|
||||
key = payload.get("api_key")
|
||||
if key:
|
||||
credentials.save_api_key(key)
|
||||
mqtt_manager.update_api_key(key)
|
||||
logger.info("Node API key received and saved.")
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user