feat: make image-update check cadence configurable and visible (#1377)

* feat: make image-update check cadence configurable and visible

The background image-update scanner polled registries on a hardcoded
6-hour interval, with no way to see when it last ran or when the next
run was due. Operators testing updates read this as auto-update being
unreliable: a manual update checks the registry immediately and applies,
so the slow background scan rarely raised the "update available"
notification before the stack was already current.

Backend:
- ImageUpdateService reads image_update_check_interval_minutes (15-1440,
  default 120) and drives a single generation-guarded self-rescheduling
  timer with 10% per-run jitter so fleet nodes do not poll in lockstep.
  restartPolling() applies a new interval live, with no restart, and
  cannot leave a duplicate timer when a save lands mid-scan.
- GET /api/image-updates/status now returns checking, intervalMinutes,
  lastCheckedAt, nextCheckAt, and the manual-cooldown fields. New
  admin-only PUT /api/image-updates/interval persists the setting and
  reschedules.

Frontend:
- New Settings > Automation > Image update checks section to choose the
  interval (read-only for non-admins; admin enforced on the backend).
- The Auto-Update readiness view shows last-checked, next-check, and a
  ticking manual-recheck cooldown, and the copy distinguishes registry
  detection from scheduled auto-update execution.

Adds backend unit and route tests and frontend component tests, and
updates the auto-update documentation.

* fix: drop stale image-update status response in the readiness strip

loadCadence() ran on mount and again after a Recheck with no request
token, so a slow initial /image-updates/status response could resolve
after the recheck-triggered one and overwrite the fresh cooldown with
stale data, or set state after the view unmounted. Guard setCadence with
a monotonic token mirroring loadReadiness, and bump it on unmount. Adds a
regression test for the out-of-order resolution.
This commit is contained in:
Anso
2026-06-15 20:06:13 -04:00
committed by GitHub
parent 02c3b006eb
commit 058cf8f2c7
16 changed files with 890 additions and 29 deletions
+21
View File
@@ -0,0 +1,21 @@
/**
* Status of a node's image-update scanner, as returned by
* `GET /api/image-updates/status`. Shared by the Settings cadence section and
* the Auto-Update readiness strip so the wire shape lives in one place.
*
* Units differ by field: `intervalMinutes` and `manualCooldownMinutes` are
* minutes; `manualCooldownRemainingMs` is milliseconds (the UI needs ms to tick
* a 1-second countdown). `manualCooldownMinutes` is a fixed ceiling (the
* cooldown window), while `manualCooldownRemainingMs` is the live remaining
* time (0 when a manual refresh is allowed). `lastCheckedAt` / `nextCheckAt`
* are epoch-ms or null ("never checked" / "not scheduled"); `nextCheckAt` is
* ignored while `checking` is true.
*/
export interface ImageUpdateStatus {
checking: boolean;
intervalMinutes: number;
lastCheckedAt: number | null;
nextCheckAt: number | null;
manualCooldownMinutes: number;
manualCooldownRemainingMs: number;
}