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,100 @@
|
||||
package catalogue
|
||||
|
||||
import (
|
||||
"sort"
|
||||
)
|
||||
|
||||
// Chosen is the provider somebody named for a provision: the 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. Module is empty only on a record made before this
|
||||
// was asked, and such a record is honoured exactly as long as it is unambiguous.
|
||||
type Chosen struct {
|
||||
Node string
|
||||
Module string
|
||||
}
|
||||
|
||||
func (c Chosen) String() string {
|
||||
if c.Module == "" {
|
||||
return c.Node
|
||||
}
|
||||
return c.Node + "/" + c.Module
|
||||
}
|
||||
|
||||
// matches is whether this provider is the one chosen.
|
||||
func (c Chosen) matches(p Provider) bool {
|
||||
return p.Node == c.Node && (c.Module == "" || p.Module == c.Module)
|
||||
}
|
||||
|
||||
// among is every offered provider the choice names — one, when the choice is whole.
|
||||
func (c Chosen) among(where []Provider) []Provider {
|
||||
var out []Provider
|
||||
for _, p := range where {
|
||||
if c.matches(p) {
|
||||
out = append(out, p)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// nameOf is how a refusal names a provider: the node and the module on it.
|
||||
func nameOf(p Provider) string {
|
||||
return Chosen{Node: p.Node, Module: p.Module}.String()
|
||||
}
|
||||
|
||||
// providerNames is every provider named, sorted, for a refusal to list.
|
||||
func providerNames(where []Provider) []string {
|
||||
out := make([]string, 0, len(where))
|
||||
for _, p := range where {
|
||||
out = append(out, nameOf(p))
|
||||
}
|
||||
sort.Strings(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// providersHere is which modules in this node's own set offer a provision, sorted.
|
||||
func providersHere(catalogue map[string]Manifest, here func(string) bool, want string) []string {
|
||||
var out []string
|
||||
for name, m := range catalogue {
|
||||
if !here(name) {
|
||||
continue
|
||||
}
|
||||
for _, o := range m.Offers() {
|
||||
if o == want {
|
||||
out = append(out, name)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
sort.Strings(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// servedByOne is what one provider beside the consumer says a consumer needs to know, or nothing.
|
||||
//
|
||||
// Serving is *whether* a need is created at all when the provider is on this same machine (novox/hq
|
||||
// 04-ISSUES/038's sibling): a need never created is a binding the consumer never gets. The manifest
|
||||
// alone answers that; the values are settled later, with the node's settings.
|
||||
func servedByOne(m Manifest, want string) map[string]any {
|
||||
if _, ok := m.Serves[want]; ok {
|
||||
return ServedOn(m, want, nil)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// sharedByOne is the own secret that provider names as its credential (ADR 0158), or "" when it
|
||||
// gives each consumer its own.
|
||||
func sharedByOne(m Manifest, want string) string {
|
||||
if own, shared := m.SharedCredentialOf(want); shared {
|
||||
return own
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func oneOf(list []string, s string) bool {
|
||||
for _, x := range list {
|
||||
if x == s {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user