feat(end-of-run): reboot to local disk instead of powering off
CI / Lint + build + test (push) Successful in 1m47s
Release / release (push) Successful in 10m8s

Completed runs now reboot the host and fall through iPXE to the next
boot device (local disk) instead of powering off. Three coordinated
changes:

- pxe/ipxe: NoActiveRunScript exits iPXE (drops to next boot entry)
  instead of `sleep 10; poweroff`. Without this, a Completed reboot
  just loops through PXE and gets told to poweroff.
- api/agent_handlers: heartbeat returns cmd=reboot (was cmd=shutdown)
  when the run reaches Completed.
- agent/runner: runs `systemctl reboot` (with `shutdown -r now`
  fallback) in response to cmd=reboot.

Operator cancel still powers off — powerOffAndReturn is unchanged
because a cancel means the operator wants the host idle so they can
walk up to it, not back in rotation.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-04-19 22:45:11 -04:00
parent 8acef92a60
commit 3656af9823
4 changed files with 23 additions and 17 deletions
+5 -5
View File
@@ -105,10 +105,10 @@ func TestSensorRejectsBadToken(t *testing.T) {
}
}
// TestHeartbeatShutdownWhenCompleted: once the orchestrator has flipped
// TestHeartbeatRebootWhenCompleted: once the orchestrator has flipped
// the run into Completed, the next heartbeat response must carry
// cmd=shutdown so the agent powers the host down.
func TestHeartbeatShutdownWhenCompleted(t *testing.T) {
// cmd=reboot so the agent reboots the host back to local disk.
func TestHeartbeatRebootWhenCompleted(t *testing.T) {
a, runID, token := setupAgent(t)
// Wire a runner so Heartbeat's TouchHeartbeat call doesn't nil-panic.
a.Runner = &orchestrator.Runner{Runs: a.Runs, Hosts: a.Hosts, Stages: &store.Stages{DB: a.Runs.DB}, EventHub: events.NewHub()}
@@ -126,8 +126,8 @@ func TestHeartbeatShutdownWhenCompleted(t *testing.T) {
if err := json.Unmarshal(rr.Body.Bytes(), &resp); err != nil {
t.Fatalf("decode: %v", err)
}
if resp["cmd"] != "shutdown" {
t.Fatalf("cmd = %v, want shutdown", resp["cmd"])
if resp["cmd"] != "reboot" {
t.Fatalf("cmd = %v, want reboot", resp["cmd"])
}
}