fix(hub): slice-3 follow-ups — /host-report 413 oversize + contract golden (v0.7.1)

- handleHostReport: read maxHostReportBytes+1 (4 MiB const) and reject oversize with
  413 instead of silent LimitReader truncation. Controller handleReport (1 MiB) is
  unchanged. Test asserts 413.
- contract: hub/internal/api/testdata/host-report.golden.json (byte-identical with
  felhom-agent's copy) + TestHostReport_GoldenContract drives the real handler and
  asserts 200 + denorm + both guests upserted.
- CHANGELOG v0.7.1.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-08 18:31:44 +02:00
parent 23611c20ef
commit 4be3bdf486
5 changed files with 144 additions and 5 deletions
+42
View File
@@ -89,3 +89,45 @@ Hub version is the `main.Version` ldflags var (`build.sh <VER>`), default `"dev"
### Repo state
Branch: `main`. Verified `go build/vet/test ./...` green in `hub/` locally (go1.26) and on the
build server (go1.26).
---
## Hub slice-3 follow-ups (v0.7.1) — 2026-06-08
Validation follow-ups (hub half). Pushed to `main`; build/vet/test green locally (go1.26) and on
the build server.
### §3 — `/host-report` rejects oversize with 413 (not silent truncation)
`handleHostReport` now reads `maxHostReportBytes+1` (const `4 << 20`, defined near
`defaultHostPollSeconds`) and returns **`413 Payload too large`** when exceeded, instead of relying
on `LimitReader` truncation (which could accept a truncated-but-valid JSON as a partial report,
dropping guests from the mirror). **Scope-frozen:** the controller `handleReport` 1 MiB read is
**unchanged** (diff touches only the host path); the small divergence is acceptable until cutover.
`TestHandleHostReport_OversizeRejected` now asserts 413.
### §4 — cross-repo contract golden fixture (hub half)
- `hub/internal/api/testdata/host-report.golden.json` — a **byte-identical copy** of felhom-agent's
golden (verified by md5).
- `TestHostReport_GoldenContract` — mints a host, POSTs the golden through the **real**
`handleHostReport`, asserts 200 + denorm (`guest_total=2`, `guest_running=1`,
`cloudflared_status="active"`) + both guests upserted. Proves `hostReportPayload` still extracts
the contract from the real wire shape.
**Caveat (called out):** the two golden files are a *duplicated* contract with no shared source of
truth. JSON can't hold a comment, so the mandatory "keep byte-identical" marker lives in each test
file's doc comment. When slices 5/6 add real `storage_targets`/`backups` fields, promote this to a
shared Go types module (the proper fix); this fixture is the bridge.
### Versioning / scope
Recorded **v0.7.1** in `hub/CHANGELOG.md`. The hub version is the `main.Version` ldflags var
(`build.sh <VER>`, default `"dev"`) — there is no in-repo version constant to bump (the task's
pointer to `web/version.go` is the controller-image `VersionChecker`, unrelated); the image tag is
applied at build/deploy (ArgoCD), not in this task. No deploy performed.
### Untouched (confirmed)
Controller path (`handleReport`/`reports`/`customer_configs`/`checkAuthCustomer`/existing checkers)
unchanged. The agent's proxmox client timeout was a "confirm" item — already bounded (30s default),
no change.
### Repo state
Branch: `main`. Verified `go build/vet/test ./...` green in `hub/` locally (go1.26) and on the build server (go1.26).