agent v0.35.1: shared-parent unit runs before pve-guests on host boot

The shared-parent oneshot never ran before pve-guests (WantedBy=multi-user
wasn't pulled into the boot transaction), so on a host reboot the guest bound a
not-yet-shared parent -> private -> propagation broken. Now WantedBy=pve-guests
(pve-guests pulls it in + Before= orders it first); EnsureSharedParent reinstalls
the unit when content differs so the fix deploys.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-15 17:58:52 +02:00
parent 26c6d1e4d1
commit a2374ca679
3 changed files with 19 additions and 6 deletions
+10 -5
View File
@@ -37,9 +37,13 @@ mount --make-shared ` + StableParentDir + `
`
const sharedParentUnitPath = "/etc/systemd/system/felhom-shared-parent.service"
// sharedParentUnit MUST run before pve-guests so the guest's parent bind inherits the shared peer group.
// WantedBy=pve-guests.service makes pve-guests itself PULL IT IN (and Before= orders it first) — a plain
// WantedBy=multi-user.target proved unreliable (the unit wasn't pulled into the boot transaction; it
// never ran before pve-guests). local-fs.target ordering ensures /mnt is available.
const sharedParentUnit = `[Unit]
Description=Felhom stable drive parent (shared bind for live drive hot-swap)
DefaultDependencies=no
After=local-fs.target
Before=pve-guests.service
ConditionPathExists=` + sharedParentScriptPath + `
@@ -50,7 +54,7 @@ RemainAfterExit=yes
ExecStart=` + sharedParentScriptPath + `
[Install]
WantedBy=multi-user.target
WantedBy=pve-guests.service multi-user.target
`
// StablePathForRaw maps a drive's RAW host mount (/mnt/<name>) to its stable in-guest path
@@ -94,9 +98,10 @@ func (b *GuestBinder) EnsureSharedParent(ctx context.Context) error {
if err := b.run(ctx, "mount", "--make-shared", StableParentDir); err != nil {
return fmt.Errorf("shared-parent: make-shared: %w", err)
}
// Install the boot-persistence unit only if it's not already there — EnsureSharedParent runs on a
// periodic reconcile, and re-writing files + daemon-reload every tick would be wasteful.
if _, err := os.Stat(sharedParentUnitPath); err != nil {
// Install the boot-persistence unit only when missing OR its content differs from what we ship (so a
// unit-template fix deploys) — EnsureSharedParent runs on a periodic reconcile, and re-writing files +
// daemon-reload every tick would be wasteful, so the common case (unchanged) is a cheap read.
if cur, err := os.ReadFile(sharedParentUnitPath); err != nil || string(cur) != sharedParentUnit {
if ierr := b.installSharedParentUnit(ctx); ierr != nil {
b.logger.Warn("shared-parent: boot-persistence unit install failed (live setup OK; survives until host reboot)", "err", ierr)
}