docs: REPORT for raw-drive first-class support (agent v0.56-0.58; live-validated)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,70 +1,66 @@
|
||||
# REPORT — Impl-2a: raw-device discovery + registry-sourced drive tracking (agent v0.55.0)
|
||||
# REPORT — raw-drive first-class support (agent v0.55.0 → v0.58.0)
|
||||
|
||||
**Date:** 2026-07-01 · **Repo:** `felhom-agent` (backend only) · **Class:** Risky/supervised (re-sources
|
||||
the drive health/detach path — a regression re-creates the 3b-fix false-detach class). No mkfs here.
|
||||
**Date:** 2026-07-01 · **Repo:** `felhom-agent` · **Class:** Risky/supervised (drive health/enroll path).
|
||||
|
||||
Impl-2a of the drive-enrollment series (SPIKE-drive-enrollment §SQ1/SQ4/SQ5). Makes raw (non-PVE-storage)
|
||||
drives discoverable for enrollment AND health-tracked without being a PVE storage. The controller wizard
|
||||
(Impl-2b) + the shared-box operator gate (Impl-3) are separate.
|
||||
Surfaced by the first live raw-drive enrollment (controller Impl-2b): the agent's enroll + tracking
|
||||
machinery assumed every drive is a PVE storage (visible in `Observe()`). A raw (non-PVE-storage) drive
|
||||
enrolled via the new wizard was therefore mounted+bound but not intent-tracked, not health-tracked, and
|
||||
shown "disconnected". This slice makes raw drives first-class end-to-end.
|
||||
|
||||
## 1. Baseline → target
|
||||
|
||||
felhom-agent `5209830` (v0.54.0) → **v0.55.0**.
|
||||
felhom-agent `91f6a26` (v0.55.0, Impl-2a) → **v0.58.0**. Companion: controller v0.95.0 (Impl-2b).
|
||||
|
||||
## 2. Files changed + commit
|
||||
## 2. Changes (all `internal/localapi/disks.go` + wiring), newest first
|
||||
|
||||
- `internal/storage/candidates.go` (new) — `ListCandidateDisks` (enumerate `/sys/block` whole-disks →
|
||||
Impl-1 `deviceUnclaimed` → probe: size/model/FS/data-bearing/durable-id; mountable ext4/xfs split).
|
||||
- `internal/storage/registry_known.go` (new) — `RegistryKnownTargets` (watchdog `Known()` from intent
|
||||
registry + Felhom `.mount` units) + `ReconcileExistingDrives` (idempotent migration).
|
||||
- `internal/localapi/disks.go` — `GET /disks/candidates` handler + `DiskOps.ListCandidateDisks`;
|
||||
`handleDisks` additive+deduped registry-drive union. `internal/localapi/server.go` — route +
|
||||
`DriveTargets` option.
|
||||
- `cmd/felhom-agent/main.go` — swap watchdog `KnownTargets` source → `RegistryKnownTargets`; run the
|
||||
migration at start; thread `driveKnown` into the local-API.
|
||||
- Tests: `internal/storage/registry_known_test.go`, `internal/localapi/disks_candidates_test.go`,
|
||||
`disks_test.go` (fake). `CHANGELOG.md`, `README.md`, version.
|
||||
- Commit **`91f6a26`** on `main` (parent `066e3bf`). Watchdog/HostLiveness/Remounter unchanged.
|
||||
- **v0.58.0** — the `/disks` registry-union row (Impl-2a) now reports `GuestPath` (`StablePathForRaw`) +
|
||||
`BoundUnderParent` (`boundUnderParent`), like the Observe path — a registry-only drive read as
|
||||
"Leválasztva" despite being bound live. Commit `3825664`.
|
||||
- **v0.57.0** — `ReassertGuestBinds` builds its durable-id→mount map from `Observe()` only → a raw drive's
|
||||
guest-bind was never re-asserted (post-reboot/re-mount). Augment from the mount table (raw `/mnt/<name>`
|
||||
→ device fs-UUID via `HostReader`, skipping the `/mnt/felhom-drives` bind); Observe failure no longer
|
||||
aborts. **Plus a latent wiring fix:** `buildLocalAPIServer` never passed `Options.HostReader`, so
|
||||
`s.host` was nil in production — the v0.56.0 fallback + the role gate's host classification silently
|
||||
no-op'd; now wired to `NewProcHostReader()`. Commits `67e2144` + `6448b80`.
|
||||
- **v0.56.0** — `durableIDForMount` was `Observe()`-only, so a raw drive's enroll/eject **intent** +
|
||||
**guest-bind** went unrecorded ("durable-id unresolved"). Fall back to resolving the mount's device
|
||||
fs-UUID (`HostReader.Mounts`+`ResolveUUID`) → `uuid:<fs-uuid>` (same scheme as Observe). Commit `e593fb2`.
|
||||
|
||||
## 3. Green gate
|
||||
## 3. Green gate + tests
|
||||
|
||||
`go build ./...`, `go vet ./...`, `go test ./...` — clean (local + build server 180).
|
||||
`go build ./...`, `go vet ./...`, `go test ./...` — clean at each version. New tests:
|
||||
`TestDurableIDForMount_RawFallback` (+ red-proof: Observe-only → ""). The `ReassertGuestBinds` +
|
||||
`/disks`-row changes are covered by the live end-to-end (§5).
|
||||
|
||||
## 4. Tests + red-proof
|
||||
## 4. Published + deployed
|
||||
|
||||
- `TestRegistryKnownTargets`: enrolled drive tracked; `new`-intent excluded; ejected still tracked;
|
||||
non-Felhom unit ignored; KnownTarget fields correct (DurableID `uuid:<UUID>`, MountBacked, MountPath).
|
||||
- **`TestRegistryVsObserve_RedProof`:** with NO PVE storage, the OLD `Observer.Known()` returns empty
|
||||
(drive invisible — the 3b-fix class) while `RegistryKnownTargets.Known()` tracks it. Proves the
|
||||
decoupling adds the coverage.
|
||||
- `TestReconcileExistingDrives_Idempotent`: a mounted Felhom-unit drive → enrolled; re-run no-op; an
|
||||
UNMOUNTED unit is NOT auto-migrated.
|
||||
- `TestDiskCandidates_Split`: initialize = all unclaimed; attach = mountable-FS subset. (Candidate
|
||||
fail-safe carries through Impl-1's `classifyClaim`, unit-tested there.)
|
||||
Gitea `felhom-agent/0.58.0` (sha `5ea7f63bb1617fa8412c1977b57369cd184bc1d6c1e6d6cf4f723835ebc06078`);
|
||||
deployed to felhom-pve (backup `.bak-0.57.0`), active. (0.56/0.57 also published; rollback binaries kept.)
|
||||
|
||||
## 5. Deploy + live validation (felhom-pve, agent v0.55.0)
|
||||
## 5. Live validation (felhom-pve + the real controller UI)
|
||||
|
||||
Published Gitea `felhom-agent/0.55.0` (sha `b8122511460413489ae0f5323ad34ebeaa00cfd091e91cba06f00398128bb577`);
|
||||
deployed (backup `.bak-0.54.0`, restart). All four scenarios proven:
|
||||
The raw `/dev/sdd` SD card, enrolled via the wizard (controller Impl-2b), ends up fully first-class:
|
||||
|
||||
| Scenario | Result |
|
||||
|----------|--------|
|
||||
| **C — no false detach** | ✅ post-deploy: 5 storages (Observe intact), both drives mounted, both intents `enrolled`, no detach/error in the journal, 9201 running. Migration a no-op (drives already enrolled). |
|
||||
| **B — registry-sourced tracking (definitive)** | ✅ controlled `systemctl stop mnt-felhom\x2dflash.mount` (app-less drive) → watchdog **detected** `felhom-flash attached→disconnected` (`durable_id=uuid:81a26531…` = the registry key) → **self-healed** (`re-mounting enrolled drive` → gate benign → `ensured mount` → remounted in 5s). Proves the Known set is registry+units-sourced, NOT silently empty. |
|
||||
| **A — discovery** | ✅ `GET /disks/candidates` (real local-API endpoint) returns **only `/dev/sdd`** (size 63.8G, model, fstype ext4, mountable, `durable_id uuid:950d8ad0…`) under both `initialize` and `attach`; **sda/sdb/sdc excluded**. |
|
||||
| **D — fail-safe** | ✅ carried through the reused Impl-1 filter (`classifyClaim` exclude-on-error; unit-tested). |
|
||||
| Property | Evidence |
|
||||
|----------|----------|
|
||||
| Mounted + bound | `/mnt/teszt_enroll` (raw) + `/mnt/felhom-drives/teszt_enroll` (bind) live. |
|
||||
| Intent recorded | `drive-intents.json` → `uuid:f2236136… = enrolled`; journal `drive intent recorded … durable_id=uuid:f2236136…` (no "unresolved" — the v0.56.0 fallback fired once HostReader was wired). |
|
||||
| Guest-bind recorded | `guest-binds.json` → `9201: […, uuid:f2236136…]`; journal `guest-bind recorded`. |
|
||||
| Health-tracked | watchdog (RegistryKnownTargets) detected a drop + self-healed (re-mounted). |
|
||||
| `/disks` status | row reports `state=attached`, `guest_path=/mnt/felhom-drives/teszt_enroll`, `bound_under_parent=true`. |
|
||||
| Controller UI | shows **"Aktív"** (USB), alongside felhom-usb + felhom-flash — all Active, no false detach. |
|
||||
|
||||
## 6. NOT yet done
|
||||
## 6. NOT done / follow-ups
|
||||
|
||||
- **Impl-2b:** the controller wizard rewiring (consume `GET /disks/candidates`) + the enroll
|
||||
orchestration UI + a live end-to-end raw enrollment (format via the Impl-1 guarded path → mount →
|
||||
bind → intent). **Impl-3:** the shared-box operator gate for self-serve format.
|
||||
- The controller settings storage-section screenshot for Scenario C (agent-side evidence above is
|
||||
definitive; the UI still shows the drives via Observe dir-storages + the registry union).
|
||||
- The raw drive's `.mount` unit is named by its UUID (assign sets `MountSpec.Name = uuid`), so the
|
||||
agent-view Name is the UUID; the controller shows its own label ("teszt_enroll"). Cosmetic.
|
||||
- Init slow-format completion is a controller concern (`runStorageInit` should poll
|
||||
`/disks/format/status`) — see controller REPORT.
|
||||
- **Impl-3** — shared-box operator gate for self-serve format.
|
||||
|
||||
## 7. Observations
|
||||
|
||||
- The existing drives are still PVE dir-storages (not removed — later cleanup, per the spike). They now
|
||||
ALSO track via the registry; `/disks` dedups by mount path (no doubles). Retiring the dir-storages is
|
||||
optional and out of scope here.
|
||||
- `/dev/sdd` (the Impl-1 throwaway, now ext4) is a live discovery candidate; unmounted, not enrolled.
|
||||
- Root theme: making a drive first-class without a PVE storage touches every seam that keyed off
|
||||
`Observe()` — candidates (Impl-2a), watchdog Known (Impl-2a), UUID resolution, intent + guest-bind
|
||||
recording, guest-bind re-assert, and the `/disks` status row. All now resolve raw mounts via
|
||||
`HostReader` (mount table + by-uuid), consistent with the `uuid:<fs-uuid>` scheme Observe derives.
|
||||
|
||||
Reference in New Issue
Block a user