A pin names the module as well as the node; a node that answers twice is refused
A provider is a (node, module) pair (design 23), and the pin — the one way a consumer names its provider — named only the node. Two modules on one node can both answer a provision (public-acme and step-ca both offer acme-ca on novox), and then the resolver, given a pin naming that node, took the last provider listed: a coin flip. The same ambiguity beside the consumer was settled by a map walk — random per plan — which is how novox's own route-proxy got its issuer (novox/hq #258). - `pin <node> <provision> <from-node> <module>`: both halves, always. The console gains `pin` and `unpin`. The provider may be on the consumer's own node, since two modules beside it can both answer. - The resolver refuses ambiguity instead of picking, across machines and beside the consumer alike, naming every candidate as node/module and the form of the pin that settles it. A plain capability that grants nothing and serves nothing (three shells beside an editor) is not a choice to put to anybody and stays as it was. - provision_pin gains a nullable module (0050); records made before are completed where the node they name answers once, and left for a person where it answers twice (0051). - The provider of something already satisfied is looked for among what was assigned, not only what the walk has reached — a consumer reached before the provider beside it no longer loses its binding. - The start-time check that every declared verb is runnable samples each verb's required arguments from its schema instead of three guessed keys. Live consequence: a node that has two providers of one bound provision assigned (novox: acme-ca) resolves only once pinned — `pin novox acme-ca novox public-acme`.
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
package inventory
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// A pin made before it named the module (migration 0051, novox/hq #258): completed where the node it
|
||||
// names answers once, left for a person where it answers twice.
|
||||
|
||||
func legacyPin(t *testing.T, inv *Inventory, node, provision, provider string) {
|
||||
t.Helper()
|
||||
_, err := inv.store.Pool().Exec(context.Background(),
|
||||
`insert into provision_pin (node, name, provider)
|
||||
select u.id, $2, p.id from node u, node p where u.name = $1 and p.name = $3`,
|
||||
node, provision, provider)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func completeEarlierPins(t *testing.T, inv *Inventory) {
|
||||
t.Helper()
|
||||
sql, err := os.ReadFile("migrations/0051-a-pin-made-before-is-completed.sql")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := inv.store.Pool().Exec(context.Background(), string(sql)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPinMadeBeforeIsCompletedWhenTheNodeAnswersOnce(t *testing.T) {
|
||||
inv := fresh(t)
|
||||
ctx := context.Background()
|
||||
for _, n := range []string{"user", "provider"} {
|
||||
if _, err := inv.AddNode(ctx, n); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
for _, m := range []string{"postgres", "redis"} {
|
||||
if err := inv.RegisterModule(ctx, manifest(m, []string{m + "-database"}, nil), Source{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := inv.Assign(ctx, "provider", m); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
legacyPin(t, inv, "user", "postgres-database", "provider")
|
||||
completeEarlierPins(t, inv)
|
||||
pins, err := inv.PinsFor(ctx, "user")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := pins["postgres-database"]; got.Node != "provider" || got.Module != "postgres" {
|
||||
t.Fatalf("the record was not completed with the one module that answers: %+v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPinMadeBeforeIsLeftOpenWhenTheNodeAnswersTwice(t *testing.T) {
|
||||
inv := fresh(t)
|
||||
ctx := context.Background()
|
||||
for _, n := range []string{"user", "provider"} {
|
||||
if _, err := inv.AddNode(ctx, n); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
for _, m := range []string{"public-acme", "step-ca"} {
|
||||
if err := inv.RegisterModule(ctx, manifest(m, []string{"acme-ca"}, nil), Source{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := inv.Assign(ctx, "provider", m); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
legacyPin(t, inv, "user", "acme-ca", "provider")
|
||||
completeEarlierPins(t, inv)
|
||||
pins, err := inv.PinsFor(ctx, "user")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := pins["acme-ca"]; got.Node != "provider" || got.Module != "" {
|
||||
t.Fatalf("a node that answers twice was guessed for: %+v", got)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user