hub v0.51.0: DR-tier-by-default — per-customer dr_tier flag (default ON, legacy backfill from reality), cascade stages, WG-registration auto-provision hook, offsite-requires-DR guard (F-6 policy), host-page capability chips (inactive=neutral)
Claude-Session: https://claude.ai/code/session_01NptTCFtu7dz2Ru89qHRagN
This commit is contained in:
@@ -118,7 +118,7 @@ func TestPBSDR_ProvisionHappyPath(t *testing.T) {
|
||||
fake := &fakeTenancy{secret: "SUPER-SECRET-TOKEN"}
|
||||
s, st, logBuf := newPBSDRServer(t, fake)
|
||||
|
||||
rr := postUpdate(t, s, url.Values{"pbsdr_enabled": {"on"}, "pbsdr_storage_id": {"felhom-pbs"}})
|
||||
rr := postUpdate(t, s, url.Values{"dr_tier": {"on"}, "pbsdr_storage_id": {"felhom-pbs"}})
|
||||
if rr.Code != 303 {
|
||||
t.Fatalf("save = %d (%s), want 303", rr.Code, rr.Body.String())
|
||||
}
|
||||
@@ -161,7 +161,7 @@ func TestPBSDR_ResaveIsNoOp(t *testing.T) {
|
||||
fake := &fakeTenancy{secret: "SUPER-SECRET-TOKEN"}
|
||||
s, st, _ := newPBSDRServer(t, fake)
|
||||
|
||||
postUpdate(t, s, url.Values{"pbsdr_enabled": {"on"}})
|
||||
postUpdate(t, s, url.Values{"dr_tier": {"on"}})
|
||||
if _, err := st.ConsumeHostPBSSecret("peti-01"); err != nil {
|
||||
t.Fatalf("first secret consume: %v", err)
|
||||
}
|
||||
@@ -169,7 +169,7 @@ func TestPBSDR_ResaveIsNoOp(t *testing.T) {
|
||||
// Idempotent re-save: NO re-key, NO second secret row, NO spurious generation bump.
|
||||
// (Red-proof: dropping the already-provisioned short-circuit in applyPBSDR re-runs
|
||||
// Provision → calls=2 + a fresh consumable secret → FAIL.)
|
||||
rr := postUpdate(t, s, url.Values{"pbsdr_enabled": {"on"}})
|
||||
rr := postUpdate(t, s, url.Values{"dr_tier": {"on"}})
|
||||
if rr.Code != 303 {
|
||||
t.Fatalf("re-save = %d, want 303 (already-provisioned is success-no-op)", rr.Code)
|
||||
}
|
||||
@@ -184,31 +184,43 @@ func TestPBSDR_ResaveIsNoOp(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// v0.51.0 (DR-tier-by-default): an UNMET PRECONDITION is an honest waiting stage, not a
|
||||
// save-blocking error — the flag is stored and the cascade converges later. A REAL provisioning
|
||||
// failure stays fail-closed. Red-proof partner for the coupling: drop the WG-peer stage check in
|
||||
// pbsdrProvisionAtom → "no WG peer waits" fails (provision would be reached).
|
||||
func TestPBSDR_FailClosed(t *testing.T) {
|
||||
t.Run("no WG peer", func(t *testing.T) {
|
||||
t.Run("no WG peer waits (stage, not error)", func(t *testing.T) {
|
||||
fake := &fakeTenancy{secret: "S"}
|
||||
s, st, _ := newPBSDRServer(t, fake)
|
||||
s, st, logBuf := newPBSDRServer(t, fake)
|
||||
// Kill the precondition: the host has never registered a WG key.
|
||||
if err := st.RemoveWGPeer("PETIPUBKEY"); err != nil {
|
||||
t.Fatalf("remove seed peer: %v", err)
|
||||
}
|
||||
rr := postUpdate(t, s, url.Values{"pbsdr_enabled": {"on"}})
|
||||
if rr.Code != 502 {
|
||||
t.Fatalf("save without WG peer = %d, want 502", rr.Code)
|
||||
rr := postUpdate(t, s, url.Values{"dr_tier": {"on"}})
|
||||
if rr.Code != 303 {
|
||||
t.Fatalf("save without WG peer = %d (%s), want 303 — the flag stores, the cascade waits", rr.Code, rr.Body.String())
|
||||
}
|
||||
if fake.provisionCalls != 0 {
|
||||
t.Errorf("provision reached despite missing peer (%d calls)", fake.provisionCalls)
|
||||
}
|
||||
h, _ := st.GetHost("peti-01")
|
||||
if readPBSDR(h.DesiredJSON) != nil || h.DesiredGeneration != 0 {
|
||||
t.Errorf("fail-closed violated: desc=%v gen=%d", readPBSDR(h.DesiredJSON), h.DesiredGeneration)
|
||||
t.Errorf("descriptor written while waiting: desc=%v gen=%d", readPBSDR(h.DesiredJSON), h.DesiredGeneration)
|
||||
}
|
||||
// The flag itself IS saved (the intent survives the wait).
|
||||
cfg, _ := st.GetCustomerConfig("peti")
|
||||
if !cfg.DRTier {
|
||||
t.Error("DR flag not stored while the cascade waits")
|
||||
}
|
||||
if !strings.Contains(logBuf.String(), "has not reported a WG key yet") {
|
||||
t.Error("the waiting stage (guard wording) was not logged")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("tenantsync error", func(t *testing.T) {
|
||||
fake := &fakeTenancy{err: errors.New("ssh boom")}
|
||||
s, st, _ := newPBSDRServer(t, fake)
|
||||
rr := postUpdate(t, s, url.Values{"pbsdr_enabled": {"on"}})
|
||||
rr := postUpdate(t, s, url.Values{"dr_tier": {"on"}})
|
||||
if rr.Code != 502 {
|
||||
t.Fatalf("save with tenantsync error = %d, want 502", rr.Code)
|
||||
}
|
||||
@@ -229,7 +241,7 @@ func TestPBSDR_FailClosed(t *testing.T) {
|
||||
t.Run("token_exists points at re-issue", func(t *testing.T) {
|
||||
fake := &fakeTenancy{err: tenantsync.ErrTokenExists}
|
||||
s, st, _ := newPBSDRServer(t, fake)
|
||||
rr := postUpdate(t, s, url.Values{"pbsdr_enabled": {"on"}})
|
||||
rr := postUpdate(t, s, url.Values{"dr_tier": {"on"}})
|
||||
if rr.Code != 502 || !strings.Contains(rr.Body.String(), "Re-issue") {
|
||||
t.Fatalf("token_exists = %d (%s), want 502 mentioning Re-issue", rr.Code, rr.Body.String())
|
||||
}
|
||||
@@ -239,11 +251,11 @@ func TestPBSDR_FailClosed(t *testing.T) {
|
||||
_ = st
|
||||
})
|
||||
|
||||
t.Run("not configured", func(t *testing.T) {
|
||||
t.Run("not configured waits (stage, not error)", func(t *testing.T) {
|
||||
s, st, _ := newPBSDRServer(t, nil) // no tenantsync
|
||||
rr := postUpdate(t, s, url.Values{"pbsdr_enabled": {"on"}})
|
||||
if rr.Code != 502 {
|
||||
t.Fatalf("save without tenantsync = %d, want 502", rr.Code)
|
||||
rr := postUpdate(t, s, url.Values{"dr_tier": {"on"}})
|
||||
if rr.Code != 303 {
|
||||
t.Fatalf("save without tenantsync = %d, want 303 — the flag stores, provisioning waits", rr.Code)
|
||||
}
|
||||
if desc, _, gen := hostState(t, st); desc != nil || gen != 0 {
|
||||
t.Errorf("state written without a provisioner: desc=%+v gen=%d", desc, gen)
|
||||
@@ -251,10 +263,67 @@ func TestPBSDR_FailClosed(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
// Scenario A (hands-free cascade): flag ON while the WG peer is missing → the save waits; the
|
||||
// host's WG registration then fires PBSDRAutoProvision and the descriptor lands with ZERO
|
||||
// operator steps. Red-proof partner: unhook the atom from PBSDRAutoProvision (early return) →
|
||||
// this fails at "descriptor after WG registration".
|
||||
func TestPBSDR_AutoProvisionOnWGRegistration(t *testing.T) {
|
||||
fake := &fakeTenancy{secret: "AUTO-SECRET"}
|
||||
s, st, logBuf := newPBSDRServer(t, fake)
|
||||
if err := st.RemoveWGPeer("PETIPUBKEY"); err != nil {
|
||||
t.Fatalf("remove seed peer: %v", err)
|
||||
}
|
||||
|
||||
// 1. Flag ON, peer missing → stored intent, no descriptor.
|
||||
postUpdate(t, s, url.Values{"dr_tier": {"on"}})
|
||||
if desc, _, _ := hostState(t, st); desc != nil {
|
||||
t.Fatalf("descriptor exists before the WG peer: %+v", desc)
|
||||
}
|
||||
|
||||
// 2. The agent registers its WG key (what the api handler does) → the hook fires.
|
||||
if _, _, err := st.RegisterWGPeerForHost("peti-01", "PETIPUBKEY"); err != nil {
|
||||
t.Fatalf("register peer: %v", err)
|
||||
}
|
||||
s.PBSDRAutoProvision(context.Background(), "peti")
|
||||
|
||||
desc, desiredJSON, _ := hostState(t, st)
|
||||
if desc == nil || !desc.Enabled || desc.Namespace != "peti" {
|
||||
t.Fatalf("descriptor after WG registration = %+v (json %s), want provisioned", desc, desiredJSON)
|
||||
}
|
||||
if fake.provisionCalls != 1 {
|
||||
t.Errorf("provision calls = %d, want 1", fake.provisionCalls)
|
||||
}
|
||||
if got, err := st.ConsumeHostPBSSecret("peti-01"); err != nil || got != "AUTO-SECRET" {
|
||||
t.Fatalf("consume-once secret after auto-provision = (%q, %v)", got, err)
|
||||
}
|
||||
if strings.Contains(logBuf.String(), "AUTO-SECRET") {
|
||||
t.Error("secret leaked into the hub log")
|
||||
}
|
||||
|
||||
// 3. Idempotent: a second hook firing (re-registration) must not re-key.
|
||||
s.PBSDRAutoProvision(context.Background(), "peti")
|
||||
if fake.provisionCalls != 1 {
|
||||
t.Errorf("second hook firing re-provisioned (%d calls)", fake.provisionCalls)
|
||||
}
|
||||
|
||||
// 4. Flag OFF → the hook never provisions (scenario B: off is off).
|
||||
fake2 := &fakeTenancy{secret: "S2"}
|
||||
s2, st2, _ := newPBSDRServer(t, fake2)
|
||||
cfg, _ := st2.GetCustomerConfig("peti")
|
||||
cfg.DRTier = false
|
||||
if err := st2.SaveCustomerConfig(cfg); err != nil {
|
||||
t.Fatalf("save flag-off config: %v", err)
|
||||
}
|
||||
s2.PBSDRAutoProvision(context.Background(), "peti")
|
||||
if fake2.provisionCalls != 0 {
|
||||
t.Errorf("hook provisioned with the DR flag OFF (%d calls)", fake2.provisionCalls)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPBSDR_DisableKeepsTenancy(t *testing.T) {
|
||||
fake := &fakeTenancy{secret: "S"}
|
||||
s, st, _ := newPBSDRServer(t, fake)
|
||||
postUpdate(t, s, url.Values{"pbsdr_enabled": {"on"}})
|
||||
postUpdate(t, s, url.Values{"dr_tier": {"on"}})
|
||||
|
||||
// Unchecked box → descriptor enabled:false, coords kept, ONE bump; no endpoint mutation.
|
||||
rr := postUpdate(t, s, url.Values{})
|
||||
@@ -281,10 +350,10 @@ func TestPBSDR_DisableKeepsTenancy(t *testing.T) {
|
||||
func TestPBSDR_StorageIDChangeUpdatesDescriptorOnly(t *testing.T) {
|
||||
fake := &fakeTenancy{secret: "S"}
|
||||
s, st, _ := newPBSDRServer(t, fake)
|
||||
postUpdate(t, s, url.Values{"pbsdr_enabled": {"on"}})
|
||||
postUpdate(t, s, url.Values{"dr_tier": {"on"}})
|
||||
st.ConsumeHostPBSSecret("peti-01") // spend the provision secret
|
||||
|
||||
rr := postUpdate(t, s, url.Values{"pbsdr_enabled": {"on"}, "pbsdr_storage_id": {"felhom-offsite"}})
|
||||
rr := postUpdate(t, s, url.Values{"dr_tier": {"on"}, "pbsdr_storage_id": {"felhom-offsite"}})
|
||||
if rr.Code != 303 {
|
||||
t.Fatalf("storage-id change = %d, want 303", rr.Code)
|
||||
}
|
||||
@@ -306,7 +375,7 @@ func TestPBSDR_StorageIDChangeUpdatesDescriptorOnly(t *testing.T) {
|
||||
func TestPBSDR_Reissue(t *testing.T) {
|
||||
fake := &fakeTenancy{secret: "OLD-SECRET"}
|
||||
s, st, logBuf := newPBSDRServer(t, fake)
|
||||
postUpdate(t, s, url.Values{"pbsdr_enabled": {"on"}})
|
||||
postUpdate(t, s, url.Values{"dr_tier": {"on"}})
|
||||
st.ConsumeHostPBSSecret("peti-01") // agent already consumed; the dead-end scenario
|
||||
|
||||
fake.secret = "FRESH-SECRET"
|
||||
@@ -353,7 +422,7 @@ func TestPBSDR_ReissueRequiresProvisionedState(t *testing.T) {
|
||||
func TestPBSDR_FormRendersState(t *testing.T) {
|
||||
fake := &fakeTenancy{secret: "S"}
|
||||
s, _, _ := newPBSDRServer(t, fake)
|
||||
postUpdate(t, s, url.Values{"pbsdr_enabled": {"on"}})
|
||||
postUpdate(t, s, url.Values{"dr_tier": {"on"}})
|
||||
|
||||
// v0.48.0 edit-a: the standalone GET edit page is a redirect now — the form renders embedded
|
||||
// in the customer page's Edit tab (the same config_form_body sub-template), so assert there.
|
||||
@@ -361,11 +430,16 @@ func TestPBSDR_FormRendersState(t *testing.T) {
|
||||
rr := httptest.NewRecorder()
|
||||
s.handleCustomerUnified(rr, req, "peti")
|
||||
out := rr.Body.String()
|
||||
if !strings.Contains(out, `name="pbsdr_enabled" checked`) {
|
||||
t.Error("enabled checkbox not checked after provisioning")
|
||||
if !strings.Contains(out, `name="dr_tier" checked`) {
|
||||
t.Error("DR-tier checkbox not checked after provisioning")
|
||||
}
|
||||
if !strings.Contains(out, "Provisioned: namespace peti, token felhom@pbs!peti") {
|
||||
t.Error("provisioned-state line missing")
|
||||
if !strings.Contains(out, "descriptor provisioned (namespace peti, token felhom@pbs!peti)") {
|
||||
t.Error("provisioned cascade stage line missing")
|
||||
}
|
||||
// The cascade (v0.51.0 scenario D): all four stages render as done on a fully-applied box.
|
||||
if strings.Count(out, `class="badge badge-neutral">waiting</span>`) > 1 {
|
||||
// escrow may still be waiting in this harness; every other stage must be done
|
||||
t.Errorf("more than the escrow stage still waiting:\n%s", out[strings.Index(out, "DR tier (PBS, ep0)"):][:1200])
|
||||
}
|
||||
if !strings.Contains(out, "pbsdr-reissue") {
|
||||
t.Error("re-issue button missing")
|
||||
|
||||
Reference in New Issue
Block a user