v0.53.0: restore guests INTO the felhom pool (pool-scoped-ACL enabler)
RestoreLXCOptions.Pool → pct restore --pool (omit-when-empty). New reconcile.DefaultPool="felhom"; BringUpSpec.Pool threaded to the bring-up restore; BOTH restore sites pool the guest (provision/DR via spec.Pool set to DefaultPool by the CLI; restore-test scratch via DefaultPool = SPIKE residual #2). No agent ACL change (ships in host-install v1.6.0); the pool param is inert until the token has Pool.Allocate + the pool exists, so publishing is safe ahead of the coordinated swap. Tests + red-proofs; go build/vet/test clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -43,6 +43,12 @@ const bringUpKind = "bring_up"
|
||||
// DefaultDataVolMount is the mpN slot the golden bakes the Docker-data volume (/var/lib/docker) at.
|
||||
const DefaultDataVolMount = "mp0"
|
||||
|
||||
// DefaultPool is the PVE pool every Felhom-managed guest is restored INTO. Under the pool-scoped agent
|
||||
// token, restore-into-pool is how a fresh vmid gets allocated (VM.Allocate + Pool.Allocate at
|
||||
// /pool/felhom) and how the guest becomes reachable by the scoped token (SPIKE-pool-scoped-acl-2026-07-01).
|
||||
// Single source of truth for both restore sites (provision bring-up + restore-test).
|
||||
const DefaultPool = "felhom"
|
||||
|
||||
// DefaultSysDataMount is the mpN slot the golden bakes the SSD user-data volume (/mnt/sys_drive) at.
|
||||
// This is the controller's system_data_path; provision grows it (SysDataGrowGB) like the Docker-data
|
||||
// volume. mp1 is the natural next bring-up slot (mp8/mp9 are added by the provision back-half).
|
||||
@@ -76,6 +82,7 @@ type BringUpSpec struct {
|
||||
VMID int // caller-provided target VMID (NOT the restore-test band / 9999)
|
||||
RestoreStorage string // rootfs target storage
|
||||
Hostname string // hostname to set (provision); ignored for DR (continuity)
|
||||
Pool string // restore the guest INTO this PVE pool ("" = none); required under a pool-scoped token
|
||||
Cores int // 0 = leave as restored
|
||||
MemoryMB int // 0 = leave as restored
|
||||
RootfsGrowGB int // optional grow-only rootfs resize (0 = skip)
|
||||
@@ -200,7 +207,7 @@ func (e *Engine) runBringUp(ctx context.Context, spec BringUpSpec, res *BringUpR
|
||||
|
||||
// 1. Restore archive → VMID (token-covered ClassCreate; keyctl preserved — phase3 + spike).
|
||||
upid, err := e.api.RestoreLXC(ctx, proxmox.RestoreLXCOptions{
|
||||
VMID: spec.VMID, Archive: spec.Archive, Storage: spec.RestoreStorage,
|
||||
VMID: spec.VMID, Archive: spec.Archive, Storage: spec.RestoreStorage, Pool: spec.Pool,
|
||||
})
|
||||
if err != nil {
|
||||
res.Err = fmt.Errorf("reconcile: bring-up restore: %w", err)
|
||||
|
||||
@@ -119,6 +119,42 @@ func TestBuildBringUpConfig_ResourceCaps(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Both restore sites allocate the guest INTO the felhom pool (SPIKE 3b): the provision bring-up
|
||||
// threads spec.Pool, and the restore-test hardcodes DefaultPool — else a pool-scoped token 403s on
|
||||
// the created guest's config/start/destroy. Asserts via the fakeAPI's captured RestoreLXCOptions.
|
||||
func TestRestoreSitesUsePool(t *testing.T) {
|
||||
// provision bring-up: spec.Pool must flow to the restore
|
||||
const vmid = 8060
|
||||
api := &fakeAPI{cfg: map[int]proxmox.GuestConfig{vmid: scratchCfg()}}
|
||||
e, _, q := newEngine(t, api, EmptyProvider{})
|
||||
defer q.Close()
|
||||
res := e.RunBringUp(context.Background(), BringUpSpec{
|
||||
Mode: ModeProvision, Archive: "local:backup/g.tar.zst", VMID: vmid,
|
||||
RestoreStorage: "local-lvm", Pool: DefaultPool,
|
||||
})
|
||||
if res.Err != nil || !res.Pass {
|
||||
t.Fatalf("bring-up must pass: %+v", res)
|
||||
}
|
||||
if len(api.restores) != 1 || api.restores[0].Pool != DefaultPool {
|
||||
t.Fatalf("provision restore must set Pool=%q, got %+v", DefaultPool, api.restores)
|
||||
}
|
||||
|
||||
// restore-test: the scratch restore hardcodes DefaultPool
|
||||
api2 := &fakeAPI{cfg: map[int]proxmox.GuestConfig{990000: scratchCfg()}}
|
||||
e2, _, q2 := newEngine(t, api2, EmptyProvider{})
|
||||
defer q2.Close()
|
||||
rt := e2.RunRestoreTest(context.Background(), RestoreTestSpec{
|
||||
Archive: "local:backup/x.tar.zst", RestoreStorage: "local-lvm",
|
||||
ScratchMin: 990000, ScratchMax: 990009, SourceTier: "local",
|
||||
})
|
||||
if rt.Err != nil || !rt.Pass {
|
||||
t.Fatalf("restore-test must pass: %+v", rt)
|
||||
}
|
||||
if len(api2.restores) != 1 || api2.restores[0].Pool != DefaultPool {
|
||||
t.Fatalf("restore-test restore must set Pool=%q, got %+v", DefaultPool, api2.restores)
|
||||
}
|
||||
}
|
||||
|
||||
// The golden-carried Docker-data volume is grown via a SEPARATE resize on its mpN slot (B4),
|
||||
// alongside (but distinct from) the rootfs grow.
|
||||
func TestRunBringUp_StorageSplit_DataVolGrow(t *testing.T) {
|
||||
|
||||
@@ -192,7 +192,9 @@ func (e *Engine) runScratchTest(ctx context.Context, vmid int, spec RestoreTestS
|
||||
}
|
||||
}
|
||||
upid, err := e.api.RestoreLXC(ctx, proxmox.RestoreLXCOptions{
|
||||
VMID: vmid, Archive: spec.Archive, Storage: spec.RestoreStorage, MountOverrides: mountOverrides,
|
||||
// Pool=DefaultPool so the scratch guest is created INTO the felhom pool — else a pool-scoped
|
||||
// token 403s on the scratch guest's config/start/destroy (SPIKE residual #2).
|
||||
VMID: vmid, Archive: spec.Archive, Storage: spec.RestoreStorage, MountOverrides: mountOverrides, Pool: DefaultPool,
|
||||
})
|
||||
if err != nil {
|
||||
res.Err = fmt.Errorf("reconcile: restore-test restore: %w", err)
|
||||
|
||||
Reference in New Issue
Block a user