a0c0fb114f
CI / Lint + build + test (push) Has been cancelled
vetting-agent gains a `host` subcommand that runs as a systemd service
installed by the quick-register one-liner, POSTing every 30s to
/api/v1/hosts/{mac}/heartbeat so the dashboard tile shows "online" or
"Nm ago" without waiting on WoL. Ships dormant client code for the
Phase 2 reboot_for_vetting command so the server can flip it on later
without a binary redeploy.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
42 lines
1.3 KiB
Go
42 lines
1.3 KiB
Go
package hostmode
|
|
|
|
import (
|
|
"context"
|
|
"log"
|
|
"os/exec"
|
|
)
|
|
|
|
// cmdRebootForVetting is the Phase 2 command the orchestrator sends
|
|
// when the operator clicked "Start vetting" and the host is actively
|
|
// heartbeating — the agent redirects next boot to PXE and reboots
|
|
// itself, obviating WoL.
|
|
const cmdRebootForVetting = "reboot_for_vetting"
|
|
|
|
// handleResponse dispatches on the heartbeat response. Phase 1 never
|
|
// sees a non-empty Cmd (the server omits the field). Phase 2 adds
|
|
// reboot_for_vetting handling.
|
|
func handleResponse(ctx context.Context, resp *heartbeatResponse) {
|
|
if resp == nil || resp.Cmd == "" {
|
|
return
|
|
}
|
|
switch resp.Cmd {
|
|
case cmdRebootForVetting:
|
|
log.Printf("hostmode: orchestrator requested reboot_for_vetting (run=%d)", resp.RunID)
|
|
rebootForVetting(ctx)
|
|
default:
|
|
log.Printf("hostmode: unknown cmd %q, ignoring", resp.Cmd)
|
|
}
|
|
}
|
|
|
|
// rebootForVetting redirects next boot to PXE (best-effort on UEFI
|
|
// via efibootmgr) and triggers a clean reboot. BIOS/legacy hosts
|
|
// typically PXE-boot via DHCP chain on every boot, so efibootmgr
|
|
// missing is non-fatal.
|
|
func rebootForVetting(ctx context.Context) {
|
|
setPXEBootNext(ctx)
|
|
log.Printf("hostmode: executing systemctl reboot")
|
|
if err := exec.CommandContext(ctx, "systemctl", "reboot").Run(); err != nil {
|
|
log.Printf("hostmode: systemctl reboot failed: %v", err)
|
|
}
|
|
}
|