feat(reconcile): re-assert pool membership after restore-over-existing (campaign-2 R2, v0.74.0)

Pool membership is what lets the pool-scoped token reach a guest; pct restore
--pool sets it only at CREATE, so a restore over an existing VMID drops the guest
from the felhom pool and 403s the next restore-test/DR on VM.Audit. This empty-pool
state is the true root cause of the campaign's "R1" (bind-mount restore failing was
a symptom — restore-test's existing bind neutralization never ran without config-read).

Add Client.PoolAddVMID (PUT /pools, additive+idempotent, Pool.Allocate) and call it
in bring-up after liveness when spec.Pool!="" — warn-not-fail on a hiccup (liveness
wins). B3 scratch-teardown 403 diagnosed as a cascade (restoretest already passes
Pool). Role/ACL untouched. Tests + red-proof.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
This commit is contained in:
2026-07-07 18:42:19 +02:00
parent e04b75e1f8
commit ca0b169a4e
8 changed files with 278 additions and 0 deletions
+17
View File
@@ -49,6 +49,23 @@ type fakeAPI struct {
waits []string
waitOpts []proxmox.WaitOptions // parallel to waits: the options each WaitTask was called with
listErr error
// poolAdds records (pool, vmid) for each PoolAddVMID; poolAddErr backs the failure path.
poolAdds []poolAddCall
poolAddErr error
}
type poolAddCall struct {
pool string
vmid int
}
func (f *fakeAPI) PoolAddVMID(_ context.Context, pool string, vmid int) error {
f.mu.Lock()
f.poolAdds = append(f.poolAdds, poolAddCall{pool: pool, vmid: vmid})
err := f.poolAddErr
f.mu.Unlock()
return err
}
type resizeCall struct {