agent v0.37.0: host-reboot remount re-resolves enrolled drives by fs-UUID
TASK A — close out the reboot story (agent half). Root cause (pinned live on felhom-pve): an enrolled .mount unit left `disabled` by a prior detach never auto-mounts at boot, and kernel re-enumeration can move a drive's node (/dev/sdb->sdc). Fix re-asserts every enrolled mount by filesystem UUID at startup + on the periodic tick. - ResolveStorageDevice: resolve uuid:<fs-uuid> -> current /dev node via /dev/disk/by-uuid (never a cached node); errors if absent. - parseFelhomMountUnit: pure inverse of renderMountUnit (marker-gated). - (*SudoHostOps).ReassertEnrolledMounts: re-run EnsureMount (enable --now) for any enrolled unit not in /proc/mounts; idempotent, skips mounted/absent. - main.go: runs before ReassertGuestBinds at startup + on the 20s tick. - tests (Linux, seam=device resolution): letter-move tolerated (sdb->sdc) + red-proof companion, absent/scheme rejection, render->parse round-trip. TASK A2 verdict: enrolling a NEW drive does NOT need an LXC restart — the path lands on the live AttachDrive (shared parent, named live slots), not RebootGuest. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -44,7 +44,7 @@ import (
|
||||
|
||||
// version is the agent version. Overridable at build time with
|
||||
// -ldflags "-X main.version=<v>"; defaults to the in-repo CHANGELOG version.
|
||||
var version = "0.36.7"
|
||||
var version = "0.37.0"
|
||||
|
||||
// runGuestHook is the PVE pre-start hook body (`felhom-agent guest-hook <vmid> <phase>`). On the
|
||||
// pre-start phase it creates placeholder dirs for any absent bind-mount source so the guest always boots
|
||||
@@ -506,6 +506,15 @@ func runDaemon(cfg config.Config, logger *slog.Logger) int {
|
||||
go func() { errc <- pbsLoop.Run(ctx) }()
|
||||
if localSrv != nil {
|
||||
localServers = 1
|
||||
// Host-reboot remount fix: BEFORE binding into the guest, re-assert enrolled drive MOUNTS on the
|
||||
// host. A `disabled` mount unit (left so by a prior detach) doesn't auto-mount at boot, and kernel
|
||||
// re-enumeration can move the device (/dev/sdb→sdc); ReassertEnrolledMounts re-resolves each by
|
||||
// filesystem UUID and re-mounts (idempotent `enable --now`) so a letter reshuffle is a no-op.
|
||||
// Type-asserted (the concrete op exposes it; the interface stays lean).
|
||||
mountReasserter, _ := hostOps.(*storage.SudoHostOps)
|
||||
if mountReasserter != nil {
|
||||
mountReasserter.ReassertEnrolledMounts(ctx)
|
||||
}
|
||||
// F9: on startup (the host's bring-up/reconcile trigger), re-assert any enrolled guest data-drive
|
||||
// bind that a re-provision dropped — before serving, so the drive is back in the guest config
|
||||
// (activates on the guest's next reboot). On-durable-id-match; absent/swapped drives are skipped.
|
||||
@@ -523,6 +532,11 @@ func runDaemon(cfg config.Config, logger *slog.Logger) int {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-t.C:
|
||||
// Re-assert mounts first (handles a USB that enumerated late after a host reboot, or a
|
||||
// unit re-disabled at runtime), then re-assert the guest binds against the now-live mounts.
|
||||
if mountReasserter != nil {
|
||||
mountReasserter.ReassertEnrolledMounts(ctx)
|
||||
}
|
||||
localSrv.ReassertGuestBinds(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user