restore-test: neutralize source bind-mount mountpoints for scratch restore

A slice-10 enrolled guest's data drive is a host bind-mount mp0 that the privsep
token can't vzrestore ("bind mount is only possible for root") — so the restore-test
failed for every enrolled guest regardless of backup tier. The restore-test now reads
the source guest config (vmid from the archive volid) and passes RestoreLXC mp
overrides converting each bind-mount mpN to a throwaway 1G volume on the restore
storage (no root needed; boot-verify doesn't need the data). proxmox.RestoreLXC gains
MountOverrides. + unit tests (archiveVMID, bindMountOverrides).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-12 20:50:35 +02:00
parent 109dd853a3
commit 7fb7ef5c3d
4 changed files with 144 additions and 1 deletions
+10
View File
@@ -34,6 +34,13 @@ type RestoreLXCOptions struct {
Archive string // source archive volid, e.g. "local:backup/vzdump-lxc-9001-...tar.zst"
Storage string // target storage for the rootfs, e.g. "local-lvm"
Force bool // overwrite an existing VMID (destructive — caller must have authority)
// MountOverrides overrides specific mountpoints at restore time (mpN -> full value, e.g.
// "local-lvm:1,mp=/data,backup=0"). A restore param takes precedence over the archive's own
// mpN. The restore-test uses this to neutralize a SOURCE host bind-mount mountpoint (slice-10
// data drive) — vzrestore refuses to restore a bind mount under the privsep token ("restoring
// 'mpN' to bind mount is only possible for root"); replacing it with a throwaway volume needs
// no root and the boot-verify doesn't need the drive's data.
MountOverrides map[string]string
}
// RestoreLXC restores an LXC from a vzdump/PBS archive via POST /nodes/{node}/lxc
@@ -52,6 +59,9 @@ func (c *Client) RestoreLXC(ctx context.Context, opts RestoreLXCOptions) (string
if opts.Force {
v.Set("force", "1")
}
for k, val := range opts.MountOverrides {
v.Set(k, val) // e.g. mp0 -> "local-lvm:1,mp=/data,backup=0" (overrides the archive's mp0)
}
return c.dataString(ctx, http.MethodPost, "/nodes/"+c.node+"/lxc", v)
}