hub v0.159.0: a refused whole-guest backup has its own alarm (R-1150); a reinstall re-issues the claim code and the off-site login when the old host is stale or off (R-1154)
R-1150: the agent's localapi:backup-cadence-cap is a separate per-host state in HostCapabilityChecker: alone it raises whole_guest_backup_refused (warning, plain text, agent reason in details) and whole_guest_backup_refusal_cleared (info); other critical caps keep agent_capability_degraded/_recovered unchanged. New types operator-only and hub-origin only (not in allowedEventTypes: the suspected controller must not post them). agent_capability_degraded/_recovered join operatorOnlyEvents (the checker's "never the customer" claim was configuration only). R-1154: handleHostEnroll's reuse path re-issues (claim code + off-site login) when no controller wait is open and either deliberate_off_at is held or both the host report and the controller report are older than alerting.stale_threshold (wired from main); once per silence (host_reenroll_reissued record, also written at mint); a live box gets nothing re-issued and the skip is recorded (host_reenroll_not_reissued). 05 section 8 [DESIGN]. Tests red-proved; go build/vet/test green.
This commit is contained in:
@@ -111,6 +111,17 @@ type Handler struct {
|
||||
// box ticks in seconds instead of ≤15 min. Shared with the web server (same *poke.Notifier). nil
|
||||
// = poke disabled (a no-op; the report cycle still reconciles).
|
||||
poker Poker
|
||||
|
||||
// staleThreshold (R-1154, v0.159.0) is the hub's report-liveness threshold (alerting.stale_threshold — the
|
||||
// same value hostStatus and both staleness checkers read). The host-enroll REUSE path re-issues the
|
||||
// clean-slate credentials only when the old box has been silent past it (reenroll.go). 0 = not wired: the
|
||||
// reuse path then never re-issues (the pre-v0.159.0 behaviour — the fail-safe direction). main wires it.
|
||||
staleThreshold time.Duration
|
||||
}
|
||||
|
||||
// SetStaleThreshold wires the report-liveness threshold the host-enroll reuse path judges by (R-1154).
|
||||
func (h *Handler) SetStaleThreshold(d time.Duration) {
|
||||
h.staleThreshold = d
|
||||
}
|
||||
|
||||
// SetClaimEngine wires the customer-claim code engine (nil-safe everywhere it is used).
|
||||
@@ -1205,7 +1216,10 @@ func (h *Handler) handleHostEnroll(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// Mint-once-reuse: an existing host for this customer is returned as-is (idempotent).
|
||||
// Mint-once-reuse: an existing host for this customer is returned as-is (idempotent) — the SAME host id and
|
||||
// key, so re-running the installer never orphans a live agent's key and a reinstalled box takes over the
|
||||
// record. R-1154 (v0.159.0): when the old box has gone silent (or was switched off on purpose), this
|
||||
// enroll is a reinstall and the clean-slate re-issues run here too — reenroll.go decides.
|
||||
existing, err := h.store.GetHostByCustomer(req.CustomerID)
|
||||
if err != nil {
|
||||
h.logger.Printf("[ERROR] host-enroll: host lookup failed for %s: %v", req.CustomerID, err)
|
||||
@@ -1219,6 +1233,7 @@ func (h *Handler) handleHostEnroll(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "Internal error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
h.reissueOnReuseIfReinstall(cc, existing, time.Now())
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(map[string]string{"host_id": existing.HostID, "api_key": existing.APIKey})
|
||||
@@ -1245,13 +1260,17 @@ func (h *Handler) handleHostEnroll(w http.ResponseWriter, r *http.Request) {
|
||||
h.logger.Printf("[INFO] host enrolled: %s (customer %s)", hostID, req.CustomerID)
|
||||
|
||||
// F2 (v0.57.0) — clean-slate reinstall of an existing customer. This mint path fires exactly
|
||||
// once per fresh host record (the clean-slate flow deletes the stale host, so re-enroll mints),
|
||||
// so it is the natural single-shot re-enroll hook. For a CLAIMED customer the fresh box has no
|
||||
// once per fresh host record (a host deleted before the reinstall), so it is one of the two
|
||||
// single-shot re-enroll hooks — the other is the reuse path above for a silent old host (R-1154).
|
||||
// For a CLAIMED customer the fresh box has no
|
||||
// password; auto-issue a reset code so the customer isn't stranded at the claim page hunting for
|
||||
// the manual "request a new code" button (delivery rides the report ACK). No-op for an unclaimed
|
||||
// customer (first provision). Also re-issue offsite credentials to the fresh box (F3) — the
|
||||
// one-time offsite password only ever reached the OLD controller, so the fresh one has no target.
|
||||
h.reissueOnReenroll(cc)
|
||||
// R-1154: the record that the re-issues ran for this host record — an installer --resume that
|
||||
// enrolls again before the new agent's first report must not re-issue a second time.
|
||||
h.recordReenrollReissued(cc.CustomerID, hostID, "a new host record")
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
|
||||
Reference in New Issue
Block a user