The first pass does not refuse two providers beside the consumer

#195 refused two modules beside a consumer that both answer a bound
provision — in every pass. The first pass exists only to answer what a node
offers, and a refusal there makes the machine vanish from every other node's
world (resolve.go says so for its sibling case): with novox refused for its
own acme-ca, ace's plan lost the vault and the identity provider and read
"nothing in this mesh provides secret". Seen live within minutes of the
rollout. The second pass refuses it, where it is asked, as before.
This commit is contained in:
2026-10-01 17:34:17 +02:00
parent d54ecb3bf2
commit 5a7ed56f61
2 changed files with 19 additions and 0 deletions
@@ -103,3 +103,13 @@ func TestAPinSettlesTwoProvidersBesideTheConsumer(t *testing.T) {
t.Fatalf("no need for acme-ca was created: %+v", got.Needs)
}
}
func TestTheFirstPassDoesNotRefuseTwoProvidersBesideTheConsumer(t *testing.T) {
// The first pass answers only what a node offers. Refused there, the node vanishes from every
// other node's world — and the whole mesh loses its vault for an ambiguity one machine has to
// settle. The second pass is where it is refused, and the test above proves it is.
if _, err := Resolve(issuerShelf(), []string{"route-proxy", "public-acme", "step-ca"}, reachable(),
World{Unchecked: true}); err != nil {
t.Fatalf("the first pass refused what only the second may: %v", err)
}
}
+9
View File
@@ -342,6 +342,15 @@ func Resolve(catalogue map[string]Manifest, assigned []string, node Node, world
// Two modules on this machine answer it. Taking whichever a map walk met first
// was the rule until novox/hq #258 — random, per plan — and the same stance as
// across machines applies: ambiguity is refused, never resolved by picking.
//
// **Not in the first pass.** That pass exists only to answer *what does this node
// offer*, and refusing there makes the machine vanish rather than report a problem
// (the sibling case below says why): every other node then loses what this one
// provides — the vault, the identity provider — and refuses for a fault that is
// this node's to settle. The second pass refuses it properly, where it is asked.
if world.Unchecked {
continue
}
c, pinned := world.Pinned[want]
if !pinned || c.Node != node.Name || !oneOf(matter, c.Module) {
reported[want] = true