Forget the accounts of modules no longer on their nodes at the start's look, and grant sealed accounts alone (issue 1945, ADR 0543, review)
mesh/delivery held for a person: merged without a passing check: only a person decides what follows
mesh/delivery-group group fix/1945-credential-when-row-without-seal held: a member is held: novox/mesh-controller@fbf14b68dbab (merged without a passing check:…
mesh/check checking fbf14b68dbab on 40034be479f8
mesh/delivery held for a person: merged without a passing check: only a person decides what follows
mesh/delivery-group group fix/1945-credential-when-row-without-seal held: a member is held: novox/mesh-controller@fbf14b68dbab (merged without a passing check:…
mesh/check checking fbf14b68dbab on 40034be479f8
Migration 0108 marks every standing account sealed, ace's stale mesh-build-go rows from 17:19 among them, and the forgetting ran only at a later unassign: the next assignment to ace would have issued nothing again. - The start's look first forgets, from each node's own resolution (assigned and required), the accounts of every module that no longer resolves on its node, and says each; only from a reading that holds assignments, and a node whose resolution is unknown is left alone. - Unassign forgets a module's accounts only once it no longer resolves on the node (a module another requires keeps running), and never the node's own provisioner account; the forgetting moved from the inventory to the act. - Forgetting holds each account's lock across its delete, so an issue in flight never writes a row back after it. - A module whose accounts are not all sealed is granted its sealed accounts alone, and its grant is dropped when none is: an older grant naming ace no longer lets the assigner want it. - An issue's locked section has a deadline of two minutes, since it holds a connection of the store's pool across the vault and the bus. Tests: cases in the existing tables. Research 072: the grants piece (memberships and credentials heard from placement).
This commit is contained in:
@@ -141,30 +141,58 @@ func (i *Inventory) LockBusAccount(ctx context.Context, username string) (func()
|
||||
}
|
||||
|
||||
// ForgetBusAccountsOf forgets the bus accounts of module on node, and the credentials sealed to the node for them (its
|
||||
// `broker`, each process's `broker-<id>`, its provisioner client's): an unassigned module keeps no account, so an
|
||||
// assignment again mints and seals afresh, never trusting a row whose credential the node no longer holds (issue 1945,
|
||||
// live on 2026-10-25 at 20:04: ace's mesh-build-go rows from 17:19 survived its unassignment, nothing was minted or
|
||||
// sealed again, and the module found no credential).
|
||||
func (i *Inventory) ForgetBusAccountsOf(ctx context.Context, node, module string) error {
|
||||
// `broker`, each process's `broker-<id>`, and with nodeOwned its provisioner client's): a module that no longer resolves
|
||||
// on a node keeps no account there, so an assignment again mints and seals afresh, never trusting a row whose credential
|
||||
// the node no longer holds (issue 1945, live on 2026-10-25 at 20:04: ace's mesh-build-go rows from 17:19 outlived its
|
||||
// unassignment, nothing was issued again, and the module found no credential). The provisioner client's account is the
|
||||
// node's (`provisioner.<node>`), forgotten only with nodeOwned. Each account's lock is held across its delete, so an issue
|
||||
// in flight never writes a row back after it. It answers the accounts forgotten.
|
||||
func (i *Inventory) ForgetBusAccountsOf(ctx context.Context, node, module string, nodeOwned bool) ([]string, error) {
|
||||
if node == "" || module == "" {
|
||||
return errors.New("forgetting the bus accounts of no node or no module would forget others' accounts")
|
||||
return nil, errors.New("forgetting the bus accounts of no node or no module would forget others' accounts")
|
||||
}
|
||||
rows, err := i.store.Pool().Query(ctx,
|
||||
`select username from bus_user where node = $1 and module = $2 and kind <> $3 and ($4 or kind <> $5)
|
||||
order by username`, node, module, BusController, nodeOwned, BusProvisioner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var names []string
|
||||
for rows.Next() {
|
||||
var n string
|
||||
if err := rows.Scan(&n); err != nil {
|
||||
rows.Close()
|
||||
return nil, err
|
||||
}
|
||||
names = append(names, n)
|
||||
}
|
||||
rows.Close()
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, n := range names { // in order, one lock at a time per issuer: no two hold each other's
|
||||
release, err := i.LockBusAccount(ctx, n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer release()
|
||||
}
|
||||
tx, err := i.store.Pool().Begin(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
defer func() { _ = tx.Rollback(context.WithoutCancel(ctx)) }()
|
||||
if _, err := tx.Exec(ctx, `delete from bus_user where node = $1 and module = $2 and kind <> $3`,
|
||||
node, module, BusController); err != nil {
|
||||
return fmt.Errorf("the bus accounts of %s on %s are not forgotten: %w", module, node, err)
|
||||
if _, err := tx.Exec(ctx, `delete from bus_user where username = any($1)`, names); err != nil {
|
||||
return nil, fmt.Errorf("the bus accounts of %s on %s are not forgotten: %w", module, node, err)
|
||||
}
|
||||
if _, err := tx.Exec(ctx,
|
||||
`delete from module_secret s using node n
|
||||
where s.node = n.id and n.name = $1 and s.module = $2
|
||||
and (s.name = 'broker' or s.name like 'broker-%' or s.name = 'provisioner-broker')`, node, module); err != nil {
|
||||
return fmt.Errorf("the bus credentials of %s on %s are not forgotten: %w", module, node, err)
|
||||
and (s.name = 'broker' or s.name like 'broker-%' or ($3 and s.name = 'provisioner-broker'))`,
|
||||
node, module, nodeOwned); err != nil {
|
||||
return nil, fmt.Errorf("the bus credentials of %s on %s are not forgotten: %w", module, node, err)
|
||||
}
|
||||
return tx.Commit(ctx)
|
||||
return names, tx.Commit(ctx)
|
||||
}
|
||||
|
||||
// MintBusPasswordUnsealed mints u's password, replacing whatever stands, with no seal yet: the account is not issued
|
||||
|
||||
@@ -105,7 +105,7 @@ func TestMintingAgainRotatesAndMintingIfAbsentHasOneWinner(t *testing.T) {
|
||||
}
|
||||
release()
|
||||
<-waiting
|
||||
if err := inv.ForgetBusAccountsOf(ctx, "one", "racer"); err != nil {
|
||||
if _, err := inv.ForgetBusAccountsOf(ctx, "one", "racer", true); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if minted, _, _ := inv.BusAccountIssued(ctx, "one.racer"); minted {
|
||||
|
||||
@@ -670,10 +670,6 @@ func (i *Inventory) Unassign(ctx context.Context, nodeName, module string) error
|
||||
if err := i.chainUnassign(ctx, c, node, module); err != nil {
|
||||
return err
|
||||
}
|
||||
// Its bus accounts on the node go with it: an assignment again issues them afresh (issue 1945).
|
||||
if err := i.ForgetBusAccountsOf(ctx, nodeName, module); err != nil {
|
||||
return err
|
||||
}
|
||||
// The seat it held falls back to derivation rather than pointing at nothing: what the store's foreign key did.
|
||||
if _, err := i.store.Pool().Exec(ctx, `delete from seat_holding where node = $1 and module = $2`, node.ID, module); err != nil {
|
||||
return err
|
||||
|
||||
Reference in New Issue
Block a user