GL-5: DR bring-up structural bind overrides + 4d real-bind swap
ModeDRGuestLoss now passes restore-time MountOverrides for the two
platform-constant structural binds (mp8 parent, mp9 bootstrap) via the
shared throwaway-volume format helper - without them a customer-archive
restore under the privsep token fails outright ("restoring 'mp8' to bind
mount is only possible for root"). New post-restore step 4d swaps the real
binds in via the host runner (root pct set, one slot per call), deletes the
displaced unusedN volumes (API config PUT; a scoped-token refusal logs the
residue loudly instead of widening privileges), and respects the
committed/launched rollback envelope. Provision passes nil overrides -
byte-identical behavior (regression contract test).
Engine grows an optional HostRunner + StateDir seam (DR refuses up front
without a runner); selftest bring-up wires the ExecRunner + cleans the
scratch mp9 host dir on teardown; proxmox.GuestConfig.Unused() added.
6 new tests incl. C2 mid-swap rollback + C3 older-archive + 403-warn paths.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
This commit is contained in:
@@ -32,6 +32,15 @@ type Engine struct {
|
||||
hostID string
|
||||
logger *slog.Logger
|
||||
|
||||
// hostRun executes host-root commands for the DR structural-bind swap (bring-up 4d): a
|
||||
// bind-mount `pct set` is root@pam-only, so it cannot go through the API token (the same
|
||||
// constraint the provision back-half documents). nil on API-only engines — RunBringUp
|
||||
// refuses ModeDRGuestLoss then (GL-5).
|
||||
hostRun proxmox.Runner
|
||||
// stateDir is the agent state dir the mp9 bootstrap host dir lives under
|
||||
// (<stateDir>/guests/<vmid>/bootstrap); "" → /var/lib/felhom-agent (mirrors provision.NewBackHalf).
|
||||
stateDir string
|
||||
|
||||
opSeq uint64 // atomic; makes each op id unique per attempt
|
||||
}
|
||||
|
||||
@@ -46,6 +55,11 @@ type EngineOptions struct {
|
||||
Gate *Gate
|
||||
HostID string
|
||||
Logger *slog.Logger
|
||||
// HostRunner enables the DR structural-bind swap (bring-up 4d, root pct ops). Optional —
|
||||
// engines that never run ModeDRGuestLoss may leave it nil.
|
||||
HostRunner proxmox.Runner
|
||||
// StateDir is the agent state dir ("" → /var/lib/felhom-agent); only the 4d swap reads it.
|
||||
StateDir string
|
||||
}
|
||||
|
||||
// NewEngine builds an Engine. The Queue is shared (the single §10 choke point); the
|
||||
@@ -69,6 +83,10 @@ func NewEngine(opts EngineOptions) *Engine {
|
||||
// the common slice-4 daemon state (no signers pinned, no desired state).
|
||||
gate = NewGate(nil, opts.HostID, nil, logger)
|
||||
}
|
||||
stateDir := opts.StateDir
|
||||
if stateDir == "" {
|
||||
stateDir = "/var/lib/felhom-agent"
|
||||
}
|
||||
return &Engine{
|
||||
api: opts.API,
|
||||
queue: opts.Queue,
|
||||
@@ -78,6 +96,8 @@ func NewEngine(opts EngineOptions) *Engine {
|
||||
gate: gate,
|
||||
hostID: opts.HostID,
|
||||
logger: logger,
|
||||
hostRun: opts.HostRunner,
|
||||
stateDir: stateDir,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user