Serve route-proxy's routes through its own contract: decode each plan entry's opaque contribution with it, say each name served, withdrawn or refused on the bus, and take who is inside from the seat mesh-placement, since the membership went with the assignments stream (issue 774, research 072 rows 33 and 34)
This commit is contained in:
@@ -14,7 +14,7 @@ import (
|
||||
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||
"git.novox.be/novox/mesh-sdk/go/provider"
|
||||
|
||||
"route-proxy/internal/routestate"
|
||||
"git.novox.be/novox/mesh-controller/modules/route-proxy/contract"
|
||||
)
|
||||
|
||||
func say(format string, args ...any) {
|
||||
@@ -36,7 +36,7 @@ func main() {
|
||||
}
|
||||
}()
|
||||
}
|
||||
tools := front{routes: stdio.State(routestate.RoutesState), unrouted: stdio.State(routestate.UnroutedState),
|
||||
tools := front{routes: stdio.State(contract.RoutesState), unrouted: stdio.State(contract.UnroutedState),
|
||||
now: time.Now}.tools()
|
||||
if err := stdio.Serve("", tools); err != nil {
|
||||
say("%v", err)
|
||||
|
||||
@@ -13,13 +13,13 @@ package main
|
||||
import (
|
||||
"git.novox.be/novox/mesh-sdk/go/provider"
|
||||
|
||||
"route-proxy/internal/routestate"
|
||||
"git.novox.be/novox/mesh-controller/modules/route-proxy/contract"
|
||||
)
|
||||
|
||||
// ProvisionName is what route-proxy provides, as its manifest says, and ModuleName this module.
|
||||
const (
|
||||
ProvisionName = routestate.Provision
|
||||
ModuleName = routestate.Module
|
||||
ProvisionName = contract.Provision
|
||||
ModuleName = contract.Module
|
||||
)
|
||||
|
||||
// newProvider is route-proxy answering its consumers over bus on node: the route, served as the manifest serves it
|
||||
|
||||
@@ -14,14 +14,14 @@ import (
|
||||
|
||||
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||
|
||||
"route-proxy/internal/routestate"
|
||||
"git.novox.be/novox/mesh-controller/modules/route-proxy/contract"
|
||||
)
|
||||
|
||||
// The tools' names, as the manifest declares them.
|
||||
const (
|
||||
RoutesTool = "route_proxy_routes"
|
||||
RouteTool = "route_proxy_route"
|
||||
UnroutedTool = "route_proxy_unrouted"
|
||||
RoutesTool = contract.ToolRoutes
|
||||
RouteTool = contract.ToolRoute
|
||||
UnroutedTool = contract.ToolUnrouted
|
||||
)
|
||||
|
||||
// reader is one of the module's states, as stdio.KeptState reads it.
|
||||
@@ -46,9 +46,9 @@ type RouteLine struct {
|
||||
// unknown before any request.
|
||||
Reachable string `json:"reachable"`
|
||||
// Certificate is its outcome, issuer and expiry in words; ExpiresInDays is the whole days until not-after.
|
||||
Certificate string `json:"certificate"`
|
||||
ExpiresInDays *int `json:"expires_in_days,omitempty"`
|
||||
LastAnswer *routestate.Answer `json:"last_answer,omitempty"`
|
||||
Certificate string `json:"certificate"`
|
||||
ExpiresInDays *int `json:"expires_in_days,omitempty"`
|
||||
LastAnswer *contract.Answer `json:"last_answer,omitempty"`
|
||||
}
|
||||
|
||||
func (f front) tools() []stdio.Tool {
|
||||
@@ -99,7 +99,7 @@ func read[T any](state reader, node string) ([]T, error) {
|
||||
}
|
||||
var out []T
|
||||
for _, key := range keys {
|
||||
at, _ := routestate.Split(key)
|
||||
at, _ := contract.Split(key)
|
||||
if node != "" && at != node {
|
||||
continue
|
||||
}
|
||||
@@ -120,7 +120,7 @@ func read[T any](state reader, node string) ([]T, error) {
|
||||
}
|
||||
|
||||
func (f front) list(node string) (map[string]any, error) {
|
||||
routes, err := read[routestate.Route](f.routes, node)
|
||||
routes, err := read[contract.Route](f.routes, node)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -131,7 +131,7 @@ func (f front) list(node string) (map[string]any, error) {
|
||||
if line.Reachable == "no" {
|
||||
unreachable++
|
||||
}
|
||||
if line.ExpiresInDays != nil && *line.ExpiresInDays < 14 || r.Certificate.Outcome == routestate.Failed {
|
||||
if line.ExpiresInDays != nil && *line.ExpiresInDays < 14 || r.Certificate.Outcome == contract.Failed {
|
||||
expiring++
|
||||
}
|
||||
lines = append(lines, line)
|
||||
@@ -146,7 +146,7 @@ func (f front) list(node string) (map[string]any, error) {
|
||||
"certificates_failing_or_within_14_days": expiring}, nil
|
||||
}
|
||||
|
||||
func (f front) line(r routestate.Route) RouteLine {
|
||||
func (f front) line(r contract.Route) RouteLine {
|
||||
line := RouteLine{Name: r.Name, Proxy: r.Proxy, Reach: r.Reach, LastAnswer: r.LastAnswer, Reachable: "unknown",
|
||||
Targets: []string{}}
|
||||
for _, t := range r.Targets {
|
||||
@@ -182,7 +182,7 @@ func (f front) line(r routestate.Route) RouteLine {
|
||||
}
|
||||
|
||||
// describe is one rule in a few words: path → module@node at target, or what it answers with.
|
||||
func describe(t routestate.Target) string {
|
||||
func describe(t contract.Target) string {
|
||||
path := t.Path
|
||||
if path == "" {
|
||||
path = "/"
|
||||
@@ -209,11 +209,11 @@ func (f front) one(name, node string) (map[string]any, error) {
|
||||
if name == "" {
|
||||
return nil, fmt.Errorf("name is the site to read, such as app.example.org")
|
||||
}
|
||||
routes, err := read[routestate.Route](f.routes, node)
|
||||
routes, err := read[contract.Route](f.routes, node)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var found []routestate.Route
|
||||
var found []contract.Route
|
||||
for _, r := range routes {
|
||||
if r.Name == name {
|
||||
found = append(found, r)
|
||||
@@ -248,32 +248,32 @@ func (f front) refusals(node string) (map[string]any, error) {
|
||||
}
|
||||
sort.Slice(asked, func(i, j int) bool { return asked[i].AskedAt.After(asked[j].AskedAt) })
|
||||
if asked == nil {
|
||||
asked = []routestate.Unrouted{}
|
||||
asked = []contract.Unrouted{}
|
||||
}
|
||||
return map[string]any{"unrouted": asked, "names": len(asked), "other_refusals": others}, nil
|
||||
}
|
||||
|
||||
// asked is the names refused and kept by name, and each proxy's count of the refusals not kept by name.
|
||||
func (f front) asked(node string) ([]routestate.Unrouted, []routestate.Other, error) {
|
||||
func (f front) asked(node string) ([]contract.Unrouted, []contract.Other, error) {
|
||||
keys, err := f.unrouted.Keys()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
names, others := keySet{}, keySet{}
|
||||
for _, key := range keys {
|
||||
if _, name := routestate.Split(key); name == routestate.OtherName {
|
||||
if _, name := contract.Split(key); name == contract.OtherName {
|
||||
others[key] = true
|
||||
} else {
|
||||
names[key] = true
|
||||
}
|
||||
}
|
||||
asked, err := read[routestate.Unrouted](only{f.unrouted, names}, node)
|
||||
asked, err := read[contract.Unrouted](only{f.unrouted, names}, node)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
counted, err := read[routestate.Other](only{f.unrouted, others}, node)
|
||||
counted, err := read[contract.Other](only{f.unrouted, others}, node)
|
||||
if counted == nil {
|
||||
counted = []routestate.Other{}
|
||||
counted = []contract.Other{}
|
||||
}
|
||||
return asked, counted, err
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||
|
||||
"route-proxy/internal/routestate"
|
||||
"git.novox.be/novox/mesh-controller/modules/route-proxy/contract"
|
||||
)
|
||||
|
||||
// held is a state as the bus holds it, read-only.
|
||||
@@ -40,19 +40,19 @@ func TestTheWebFrontsToolsAnswerFromTheBus(t *testing.T) {
|
||||
now := time.Date(2026, 10, 30, 21, 0, 0, 0, time.UTC)
|
||||
expires := now.Add(30 * 24 * time.Hour)
|
||||
routes := held{
|
||||
"anchor.grafana.example": routestate.Route{Name: "grafana.example", Proxy: "anchor", Reach: routestate.ReachPublic,
|
||||
Targets: []routestate.Target{{Module: "grafana", Node: "home-server", Target: "http://10.0.0.2:3000"}},
|
||||
Certificate: routestate.Certificate{Authority: routestate.ReachPublic, Issuer: "Let's Encrypt R11", NotAfter: &expires, Outcome: routestate.Issued},
|
||||
LastAnswer: &routestate.Answer{Status: 502, At: now, Error: "dial tcp 10.0.0.2:3000: connect: connection refused"}},
|
||||
"anchor.drive.example": routestate.Route{Name: "drive.example", Proxy: "anchor", Reach: routestate.ReachPublic,
|
||||
Targets: []routestate.Target{{Module: "nextcloud", Node: "home-server", Target: "http://10.0.0.2:8080"}},
|
||||
Certificate: routestate.Certificate{Authority: routestate.ReachPublic},
|
||||
LastAnswer: &routestate.Answer{Status: 200, At: now}},
|
||||
"anchor.grafana.example": contract.Route{Name: "grafana.example", Proxy: "anchor", Reach: contract.ReachPublic,
|
||||
Targets: []contract.Target{{Module: "grafana", Node: "home-server", Target: "http://10.0.0.2:3000"}},
|
||||
Certificate: contract.Certificate{Authority: contract.ReachPublic, Issuer: "Let's Encrypt R11", NotAfter: &expires, Outcome: contract.Issued},
|
||||
LastAnswer: &contract.Answer{Status: 502, At: now, Error: "dial tcp 10.0.0.2:3000: connect: connection refused"}},
|
||||
"anchor.drive.example": contract.Route{Name: "drive.example", Proxy: "anchor", Reach: contract.ReachPublic,
|
||||
Targets: []contract.Target{{Module: "nextcloud", Node: "home-server", Target: "http://10.0.0.2:8080"}},
|
||||
Certificate: contract.Certificate{Authority: contract.ReachPublic},
|
||||
LastAnswer: &contract.Answer{Status: 200, At: now}},
|
||||
}
|
||||
unrouted := held{
|
||||
"anchor.old.example": routestate.Unrouted{Name: "old.example", Proxy: "anchor", AskedAt: now.Add(-time.Hour), Times: 3},
|
||||
"anchor.wiki.example": routestate.Unrouted{Name: "wiki.example", Proxy: "anchor", AskedAt: now, Times: 1},
|
||||
"anchor._other": routestate.Other{Proxy: "anchor", Minute: now, LastMinute: 40, SinceStart: 900},
|
||||
"anchor.old.example": contract.Unrouted{Name: "old.example", Proxy: "anchor", AskedAt: now.Add(-time.Hour), Times: 3},
|
||||
"anchor.wiki.example": contract.Unrouted{Name: "wiki.example", Proxy: "anchor", AskedAt: now, Times: 1},
|
||||
"anchor._other": contract.Other{Proxy: "anchor", Minute: now, LastMinute: 40, SinceStart: 900},
|
||||
}
|
||||
tools := map[string]stdio.Tool{}
|
||||
for _, tool := range (front{routes: routes, unrouted: unrouted, now: func() time.Time { return now }}).tools() {
|
||||
|
||||
Reference in New Issue
Block a user