v0.89.0: pbsdr self-grant (R-22) + escrow config live-reload + agent-plane poke listener (Direction-2a)
- pbsdr: on a 403 pre-check (non-default storage id, no ACL yet) self-grant via the root wrapper then re-read, instead of aborting before the grant — closes F4/R-22. Red-proof TestSelfGrant_PreCheck403DoesNotAbortBeforeGrant. - escrow preflight: late-bound CurrentPBSStorageID re-reads agent.json so a pbsdr-seeded pbs_storage_id flips the row green in-process (no restart). Red-proof TestEscrowPreflight_PBSStorageIDLiveReload. - internal/poke: contentless UDP poke listener bound exclusively to the box WG /32 (port 51822), leading-edge debounced, fires the hub-loop out-of-band trigger for an immediate desired-state cycle. First slice of R-13. Red-proofs TestBindConfinement + TestDebounceCoalescesBurst.
This commit is contained in:
@@ -67,9 +67,19 @@ type fakeStorage struct {
|
||||
found bool
|
||||
active []bool // consumed per StorageActive call; last value repeats
|
||||
calls int
|
||||
// entryErrs is consumed per StorageEntry call (nil = the normal (entry,found,nil) answer);
|
||||
// after the slice is exhausted every call answers normally. Lets a test model the R-22
|
||||
// pre-check 403 that self-grant must recover from without aborting.
|
||||
entryErrs []error
|
||||
entryCalls int
|
||||
}
|
||||
|
||||
func (f *fakeStorage) StorageEntry(context.Context, string) (*proxmox.StorageEntryConfig, bool, error) {
|
||||
i := f.entryCalls
|
||||
f.entryCalls++
|
||||
if i < len(f.entryErrs) && f.entryErrs[i] != nil {
|
||||
return nil, false, f.entryErrs[i]
|
||||
}
|
||||
return f.entry, f.found, nil
|
||||
}
|
||||
|
||||
@@ -182,6 +192,58 @@ func TestFreshPath_SecretOnStdinNeverArgv(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestSelfGrant_PreCheck403DoesNotAbortBeforeGrant pins R-22 (F4 from tests/VALIDATION-n100):
|
||||
// on a non-default storage id whose ACL the token lacks, the token-auth pre-check GET /storage/<id>
|
||||
// 403s. The fix must NOT abort — it must run the root-run `grant` (which creates that very ACL),
|
||||
// re-read, and converge. RED-PROOF: the pre-fix code returns on the StorageEntry error before any
|
||||
// runner call, so NO grant runs and the box never converges — this test then fails on
|
||||
// "self-grant never ran". No secret may be consumed on this (adoption) path.
|
||||
func TestSelfGrant_PreCheck403DoesNotAbortBeforeGrant(t *testing.T) {
|
||||
r := &fakeRunner{}
|
||||
forbidden := &proxmox.APIError{StatusCode: 403, Method: "GET", Path: "/storage/felhom-offsite",
|
||||
Body: "Permission check failed (/storage/felhom-offsite, Datastore.Audit)"}
|
||||
st := &fakeStorage{
|
||||
// First read 403s (no ACL); after the self-grant the entry reads healthy → adoption.
|
||||
entryErrs: []error{forbidden},
|
||||
entry: &proxmox.StorageEntryConfig{Storage: "felhom-offsite", Type: "pbs",
|
||||
Namespace: "peti", Fingerprint: testFP},
|
||||
found: true,
|
||||
active: []bool{true},
|
||||
}
|
||||
c := &fakeConsumer{secret: "MUST-NOT-BURN"}
|
||||
m, _ := newTestManager(t, r, st, c)
|
||||
block := testBlock()
|
||||
block.StorageID = "felhom-offsite"
|
||||
block.Namespace = "peti"
|
||||
|
||||
m.Apply(context.Background(), true, block)
|
||||
|
||||
// A 403 on IsForbidden must be recognised as such (regression guard on the type assertion).
|
||||
if !forbidden.IsForbidden() {
|
||||
t.Fatal("precondition: crafted APIError is not IsForbidden")
|
||||
}
|
||||
calls := r.recorded()
|
||||
grants := 0
|
||||
for _, call := range calls {
|
||||
if len(call.Args) > 0 && call.Args[0] == "grant" {
|
||||
grants++
|
||||
}
|
||||
}
|
||||
if grants == 0 {
|
||||
t.Fatalf("self-grant never ran — the pre-check 403 aborted before the root grant (R-22 regression); calls=%+v", calls)
|
||||
}
|
||||
if c.calls != 0 {
|
||||
t.Fatalf("a secret was consumed on the self-grant/adoption path (%d calls) — the no-consume law", c.calls)
|
||||
}
|
||||
if s := m.Status(); s == nil || (s.State != "adopted" && s.State != "applied") {
|
||||
t.Fatalf("status = %+v, want converged (adopted/applied) after self-grant", s)
|
||||
}
|
||||
// The pre-check was re-read (not dropped): 2 StorageEntry calls — the 403, then the post-grant read.
|
||||
if st.entryCalls < 2 {
|
||||
t.Fatalf("StorageEntry called %d times — the post-grant re-read is missing", st.entryCalls)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyPinBeforeConsume(t *testing.T) {
|
||||
r := &fakeRunner{}
|
||||
st := &fakeStorage{found: false}
|
||||
|
||||
Reference in New Issue
Block a user