4d9e76e66a
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
52 lines
3.4 KiB
Markdown
52 lines
3.4 KiB
Markdown
# REPORT — slice 8B (agent half): app-consistent backup policy + status phases (v0.11.0) (2026-06-10)
|
|
|
|
> Overwrite-latest report (most recent significant work only). Cumulative history lives in
|
|
> [CHANGELOG.md](CHANGELOG.md). Implements the host-agent half of `TASK — Slice 8B`. The in-guest
|
|
> controller quiesce loop is in `felhom-controller` v0.36.0. No hub change.
|
|
|
|
## Outcome
|
|
|
|
Turns the 8A thin backup stubs into the real policy the controller's quiesce loop drives. An
|
|
agent-initiated vzdump is crash-consistent only (an LXC has no fsfreeze); the controller now stops
|
|
its app stacks around `POST /backup` (8B) so the captured state is clean-shutdown-consistent —
|
|
**validated live end-to-end** including the postgres clean-vs-crash-recovery restore contrast.
|
|
|
|
## What landed (`internal/localapi`)
|
|
|
|
- **`GET /backup/due`** — real **cadence** policy (replaces the 8A "never backed up" stub): due when
|
|
no **successful** backup is recorded OR the newest one is older than the agent-local cadence
|
|
(`backup.backup_cadence_seconds`, default 24h). A successful `POST /backup` flips due to **false**
|
|
for the window (the controller won't re-quiesce in a loop); a **failed** backup does not satisfy the
|
|
cadence. Returns `age_seconds`.
|
|
- **`GET /backup/status`** — real **phases** `idle | running | done | failed` + the job id, so the
|
|
controller can poll a backup to completion. (The `snapshotted` phase is the 8B.2 hook.)
|
|
- **`POST /backup`** — returns a **job id** + `running`; tracks the in-flight job and is
|
|
**single-flight per guest** (a second POST while one runs returns the same job — no concurrent
|
|
vzdump). On completion the job transitions done/failed and the result is recorded to the store.
|
|
- Config: `backup.backup_cadence_seconds` + `BackupCadence()`; the local-API server takes the cadence.
|
|
- All still self-scoped (token→guest); the snapshot/rollback/storage 8A surface is unchanged.
|
|
|
|
## Tests
|
|
|
|
`go test ./...` green; `-race` green on the build server (pure-Go parts). `/backup/due`: due when
|
|
stale / no backup, **not due within the window after a success**, due again past the cadence, **a
|
|
failed backup does not count**. `/backup/status`: running→done and running→failed (a gated fake
|
|
observes the running phase). `POST /backup` single-flight (one vzdump for concurrent POSTs). Plus the
|
|
8A self-scoping/auth suite unchanged.
|
|
|
|
## Live validation (demo-felhom)
|
|
|
|
A provisioned guest (controller v0.36.0, postgres app stack) with a short test cadence: the
|
|
controller's quiesce loop hit `GET /backup/due` (true) → quiesced → `POST /backup` → the agent ran a
|
|
**real vzdump** (`local-api: backup job complete … archive=vzdump-lxc-8202-…`) → `GET /backup/status`
|
|
`done` → unquiesce. `/backup/due` then correctly went **false** for the cadence window (no re-loop).
|
|
The single-flight + phase transitions behaved against the live server. (The controller's legacy
|
|
hub-report HTTP 401 is the frozen per-customer path, unrelated to 8B.) The standing demo agent was
|
|
upgraded to **v0.11.0** (local-API live on `192.168.0.162:8443`); test guests torn down, cadence reset.
|
|
|
|
## Deferred (stated, not built)
|
|
|
|
**8B.2** downtime optimization (`vzdump --mode snapshot` + a `snapshotted` status phase so the
|
|
controller resumes at snapshot-taken) — needs snapshot-capable storage validated for LXC; the status
|
|
phase enum already carries the hook. Hub-served per-guest backup policy → slice 10. No secrets committed.
|