v0.246.0: an interrupted restore is told; the recovery-code reminder waits until the box can take it
gates / gates (push) Successful in 15s
gates / gates (push) Successful in 15s
MinAgent: 0.131.0 (unchanged). Requires hub v0.117.0 for restore_interrupted. R-550 (operator ruling: fix). A design reversed and recorded: the restore op-status was in memory by choice. Now restore-status.json in DataDir, written atomically at both ends of an op. At startup a record still marked running becomes a failed, interrupted result kept per app until that app's next restore, shown on /backups/restore and the off-site wizard, and raised once as restore_interrupted. Cooldowns stay in memory. R-546. The R-543 reminder bar consults the agent's own preflight ok (every blocking item, not a copy of pbs_storage_id), cached 60 s, probed only while paused. /backup/escrow shows a waiting card that polls and reloads instead of red crosses and English diagnostics. POST /api/escrow/start refuses 409 before staging or starting - the direct path chaos night used. Unknown readiness keeps the bar. Red-proofs (each seen failing): restore record across restart; main() calls both startup functions; startup helper with loading skipped; restore page card; bar held back; waiting card; start refusal. go build/vet/test ./... green, 28 packages; controller_gates --fast all OK. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -127,6 +127,15 @@ func (s *Server) escrowWizardPageHandler(w http.ResponseWriter, r *http.Request)
|
||||
"OffboxConfigured": s.backupMgr != nil && s.backupMgr.OffboxConfigured(),
|
||||
"AgentSupported": escrowAgentSupported(agentVer),
|
||||
}
|
||||
// R-546: a paused box whose agent says the ceremony cannot run yet gets the waiting card, not a red
|
||||
// checklist and a start form that would refuse. Only for the FIRST ceremony — an escrowed box's
|
||||
// re-ceremony keeps the checklist, where a red row is a real fault to show.
|
||||
if !escrowed {
|
||||
if ready, known := s.escrowReadiness(r.Context(), true); known && !ready {
|
||||
data["EscrowNotReady"] = true
|
||||
data["EscrowNotReadyMessage"] = escrowNotReadyMessage
|
||||
}
|
||||
}
|
||||
s.executeTemplate(w, r, "backups_escrow", data)
|
||||
}
|
||||
|
||||
@@ -188,6 +197,16 @@ func (s *Server) escrowStartAPIHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
s.clearAuthFailures(ip)
|
||||
|
||||
// (1b) R-546: readiness. The page hides the start form while the agent's preflight is red, but a
|
||||
// direct POST (the path chaos night used) ran the ceremony and returned the agent's raw stderr.
|
||||
// Refuse here, BEFORE anything is staged or started. Unknown readiness does not refuse: the
|
||||
// agent-conn and version gates below answer that case, as before.
|
||||
if ready, known := s.escrowReadiness(r.Context(), true); known && !ready {
|
||||
s.logger.Printf("[INFO] [web] escrow start refused: the box is not ready yet (agent preflight not ok)")
|
||||
escrowJSON(w, http.StatusConflict, nil, escrowNotReadyMessage)
|
||||
return
|
||||
}
|
||||
|
||||
// (2) Re-stage-first (only when offsite is configured — Scenario C boxes skip it).
|
||||
if s.backupMgr != nil && s.backupMgr.OffboxConfigured() {
|
||||
if err := s.escrowStage(r.Context()); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user