agent v0.34.0: intermediary mount model — shared parent + host-side attach/detach + reconcile

Replaces the per-drive 'pct set -mpN' bind with ONE permanent parent bind
/mnt/felhom-drives plus host-side felhom-data swaps underneath it (propagates
into the running guest live, no pct, no reboot; C1-immune; confined; fail-closed
when absent). EnsureSharedParent installs a boot unit ordered Before=pve-guests.
ReassertGuestBinds is now a pure host-side reconcile. /disks reports GuestPath +
BoundUnderParent for the controller repoint+gate. Non-hollow tests + companions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-15 16:29:32 +02:00
parent 44cdf82631
commit 3a9be73875
11 changed files with 528 additions and 131 deletions
+21
View File
@@ -31,6 +31,13 @@ const (
// mappedRoot is the unprivileged-LXC host uid/gid that maps to the guest's root (spike
// gotcha 1): files chowned to this appear as root:root 0600 inside the guest.
mappedRoot = "100000:100000"
// stableParentDir is the permanent host dir bound once into the guest (intermediary mount model);
// the agent swaps drive felhom-data namespaces underneath it host-side. Mirrors
// localapi.StableParentDir (kept as a literal to avoid a provision→localapi import edge).
stableParentDir = "/mnt/felhom-drives"
// parentBindSlot is the dedicated mpN for the single permanent parent bind. mp8 — high enough to not
// collide with bring-up data mounts (mp0..), below the mp9 bootstrap.
parentBindSlot = "mp8"
)
// BackHalf populates a guest's bootstrap config mount host-side (F3). It mints the per-guest
@@ -155,6 +162,20 @@ func (b *BackHalf) Provision(ctx context.Context, in Input) (Result, error) {
b.logger.Warn("provision: pre-start hook registration failed (non-fatal)", "vmid", in.VMID, "err", err)
}
// 7. Intermediary mount model: add the ONE permanent parent bind so enrolled data drives appear in
// the guest LIVE (the agent later binds each drive's felhom-data UNDER /mnt/felhom-drives host-side
// — no per-drive mp, no reboot). The host SHARED state + boot-persistence unit are the agent
// daemon's job (EnsureSharedParent at startup); here we ensure the dir exists (pct validates the
// bind source) and attach the parent bind. Best-effort + non-fatal.
if err := b.run(ctx, "mkdir", "-p", stableParentDir); err != nil {
b.logger.Warn("provision: stable parent dir create failed (non-fatal)", "vmid", in.VMID, "err", err)
} else {
parentSpec := fmt.Sprintf("%s,mp=%s", stableParentDir, stableParentDir)
if err := b.run(ctx, "pct", "set", strconv.Itoa(in.VMID), "-"+parentBindSlot, parentSpec); err != nil {
b.logger.Warn("provision: parent bind attach failed (non-fatal)", "vmid", in.VMID, "slot", parentBindSlot, "err", err)
}
}
b.logger.Info("provision: back-half complete",
"vmid", in.VMID, "mount", mountKey, "guest_path", guestPath, "endpoint", in.Endpoint)
// tok intentionally goes out of scope here — never logged, never returned.