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:
@@ -841,24 +841,28 @@ func (i *Inventory) SettingsFor(ctx context.Context, nodeName, module string) ([
|
||||
return layers, rows.Err()
|
||||
}
|
||||
|
||||
// PinProvision records which node a machine gets a provision from.
|
||||
// PinProvision records which provider a machine gets a provision from: a module, and the node it
|
||||
// runs on — both, always (novox/hq #258). A provision comes from a module, and the same module on
|
||||
// two machines is two answers, so neither half alone says which.
|
||||
//
|
||||
// Only needed when more than one node could answer. Recordable before that, because a mesh with
|
||||
// one database should not change where an existing machine gets its data the day a second
|
||||
// arrives.
|
||||
func (i *Inventory) PinProvision(ctx context.Context, nodeName, provision, provider string) error {
|
||||
// Only needed when more than one could answer. Recordable before that, because a mesh with one
|
||||
// database should not change where an existing machine gets its data the day a second arrives.
|
||||
func (i *Inventory) PinProvision(ctx context.Context, nodeName, provision, providerNode, module string) error {
|
||||
if strings.TrimSpace(module) == "" {
|
||||
return fmt.Errorf("a pin names the module providing %q as well as the node it runs on", provision)
|
||||
}
|
||||
node, err := i.NodeByName(ctx, nodeName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
from, err := i.NodeByName(ctx, provider)
|
||||
from, err := i.NodeByName(ctx, providerNode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = i.store.Pool().Exec(ctx,
|
||||
`insert into provision_pin (node, name, provider) values ($1, $2, $3)
|
||||
on conflict (node, name) do update set provider = excluded.provider, pinned_at = now()`,
|
||||
node.ID, provision, from.ID)
|
||||
`insert into provision_pin (node, name, provider, module) values ($1, $2, $3, $4)
|
||||
on conflict (node, name) do update set provider = excluded.provider, module = excluded.module, pinned_at = now()`,
|
||||
node.ID, provision, from.ID, module)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -879,27 +883,28 @@ func (i *Inventory) UnpinProvision(ctx context.Context, nodeName, provision stri
|
||||
return nil
|
||||
}
|
||||
|
||||
// PinsFor is what a node was told about where its provisions come from.
|
||||
func (i *Inventory) PinsFor(ctx context.Context, nodeName string) (map[string]string, error) {
|
||||
// PinsFor is what a node was told about where its provisions come from. A record from before a pin
|
||||
// named the module carries the node alone; the resolver honours it while it is unambiguous.
|
||||
func (i *Inventory) PinsFor(ctx context.Context, nodeName string) (map[string]catalogue.Chosen, error) {
|
||||
node, err := i.NodeByName(ctx, nodeName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rows, err := i.store.Pool().Query(ctx,
|
||||
`select p.name, n.name from provision_pin p join node n on n.id = p.provider
|
||||
`select p.name, n.name, coalesce(p.module, '') from provision_pin p join node n on n.id = p.provider
|
||||
where p.node = $1`, node.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
out := map[string]string{}
|
||||
out := map[string]catalogue.Chosen{}
|
||||
for rows.Next() {
|
||||
var name, provider string
|
||||
if err := rows.Scan(&name, &provider); err != nil {
|
||||
var name, provider, module string
|
||||
if err := rows.Scan(&name, &provider, &module); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out[name] = provider
|
||||
out[name] = catalogue.Chosen{Node: provider, Module: module}
|
||||
}
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user