diff --git a/modules/mesh-vault/cmd/mesh-vault/retire.go b/modules/mesh-vault/cmd/mesh-vault/retire.go index ddc47474..ea694aa9 100644 --- a/modules/mesh-vault/cmd/mesh-vault/retire.go +++ b/modules/mesh-vault/cmd/mesh-vault/retire.go @@ -236,6 +236,18 @@ func (v *Vault) RetireAccounts(caller string, users []string, why string) (any, if len(users) == 0 { return nil, errors.New("account_retire names the accounts (`users`), as vault_status lists them waiting") } + // As the passes: never while the controller holds the lease (it forgets the accounts it mints), nor before the wanted + // accounts are read whole (an account not read yet is not known to be gone). + v.leaseAfresh() + if on, why := v.mintingOn(); !on { + return nil, fmt.Errorf("nothing was retired: %s", why) + } + v.minting.mu.Lock() + read := v.minting.retiring.wantsRead + v.minting.mu.Unlock() + if !read { + return nil, errors.New("nothing was retired: the wanted accounts are not read whole yet, so none is known to be gone") + } for _, u := range users { if _, wanted := v.wanted(u); wanted { return nil, fmt.Errorf("%s is still wanted by the grants part: only an account no longer wanted is retired; nothing was retired", u) diff --git a/modules/mesh-vault/cmd/mesh-vault/retire_test.go b/modules/mesh-vault/cmd/mesh-vault/retire_test.go index df6c2721..2c4fe6ae 100644 --- a/modules/mesh-vault/cmd/mesh-vault/retire_test.go +++ b/modules/mesh-vault/cmd/mesh-vault/retire_test.go @@ -65,6 +65,21 @@ func TestAWantGoneRetiresItsAccountOnlyWithinTheBounds(t *testing.T) { if _, err := v.RetireAccounts("person.jochen", []string{"ace.m2"}, "test"); err == nil { t.Error("retired by hand an account still wanted") } + // Never while the controller holds the lease, nor before the wants are read whole. + held := v.LeaseRead + v.LeaseRead = func() ([]byte, bool, error) { return []byte(`{"instance":"novox/1","epoch":57}`), true, nil } + if _, err := v.RetireAccounts("person.jochen", []string{"ace.m5"}, "the modules left"); err == nil || !strings.Contains(err.Error(), "controller holds the lease") { + t.Errorf("retired by hand under the controller's lease: %v", err) + } + v.LeaseRead = held + v.LeaseHeard([]byte(`{"handed-to":"the operator's hand-over"}`), true, nil) + v.minting.mu.Lock() + v.minting.retiring.wantsRead = false + v.minting.mu.Unlock() + if _, err := v.RetireAccounts("person.jochen", []string{"ace.m5"}, "the modules left"); err == nil || !strings.Contains(err.Error(), "not read whole") { + t.Errorf("retired by hand before the wants were read: %v", err) + } + v.WantsRead() if _, err := v.RetireAccounts("person.jochen", []string{"ace.m5", "ace.m6"}, "the modules left"); err != nil { t.Fatal(err) }