Fix 9.7e: route-proxy names the seats it reads (seat:mesh-provisioner, seat:mesh-module-assigner), no module

Builds: design 47 G A-116 (§636, §616)

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
This commit is contained in:
2026-11-12 01:21:09 +01:00
parent 3c2239802d
commit ca6d8cd8eb
9 changed files with 109 additions and 34 deletions
+12 -5
View File
@@ -12,7 +12,10 @@ import (
"time"
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider"
meshprovisioner "git.novox.be/novox/mesh-contracts/provisioning/go/seats/meshprovisioner"
stdio "git.novox.be/novox/mesh-sdk/go"
"git.novox.be/novox/mesh-sdk/go/busretry"
"git.novox.be/novox/mesh-sdk/go/seatlist"
"route-proxy/contract"
)
@@ -26,11 +29,19 @@ func main() {
if node == "" {
say("MESH_NODE is not set; answering no consumers")
} else {
p := newProvider(node, provider.StateBus(ModuleName, provisionerModule), say)
go func() {
// The helper's watches go through the runtime's channel, which Serve holds: one asked before it does is
// refused ("before Serve"), Run returns and the helper stays stopped until the process restarts.
<-stdio.Served()
// The holder of the seat mesh-provisioner is read from the seat that places modules, not named here (A-116).
holder, err := busretry.DoRepeatable("who holds "+meshprovisioner.Seat, func() (string, error) {
return seatlist.HolderOf(stdio.Ask, meshprovisioner.Seat)
}, nil)
if err != nil {
say("the provider helper cannot start: %v", err)
return
}
p := newProvider(node, holder, provider.StateBus(ModuleName, holder), say)
if err := p.Run(context.Background()); err != nil {
say("the provider helper stopped: %v", err)
}
@@ -43,7 +54,3 @@ func main() {
os.Exit(1)
}
}
// provisionerModule is the holder of the seat mesh-provisioner, named from this module's manifest (its `reads` of plans and
// provisions; TestTheProvisionerIsTheManifests).
const provisionerModule = "mesh-provisioner"
@@ -88,8 +88,8 @@ func TestRouteProxyKeepsItsPublicAccountDirectory(t *testing.T) {
// seat mesh-provisioner's plans alone (issue 774, research 072 row 34): it reads them, and receives no file.
func TestRouteProxyRunsItsMountedProgramOnTheTrustImage(t *testing.T) {
m := ownManifest(t)
if !slices.Contains(m.Reads, "mesh-provisioner.plans") {
t.Errorf("route-proxy does not read mesh-provisioner.plans, where its routes are: %v", m.Reads)
if !slices.Contains(m.Reads, "seat:mesh-provisioner.plans") {
t.Errorf("route-proxy does not read seat:mesh-provisioner.plans, where its routes are: %v", m.Reads)
}
if len(m.Receives) > 0 {
t.Errorf("route-proxy receives %v: a route is served from the seat's plans, never a received file", m.Receives)
@@ -22,12 +22,13 @@ const (
ModuleName = contract.Module
)
// newProvider is route-proxy answering its consumers over bus on node: the route, served as the manifest serves it
// newProvider is route-proxy answering its consumers over bus on node, the plans and provisions being those of provisioner, the
// module holding the seat mesh-provisioner (read by seatlist.HolderOf; design 47 G A-116): the route, served as the manifest serves it
// (nothing of its own), with no credential, and nothing to make or retire for a consumer.
func newProvider(node string, bus provider.Bus, logf func(string, ...any)) *provider.Provider {
func newProvider(node, provisioner string, bus provider.Bus, logf func(string, ...any)) *provider.Provider {
return provider.New(provider.Options{
Node: node,
Module: ModuleName, Provisioner: provisionerModule,
Module: ModuleName, Provisioner: provisioner,
Offers: map[string]provider.Offer{ProvisionName: {
Credential: provider.NoCredential,
Serves: map[string]any{},
@@ -63,7 +63,7 @@ func TestAConsumerOfARouteIsToldItsNamesFromItsPlanEntry(t *testing.T) {
t.Fatal(err)
}
key := "g14." + module
if err := bus.Put(context.Background(), meshprovisionerclient.PlansBucketOf(provisionerModule), key, plan); err != nil {
if err := bus.Put(context.Background(), meshprovisionerclient.PlansBucketOf(theProvisioner), key, plan); err != nil {
t.Fatal(err)
}
asked, err := json.Marshal(meshprovisionerclient.Provision{V: meshprovisionerclient.Version, Provision: ProvisionName,
@@ -72,11 +72,11 @@ func TestAConsumerOfARouteIsToldItsNamesFromItsPlanEntry(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if err := bus.Put(context.Background(), meshprovisionerclient.ProvisionsBucketOf(provisionerModule), key+"."+ProvisionName, asked); err != nil {
if err := bus.Put(context.Background(), meshprovisionerclient.ProvisionsBucketOf(theProvisioner), key+"."+ProvisionName, asked); err != nil {
t.Fatal(err)
}
}
p := newProvider("novox", bus, t.Logf)
p := newProvider("novox", theProvisioner, bus, t.Logf)
out, err := p.Pass(context.Background())
if err != nil {
t.Fatal(err)
@@ -127,7 +127,7 @@ func TestRouteProxyIsNotMovedAndKeepsItsOwnFiles(t *testing.T) {
t.Fatalf("route is not said to be answered by the helper: %v", provides)
}
// Its states and its tools are what it says on the bus, not what it runs or writes (issue 774).
for _, k := range []string{"build", "provides", "state", "reads", "tools", "replaces"} {
for _, k := range []string{"build", "provides", "state", "reads", "invokes", "tools", "replaces"} {
delete(m, k)
}
// Refreshed on purpose for the events its contract names (`emits`: route.served, route.withdrawn, route.refused), and for the
@@ -158,3 +158,6 @@ func TestRouteProxyIsNotMovedAndKeepsItsOwnFiles(t *testing.T) {
}
}
}
// theProvisioner is the module a test has hold the seat mesh-provisioner: the code reads it, and names none.
const theProvisioner = "a-provisioner"
@@ -5,24 +5,31 @@ import (
"os"
"slices"
"testing"
"git.novox.be/novox/mesh-contracts/provisioning/go/seats/meshprovisioner"
"git.novox.be/novox/mesh-sdk/go/seatlist"
)
// The module holding the seat mesh-provisioner, which this provider reads the plans and provisions of, is the one its manifest's
// `reads` name: no client names a holder (novox/hq ADR 0625 §4).
// The seat mesh-provisioner, whose plans and provisions this provider reads, is named by the seat in its manifest's `reads`
// (`seat:<seat>.<state>`), and the manifest invokes the verb that says who holds it: no module is named (design 47 G A-116).
func TestTheProvisionerIsTheManifests(t *testing.T) {
raw, err := os.ReadFile("../../module.json")
if err != nil {
t.Fatal(err)
}
var m struct {
Reads []string `json:"reads"`
Reads []string `json:"reads"`
Invokes []string `json:"invokes"`
}
if err := json.Unmarshal(raw, &m); err != nil {
t.Fatal(err)
}
for _, state := range []string{"plans", "provisions"} {
if !slices.Contains(m.Reads, provisionerModule+"."+state) {
t.Errorf("the provider reads %s.%s, its manifest's reads are %v", provisionerModule, state, m.Reads)
for _, state := range []string{meshprovisioner.StatePlans, meshprovisioner.StateProvisions} {
if want := "seat:" + meshprovisioner.Seat + "." + state; !slices.Contains(m.Reads, want) {
t.Errorf("the provider reads %s, its manifest's reads are %v", want, m.Reads)
}
}
if want := seatlist.HolderKey(); !slices.Contains(m.Invokes, want) {
t.Errorf("the provider asks %s for the holder, its manifest invokes %v", want, m.Invokes)
}
}