|
|
|
@@ -243,100 +243,17 @@ func indexOn(conn *bus.Conn) (*index, error) {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
l := &Listing{Tools: []Tool{}, NotAnswering: []string{}}
|
|
|
|
|
x := &index{Modules: map[string]*moduleInfo{}, Listing: l, Discovery: d, Recorded: map[string][]string{}}
|
|
|
|
|
var endpoints []announce.Endpoint
|
|
|
|
|
for _, h := range d.Heard {
|
|
|
|
|
endpoints = append(endpoints, announce.Endpoints(h.Info)...)
|
|
|
|
|
}
|
|
|
|
|
x, announced, machines := indexOfEndpoints(endpoints)
|
|
|
|
|
l := x.Listing
|
|
|
|
|
x.Discovery = d
|
|
|
|
|
for _, s := range d.Silent {
|
|
|
|
|
x.Unheard = append(x.Unheard, unheard{Runtime: s.Name, Machine: s.Machine,
|
|
|
|
|
Why: fmt.Sprintf("it answered PING; what it serves did not arrive within %s", announce.Patience)})
|
|
|
|
|
}
|
|
|
|
|
announced := map[string]map[string]bool{} // module → node → announced something
|
|
|
|
|
seats := map[string]*seatInfo{}
|
|
|
|
|
toolAt := map[string]int{} // <module>.<tool> or <seat>.<verb> → index in l.Tools
|
|
|
|
|
machines := map[string]bool{}
|
|
|
|
|
for _, h := range d.Heard {
|
|
|
|
|
for _, e := range announce.Endpoints(h.Info) {
|
|
|
|
|
if e.Node != "" {
|
|
|
|
|
machines[e.Node] = true
|
|
|
|
|
}
|
|
|
|
|
if announced[e.Module] == nil {
|
|
|
|
|
announced[e.Module] = map[string]bool{}
|
|
|
|
|
}
|
|
|
|
|
announced[e.Module][e.Node] = true
|
|
|
|
|
switch e.Kind {
|
|
|
|
|
case announce.KindSeat:
|
|
|
|
|
st := seats[e.Seat]
|
|
|
|
|
if st == nil {
|
|
|
|
|
st = &seatInfo{Seat: e.Seat, Scope: e.Scope}
|
|
|
|
|
seats[e.Seat] = st
|
|
|
|
|
}
|
|
|
|
|
if st.Scope != "node" && e.Scope == "node" {
|
|
|
|
|
st.Scope = "node"
|
|
|
|
|
}
|
|
|
|
|
h := holder{Module: e.Module, Node: e.Node}
|
|
|
|
|
if !containsHolder(st.Holders, h) {
|
|
|
|
|
st.Holders = append(st.Holders, h)
|
|
|
|
|
}
|
|
|
|
|
key := e.Seat + "." + e.Tool
|
|
|
|
|
if _, have := toolAt[key]; !have {
|
|
|
|
|
toolAt[key] = len(l.Tools)
|
|
|
|
|
t := Tool{Module: e.Seat, Name: e.Tool, Description: e.Description, Input: e.Schema, Seat: true, Scope: st.Scope}
|
|
|
|
|
l.Tools = append(l.Tools, t)
|
|
|
|
|
st.Verbs = append(st.Verbs, t)
|
|
|
|
|
}
|
|
|
|
|
case announce.KindTool:
|
|
|
|
|
m := x.Modules[e.Module]
|
|
|
|
|
if m == nil {
|
|
|
|
|
m = &moduleInfo{Module: e.Module}
|
|
|
|
|
x.Modules[e.Module] = m
|
|
|
|
|
}
|
|
|
|
|
if e.Node != "" && !contains(m.On, e.Node) {
|
|
|
|
|
m.On = append(m.On, e.Node)
|
|
|
|
|
}
|
|
|
|
|
m.Interchangeable = m.Interchangeable || e.Interchangeable
|
|
|
|
|
key := e.Module + "." + e.Tool
|
|
|
|
|
i, have := toolAt[key]
|
|
|
|
|
if !have {
|
|
|
|
|
i = len(l.Tools)
|
|
|
|
|
toolAt[key] = i
|
|
|
|
|
l.Tools = append(l.Tools, Tool{Module: e.Module, Name: e.Tool, Description: e.Description, Input: e.Schema})
|
|
|
|
|
}
|
|
|
|
|
if !contains(l.Tools[i].Subjects, e.Subject) {
|
|
|
|
|
l.Tools[i].Subjects = append(l.Tools[i].Subjects, e.Subject)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// A tool's subjects as a call looks them up: the plain one any instance answers first, then each
|
|
|
|
|
// machine's.
|
|
|
|
|
for i := range l.Tools {
|
|
|
|
|
t := &l.Tools[i]
|
|
|
|
|
if t.Seat {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
plain := "mesh.mod." + t.Module + ".tool." + t.Name
|
|
|
|
|
sort.SliceStable(t.Subjects, func(a, b int) bool {
|
|
|
|
|
if (t.Subjects[a] == plain) != (t.Subjects[b] == plain) {
|
|
|
|
|
return t.Subjects[a] == plain
|
|
|
|
|
}
|
|
|
|
|
return t.Subjects[a] < t.Subjects[b]
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
for _, t := range l.Tools {
|
|
|
|
|
if !t.Seat {
|
|
|
|
|
x.Modules[t.Module].Tools = append(x.Modules[t.Module].Tools, t)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for _, m := range x.Modules {
|
|
|
|
|
sort.Strings(m.On)
|
|
|
|
|
}
|
|
|
|
|
for _, st := range seats {
|
|
|
|
|
sort.Slice(st.Holders, func(a, b int) bool { return st.Holders[a].Node < st.Holders[b].Node })
|
|
|
|
|
x.Seats = append(x.Seats, *st)
|
|
|
|
|
}
|
|
|
|
|
sort.Slice(x.Seats, func(i, j int) bool { return x.Seats[i].Seat < x.Seats[j].Seat })
|
|
|
|
|
sort.SliceStable(l.Tools, func(i, j int) bool {
|
|
|
|
|
return l.Tools[i].Module+"."+l.Tools[i].Name < l.Tools[j].Module+"."+l.Tools[j].Name
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// What should have answered: every assignment of a module that declares tools. Silence is named;
|
|
|
|
|
// a module with no tools is never a name here.
|
|
|
|
@@ -410,6 +327,104 @@ func (s *Surface) indexAsked(fresh bool) (*index, error) {
|
|
|
|
|
return x, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// indexOfEndpoints is what the announced endpoints say: every seat with its verbs and holders, every
|
|
|
|
|
// module with its tools and machines, the flat listing, and which module announced on which machine.
|
|
|
|
|
func indexOfEndpoints(endpoints []announce.Endpoint) (*index, map[string]map[string]bool, map[string]bool) {
|
|
|
|
|
l := &Listing{Tools: []Tool{}, NotAnswering: []string{}}
|
|
|
|
|
x := &index{Modules: map[string]*moduleInfo{}, Listing: l, Recorded: map[string][]string{}}
|
|
|
|
|
announced := map[string]map[string]bool{} // module → node → announced something
|
|
|
|
|
seats := map[string]*seatInfo{}
|
|
|
|
|
// <module>.<tool>, or seat:<seat>.<verb>, → index in l.Tools. **Apart**, because a seat and a module may
|
|
|
|
|
// share a name — mesh-delivery is the delivery's seat and the module holding it (novox/hq ADR 0239), and
|
|
|
|
|
// the module answers the seat's verbs with tools of the same names: in one namespace the module's tool
|
|
|
|
|
// took the key first, and the seat was listed with no verb at all (novox/hq issue 287).
|
|
|
|
|
toolAt := map[string]int{}
|
|
|
|
|
machines := map[string]bool{}
|
|
|
|
|
for _, e := range endpoints {
|
|
|
|
|
if e.Node != "" {
|
|
|
|
|
machines[e.Node] = true
|
|
|
|
|
}
|
|
|
|
|
if announced[e.Module] == nil {
|
|
|
|
|
announced[e.Module] = map[string]bool{}
|
|
|
|
|
}
|
|
|
|
|
announced[e.Module][e.Node] = true
|
|
|
|
|
switch e.Kind {
|
|
|
|
|
case announce.KindSeat:
|
|
|
|
|
st := seats[e.Seat]
|
|
|
|
|
if st == nil {
|
|
|
|
|
st = &seatInfo{Seat: e.Seat, Scope: e.Scope}
|
|
|
|
|
seats[e.Seat] = st
|
|
|
|
|
}
|
|
|
|
|
if st.Scope != "node" && e.Scope == "node" {
|
|
|
|
|
st.Scope = "node"
|
|
|
|
|
}
|
|
|
|
|
h := holder{Module: e.Module, Node: e.Node}
|
|
|
|
|
if !containsHolder(st.Holders, h) {
|
|
|
|
|
st.Holders = append(st.Holders, h)
|
|
|
|
|
}
|
|
|
|
|
key := "seat:" + e.Seat + "." + e.Tool
|
|
|
|
|
if _, have := toolAt[key]; !have {
|
|
|
|
|
toolAt[key] = len(l.Tools)
|
|
|
|
|
t := Tool{Module: e.Seat, Name: e.Tool, Description: e.Description, Input: e.Schema, Seat: true, Scope: st.Scope}
|
|
|
|
|
l.Tools = append(l.Tools, t)
|
|
|
|
|
st.Verbs = append(st.Verbs, t)
|
|
|
|
|
}
|
|
|
|
|
case announce.KindTool:
|
|
|
|
|
m := x.Modules[e.Module]
|
|
|
|
|
if m == nil {
|
|
|
|
|
m = &moduleInfo{Module: e.Module}
|
|
|
|
|
x.Modules[e.Module] = m
|
|
|
|
|
}
|
|
|
|
|
if e.Node != "" && !contains(m.On, e.Node) {
|
|
|
|
|
m.On = append(m.On, e.Node)
|
|
|
|
|
}
|
|
|
|
|
m.Interchangeable = m.Interchangeable || e.Interchangeable
|
|
|
|
|
key := e.Module + "." + e.Tool
|
|
|
|
|
i, have := toolAt[key]
|
|
|
|
|
if !have {
|
|
|
|
|
i = len(l.Tools)
|
|
|
|
|
toolAt[key] = i
|
|
|
|
|
l.Tools = append(l.Tools, Tool{Module: e.Module, Name: e.Tool, Description: e.Description, Input: e.Schema})
|
|
|
|
|
}
|
|
|
|
|
if !contains(l.Tools[i].Subjects, e.Subject) {
|
|
|
|
|
l.Tools[i].Subjects = append(l.Tools[i].Subjects, e.Subject)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// A tool's subjects as a call looks them up: the plain one any instance answers first, then each
|
|
|
|
|
// machine's.
|
|
|
|
|
for i := range l.Tools {
|
|
|
|
|
t := &l.Tools[i]
|
|
|
|
|
if t.Seat {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
plain := "mesh.mod." + t.Module + ".tool." + t.Name
|
|
|
|
|
sort.SliceStable(t.Subjects, func(a, b int) bool {
|
|
|
|
|
if (t.Subjects[a] == plain) != (t.Subjects[b] == plain) {
|
|
|
|
|
return t.Subjects[a] == plain
|
|
|
|
|
}
|
|
|
|
|
return t.Subjects[a] < t.Subjects[b]
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
for _, t := range l.Tools {
|
|
|
|
|
if !t.Seat {
|
|
|
|
|
x.Modules[t.Module].Tools = append(x.Modules[t.Module].Tools, t)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for _, m := range x.Modules {
|
|
|
|
|
sort.Strings(m.On)
|
|
|
|
|
}
|
|
|
|
|
for _, st := range seats {
|
|
|
|
|
sort.Slice(st.Holders, func(a, b int) bool { return st.Holders[a].Node < st.Holders[b].Node })
|
|
|
|
|
x.Seats = append(x.Seats, *st)
|
|
|
|
|
}
|
|
|
|
|
sort.Slice(x.Seats, func(i, j int) bool { return x.Seats[i].Seat < x.Seats[j].Seat })
|
|
|
|
|
sort.SliceStable(l.Tools, func(i, j int) bool {
|
|
|
|
|
return l.Tools[i].Module+"."+l.Tools[i].Name < l.Tools[j].Module+"."+l.Tools[j].Name
|
|
|
|
|
})
|
|
|
|
|
return x, announced, machines
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// target is what an address resolves to.
|
|
|
|
|
type target struct {
|
|
|
|
|
Address string
|
|
|
|
@@ -433,9 +448,21 @@ func resolve(x *index, address string) (target, error) {
|
|
|
|
|
}
|
|
|
|
|
prefix, name := rest[:dot], rest[dot+1:]
|
|
|
|
|
|
|
|
|
|
if s := x.seat(prefix); s != nil {
|
|
|
|
|
s := x.seat(prefix)
|
|
|
|
|
if s != nil && x.Modules[prefix] != nil && findTool(x.Modules[prefix].Tools, name) != nil {
|
|
|
|
|
// **A seat and a module of one name** (novox/hq issue 287): mesh-delivery is the delivery's seat and
|
|
|
|
|
// the module holding it. The module's tool is meant when the seat has no such verb, or when a machine
|
|
|
|
|
// is named for a seat held once for the mesh — `<node>/<module>.<tool>` is a module on one machine.
|
|
|
|
|
if findTool(s.Verbs, name) == nil || (node != "" && s.Scope != "node") {
|
|
|
|
|
s = nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if s != nil {
|
|
|
|
|
verb := findTool(s.Verbs, name)
|
|
|
|
|
if verb == nil {
|
|
|
|
|
if len(s.Verbs) == 0 {
|
|
|
|
|
return target{}, fmt.Errorf("the seat %s has no verb %s: its holder announced none", prefix, name)
|
|
|
|
|
}
|
|
|
|
|
return target{}, fmt.Errorf("the seat %s has no verb %s; it has %s", prefix, name, toolNames(s.Verbs))
|
|
|
|
|
}
|
|
|
|
|
if s.Scope == "node" {
|
|
|
|
|