From 783c79016a4e4cd642411357c010dfc6bcab75b3 Mon Sep 17 00:00:00 2001 From: kisfenyo Date: Mon, 15 Jun 2026 17:51:26 +0200 Subject: [PATCH] controller v0.67.5: startup recreate waits for stack scan recreateBootStaleApps ran before the stack manager finished scanning (GetStacks empty) so it found no apps; add a bounded wait for stacks before the one-time boot-stale recreate. Deterministic guest-reboot convergence. Co-Authored-By: Claude Opus 4.8 (1M context) --- CHANGELOG.md | 6 +++++- controller/internal/web/intermediary.go | 8 ++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41b9249..25c98ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ ## Changelog -### v0.67.4 — gate: startup recreate also recovers exited/restarting drive-backed apps (2026-06-15) +### v0.67.5 — gate: startup recreate waits for stack scan + handles exited apps (2026-06-15) + +Adds a bounded wait for the stack scan (GetStacks is empty at NewServer time, so the recreate found no +apps) before the one-time boot-stale recreate, and recovers exited/restarting/unhealthy drive-backed +apps (not only recently-started). The deterministic guest-reboot convergence. Refines v0.67.3's `recreateBootStaleApps`: recreate a present drive-backed app when it is boot-stale (recently started) OR currently `exited`/`restarting`/`unhealthy` (came up wrong on the empty bind and diff --git a/controller/internal/web/intermediary.go b/controller/internal/web/intermediary.go index ae723b5..7a09613 100644 --- a/controller/internal/web/intermediary.go +++ b/controller/internal/web/intermediary.go @@ -203,6 +203,14 @@ func (s *Server) ReconcileDriveGates() { // startup recreate (see recreateBootStaleApps) to converge a guest reboot deterministically, then the // periodic gate. func (s *Server) driveGateLoop() { + // Wait for the stack scan to complete (GetStacks empty at NewServer time) and the agent to come up, + // so the one-time boot-stale recreate sees the real deployed apps + drive state. Bounded poll. + for i := 0; i < 30; i++ { + if s.stackMgr != nil && len(s.stackMgr.GetStacks()) > 0 { + break + } + time.Sleep(time.Second) + } s.recreateBootStaleApps() s.ReconcileDriveGates() t := time.NewTicker(30 * time.Second)