package catalogue import ( "sort" "strings" ) // Which machine serves each routed name (novox/hq ADR 0066, issue 178). // // A routed name is a label the mesh composed for a consumer's endpoint, and it is *served* by the // provider that answers requests for it — the proxy the consumer's route reaches. The same name is // composed into every labelled contribution the consumer makes, because a provider that must know // the consumer's public name (an identity provider composing a redirect) is told it the same way // (04-ISSUES/122). Attributing the name to whichever of those providers a map happened to yield // last sent a public name to the identity provider's machine on one plan and to the proxy's on the // next (forge issue 227), and the whole names region flipped with it. // // **The terminus serves the name.** Among the providers a name reaches, the one that serves it is // the one that is not itself routed: a provider that contributes a labelled name of its own to some // requirement is published through another provider, and is a consumer of names, not their end. // Name-agnostic — nothing here knows what "route" means — and structural: it reads the graph the // modules declared. Deterministic: names, requirements and nodes are walked in order, so two // plans of one mesh yield one region. // NamesServed is every routed name across the mesh and the node that serves it, from every node's // resolution and settings. A name several termini claim goes to the first node in name order, so // the answer is stable; a name nothing terminal claims is left out. func NamesServed(plans map[string]Resolution, settings map[string]SettingsBy) (map[string]string, error) { nodes := make([]string, 0, len(plans)) for n := range plans { nodes = append(nodes, n) } sort.Strings(nodes) out := map[string]string{} for _, node := range nodes { plan := plans[node] all, err := plan.contributions(settings[node], nil, nil) if err != nil { return nil, err } requirements := make([]string, 0, len(all)) for to := range all { requirements = append(requirements, to) } sort.Strings(requirements) for _, to := range requirements { for _, given := range all[to] { if given.Node != "" { // Said from another machine; that machine's own resolution carries it. continue } // A routed name, and only that: a contribution the mesh composed a name for from a // label it was given. A grant that happens to carry a `name` of its own — a database // name — carries no label and is left alone. if _, labelled := given.Values["label"]; !labelled { continue } name, _ := given.Values["name"].(string) if name == "" { continue } serving := servingNodeOf(plan, to, given.From, node) if !servesNames(plans[serving], to) { continue } name = strings.ToLower(name) if held, taken := out[name]; !taken || serving < held { out[name] = serving } } } } return out, nil } // servingNodeOf is the node answering one consumer's requirement: whoever the plan needs it from, // or this same node when the provider is beside the consumer. func servingNodeOf(plan Resolution, requirement, consumer, self string) string { for _, need := range plan.Needs { if need.Name == requirement && need.For == consumer && need.From != "" { return need.From } } return self } // servesNames says whether the module providing a requirement on a node is a terminus: it is not // itself published under a labelled name through some other provider. A node whose plan is not // known (it did not resolve) serves nothing. func servesNames(plan Resolution, requirement string) bool { for _, m := range plan.Modules { if !offers(m, requirement) { continue } return !contributesALabel(m) } return false } func offers(m Manifest, requirement string) bool { for _, o := range m.Offers() { if o == requirement { return true } } return false } func contributesALabel(m Manifest) bool { for _, values := range m.Contributes { if _, labelled := values["label"]; labelled { return true } } for _, locals := range m.ContributesMany { for _, values := range locals { if _, labelled := values["label"]; labelled { return true } } } return false }