novox/hq ADR 0035: one implementation, several surfaces, and a surface holds no decisions. The act of assigning — including that an assignment which does not resolve is kept and still refused — moved into acts.go, and the command line now calls it too. Two surfaces, one refusal, in the same words. It will not run without --issuer, and refuses at start rather than per request so it is found by whoever ran it rather than by whoever finds it. There is no flag that removes the check. The authenticator is honest about what it is: no token can be verified until an identity provider exists, because that is a module and none is running, so every request is refused and told that the command line still works. A surface that functioned without authentication would be one somebody left running — and the board this stands behind is published on a public name. Four refusals, four tests. The last one first asserted "not 200", which passed because a request with no database fails at the store anyway — it proved nothing about whether the input was checked. It now asserts the specific refusal, and bites when the check is removed.
44 lines
1.9 KiB
Go
44 lines
1.9 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
)
|
|
|
|
// The things the mesh can be asked to do, separated from how it was asked.
|
|
//
|
|
// **A surface is an adapter with no decisions in it** (novox/hq ADR 0035). The command line and
|
|
// the command API call the same functions here, so an assignment refused at one is refused at the
|
|
// other for the same reason and in the same words. The moment a surface can accept something
|
|
// another would reject, the mesh has two answers to one question and people learn which to trust.
|
|
//
|
|
// Each returns what happened as text a person can read and a caller can pass on. Neither surface
|
|
// composes its own explanation, because two explanations of one refusal drift.
|
|
|
|
// assign puts a module on a node, and says at once whether the whole set still resolves.
|
|
//
|
|
// The assignment is kept even when it does not: it is what a person meant, and the refusal is
|
|
// about the set rather than about this one. That is a decision, so it lives here rather than in
|
|
// whichever surface asked.
|
|
func assign(ctx context.Context, open *stores, node, module string) (string, error) {
|
|
if err := open.inventory.Assign(ctx, node, module); err != nil {
|
|
return "", err
|
|
}
|
|
said := fmt.Sprintf("%s is assigned %s", node, module)
|
|
if _, _, err := planFor(ctx, open, node); err != nil {
|
|
// Kept, and still refused. Both halves are the answer.
|
|
return said, err
|
|
}
|
|
return said + fmt.Sprintf("\n run `push %s` to send it", node), nil
|
|
}
|
|
|
|
// unassign takes a module off a node. What it leaves behind is the host's business: a directory
|
|
// holding anything the mesh did not put there is kept (novox/hq ADR 0030).
|
|
func unassign(ctx context.Context, open *stores, node, module string) (string, error) {
|
|
if err := open.inventory.Unassign(ctx, node, module); err != nil {
|
|
return "", err
|
|
}
|
|
return fmt.Sprintf("%s no longer runs %s — run `push %s` to make it so",
|
|
node, module, node), nil
|
|
}
|