No client names a holder: each reader keeps its own constant, held to its manifest (ADR 0625 section 4, issue 1966)
Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
This commit is contained in:
@@ -85,7 +85,12 @@ const (
|
||||
// not read here leaves its keys asked, saying its entries are not read. route-proxy (mesh-control) and node-tools
|
||||
// (mesh-tools) answer route and mcp-endpoint by the provider helper from outside this catalogue (issue 774, sweep of
|
||||
// 2026-10-30 20:10: their keys stood asked on every node).
|
||||
var BoundReads = []string{"gitea", "keycloak", "postgres", "route-adapter", "route-proxy", "node-tools", "mesh-vault"}
|
||||
var BoundReads = []string{"gitea", "keycloak", "postgres", "route-adapter", "route-proxy", "node-tools", vaultModule}
|
||||
|
||||
// vaultModule is the holder of the seat mesh-vault, the provider of every credential: named here from this module's own
|
||||
// manifest (its `reads` has mesh-vault.bound; TestTheManifestDeclaresWhatTheCodeKeeps), since no client names a holder
|
||||
// (novox/hq ADR 0625 §4).
|
||||
const vaultModule = "mesh-vault"
|
||||
|
||||
// boundRead is a provider's entries as this module reads them.
|
||||
func boundRead(module string) string { return module + ".bound" }
|
||||
@@ -299,7 +304,7 @@ func (h *Holder) ComposeNodeWith(ctx context.Context, node string, retireMany bo
|
||||
continue
|
||||
}
|
||||
cred := meshprovisionerclient.Provision{Provision: cp, Local: cl, Consumer: meshprovisionerclient.End{Module: cm, Node: cn},
|
||||
Provider: meshprovisionerclient.End{Module: meshprovisionerclient.VaultModule, Node: vaultNode}}
|
||||
Provider: meshprovisionerclient.End{Module: vaultModule, Node: vaultNode}}
|
||||
if cn == node {
|
||||
if _, seen := needed[ck]; !seen {
|
||||
needed[ck] = cred
|
||||
@@ -404,7 +409,7 @@ func (h *Holder) providerPlan(p catalogue.PlanProvision, node string, here map[s
|
||||
|
||||
// vaultNode is the node mesh-vault is placed on: the provider of every credential.
|
||||
func (h *Holder) vaultNode() string {
|
||||
nodes := h.placedOn(meshprovisionerclient.VaultModule)
|
||||
nodes := h.placedOn(vaultModule)
|
||||
if len(nodes) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -134,9 +134,9 @@ func TestTheHolderPlansProvidesAndAsksOnTheBus(t *testing.T) {
|
||||
published(t, mem, c)
|
||||
vaultManifest := `{"module":"mesh-vault","version":"1","state":["bound"]}` // the vault keeps its outcomes as entries
|
||||
vaultDigest := catalogclient.ManifestDigest([]byte(vaultManifest))
|
||||
vaultValue, _ := catalogclient.EncodeManifest(catalog.ManifestsByDigestValue{Module: meshprovisionerclient.VaultModule, Manifest: vaultManifest})
|
||||
vaultValue, _ := catalogclient.EncodeManifest(catalog.ManifestsByDigestValue{Module: vaultModule, Manifest: vaultManifest})
|
||||
mem.write(t, ManifestsRead, mustKey(catalogclient.ManifestsKey(vaultDigest)), string(vaultValue))
|
||||
mem.write(t, ModulesRead, meshprovisionerclient.VaultModule, `{"v":1,"none2":"the test's","manifest":"`+vaultDigest+`"}`)
|
||||
mem.write(t, ModulesRead, vaultModule, `{"v":1,"none2":"the test's","manifest":"`+vaultDigest+`"}`)
|
||||
for state, keys := range mem.states {
|
||||
for key, raw := range keys {
|
||||
if err := s.Put(state, key, json.RawMessage(raw)); err != nil {
|
||||
@@ -217,7 +217,7 @@ func TestTheHolderPlansProvidesAndAsksOnTheBus(t *testing.T) {
|
||||
}
|
||||
// The providers' outcomes: the vault provides the credential where restic has one, postgres runs healthy.
|
||||
for _, dep := range state(key).DependsOn {
|
||||
if err := s.Put(boundRead(meshprovisionerclient.VaultModule), dep, map[string]any{"state": "provided"}); err != nil {
|
||||
if err := s.Put(boundRead(vaultModule), dep, map[string]any{"state": "provided"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -334,6 +334,10 @@ func TestTheManifestDeclaresWhatTheCodeKeeps(t *testing.T) {
|
||||
t.Errorf("the manifest lists %s, which is not served", tool)
|
||||
}
|
||||
}
|
||||
// The vault the holder places credentials on is the holder its manifest reads the bound outcomes of.
|
||||
if !slices.Contains(m.Reads, vaultModule+".bound") || !slices.Contains(BoundReads, vaultModule) {
|
||||
t.Errorf("the vault is %q: the manifest reads %v and the holder reads the bound states of %v", vaultModule, m.Reads, BoundReads)
|
||||
}
|
||||
// The builds read serves only haveOutcome's offer check (ADR 0603 clause 5: which manifest the build a provider runs
|
||||
// was made from) and makes no plan, so ADR 0593 §8 ("it composes nothing") holds.
|
||||
// The assigner's placement and wants and the indexer's modules and manifests are asked once of their owners' read
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
assignerclient "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient"
|
||||
placing "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner"
|
||||
|
||||
"git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient"
|
||||
@@ -78,8 +77,8 @@ func NewChecker() *Checker {
|
||||
// self-check's own (first-view-unanswered), written into its state findings.
|
||||
func newViews() *views.Reader {
|
||||
r := &views.Reader{Module: Module, Logf: logf, Findings: meshconditionsclient.FindingWriter{State: stdio.State("findings")}, Acts: meshhandactsclient.Recorder{Ask: stdio.Ask}}
|
||||
r.Follow(assignerclient.Module, placing.StatePlacement, PlacementTool)
|
||||
r.Follow(assignerclient.Module, placing.StateSeatHolders, SeatHoldersTool)
|
||||
r.Follow(assignerModule, placing.StatePlacement, PlacementTool)
|
||||
r.Follow(assignerModule, placing.StateSeatHolders, SeatHoldersTool)
|
||||
r.Follow(indexerModule, "modules", ModulesTool)
|
||||
r.Follow(indexerModule, "manifests", ManifestsTool)
|
||||
return r
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
placing "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner"
|
||||
)
|
||||
|
||||
type manifest struct {
|
||||
@@ -69,6 +71,14 @@ func TestTheManifestSaysWhatTheCodeDoes(t *testing.T) {
|
||||
if !slices.Equal(m.Consume, Consumes) {
|
||||
t.Errorf("consumes %v, want the views families the probes follow, %v", m.Consume, Consumes)
|
||||
}
|
||||
for _, view := range []string{assignerModule + ".views." + placing.StatePlacement, assignerModule + ".views." + placing.StateSeatHolders} {
|
||||
if !slices.Contains(m.Consume, view) {
|
||||
t.Errorf("the probes follow %s, the manifest consumes %v", view, m.Consume)
|
||||
}
|
||||
}
|
||||
if PlacementRead != "mesh-delivery-assigner.placement" || SeatHoldersRead != "mesh-delivery-assigner.seat-holders" {
|
||||
t.Errorf("the states are read as %s and %s", PlacementRead, SeatHoldersRead)
|
||||
}
|
||||
states := map[string]int{}
|
||||
for _, s := range m.State {
|
||||
var d struct {
|
||||
|
||||
@@ -26,8 +26,8 @@ import (
|
||||
|
||||
// The states the machine probes read (declared under `reads`), and the verb D8 asks each node.
|
||||
const (
|
||||
PlacementRead = assignerclient.Module + "." + "placement"
|
||||
SeatHoldersRead = assignerclient.SeatHoldersRead
|
||||
PlacementRead = assignerModule + "." + placing.StatePlacement
|
||||
SeatHoldersRead = assignerModule + "." + placing.StateSeatHolders
|
||||
PresenceRead = watchmanModule + ".presence"
|
||||
BannedSeat = "node-intrusion-prevention"
|
||||
BannedVerb = "seat:" + BannedSeat + ".banned"
|
||||
@@ -226,7 +226,9 @@ func probeBans(ctx context.Context, c *Checker) ([]meshconditionsclient.Observat
|
||||
|
||||
// The holders whose states the probes read, one constant each (a reader names its holder once; ADR 0634 §4).
|
||||
const (
|
||||
watchmanModule = "mesh-delivery-watchman"
|
||||
watchmanModule = "mesh-delivery-watchman"
|
||||
// assignerModule is the holder of the seat mesh-module-assigner, named from this module's manifest (consumes: its views).
|
||||
assignerModule = "mesh-delivery-assigner"
|
||||
indexerModule = "mesh-delivery-indexer"
|
||||
nodeEngineModule = "mesh-host"
|
||||
)
|
||||
|
||||
@@ -122,12 +122,12 @@ func (v *Vault) outcomes() (map[string]BoundOutcome, error) {
|
||||
consumer := meshprovisionerclient.End{Module: a.Module, Node: a.Node}
|
||||
for _, p := range a.Provisioning.Provisions {
|
||||
provider := meshprovisionerclient.End{Module: p.ProviderModule, Node: p.Provider}
|
||||
if p.ProviderModule == meshprovisionerclient.VaultModule && p.Provision == "secret" {
|
||||
if p.ProviderModule == vaultModule && p.Provision == "secret" {
|
||||
// The vault's own offer: a value it hands the consumer's client on request (`get`), which it serves as
|
||||
// soon as it runs.
|
||||
key, err := meshprovisionerclient.Key(a.Node, a.Module, p.Provision, p.Local)
|
||||
if err == nil {
|
||||
out[key] = BoundOutcome{Provider: p.Provider, ProviderModule: meshprovisionerclient.VaultModule, Provision: p.Provision,
|
||||
out[key] = BoundOutcome{Provider: p.Provider, ProviderModule: vaultModule, Provision: p.Provision,
|
||||
Consumer: a.Node, Module: a.Module, Local: p.Local, State: meshprovisionerclient.Provided,
|
||||
Why: "the vault hands it to the consumer's client on request"}
|
||||
}
|
||||
@@ -143,7 +143,7 @@ func (v *Vault) outcomes() (map[string]BoundOutcome, error) {
|
||||
if err != nil || name == "" {
|
||||
continue
|
||||
}
|
||||
o := BoundOutcome{Provider: p.Provider, ProviderModule: meshprovisionerclient.VaultModule, Provision: cp, Consumer: cn,
|
||||
o := BoundOutcome{Provider: p.Provider, ProviderModule: vaultModule, Provision: cp, Consumer: cn,
|
||||
Module: cm, Local: cl, VaultName: name}
|
||||
rec, held, err := v.Store.Read(name)
|
||||
switch {
|
||||
|
||||
@@ -866,6 +866,6 @@ func (e *Env) credentialsState() []PairOutcome {
|
||||
// §1, §6): a provision `secret-<provision>` whose provider is the vault, in state asked.
|
||||
func credentialAsked(raw []byte) bool {
|
||||
p, err := meshprovisionerclient.Decode(raw)
|
||||
return err == nil && p.State == meshprovisionerclient.Asked && p.Provider.Module == meshprovisionerclient.VaultModule &&
|
||||
return err == nil && p.State == meshprovisionerclient.Asked && p.Provider.Module == vaultModule &&
|
||||
strings.HasPrefix(p.Provision, catalogue.CredentialProvisionPrefix)
|
||||
}
|
||||
|
||||
@@ -16,9 +16,6 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// PlacementRead is the seat mesh-module-assigner's placement, as the vault's manifest reads it (mesh-sdk's contract).
|
||||
const PlacementRead = assignerclient.PlacementRead
|
||||
|
||||
// PlacementReader reads the seat's placement: every key, and one key's value (nil when there is none).
|
||||
type PlacementReader struct {
|
||||
Keys func() ([]string, error)
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
vaultclient "git.novox.be/novox/mesh-contracts/identity-and-access/go/clients/meshvaultclient"
|
||||
assignerclient "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient"
|
||||
placing "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// placementsOf is the seat mesh-module-assigner's placement of each node, as the vault reads it.
|
||||
@@ -67,3 +71,22 @@ func TestTheVaultReadsWhereModulesStandFromPlacement(t *testing.T) {
|
||||
t.Errorf("two bus nodes: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// The assigner's tool and views family the vault names are the ones its manifest invokes and consumes; no client names a
|
||||
// holder (novox/hq ADR 0625 §4).
|
||||
func TestThePlacementNamesAreTheManifests(t *testing.T) {
|
||||
raw, err := os.ReadFile("../../module.json")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var m struct {
|
||||
Invokes []string `json:"invokes"`
|
||||
Consumes []string `json:"consumes"`
|
||||
}
|
||||
if err := json.Unmarshal(raw, &m); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !slices.Contains(m.Invokes, PlacementTool) || !slices.Contains(m.Consumes, PlacementViewsEvent) {
|
||||
t.Errorf("the vault asks %s and follows %s, its manifest invokes %v and consumes %v", PlacementTool, PlacementViewsEvent, m.Invokes, m.Consumes)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
assignerclient "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -22,8 +21,11 @@ import (
|
||||
|
||||
// PlacementTool is the assigner's ordinary read tool of its placement; PlacementViewsEvent the event of its views family.
|
||||
const (
|
||||
PlacementTool = assignerclient.Module + ".assigner_read_placement"
|
||||
PlacementViewsEvent = assignerclient.Module + ".views.placement"
|
||||
// assignerModule is the holder of the seat mesh-module-assigner, named from this module's manifest (invokes its read tool,
|
||||
// consumes its views; TestThePlacementNamesAreTheManifests).
|
||||
assignerModule = "mesh-delivery-assigner"
|
||||
PlacementTool = assignerModule + ".assigner_read_placement"
|
||||
PlacementViewsEvent = assignerModule + ".views.placement"
|
||||
FirstViewsTool = "vault_first_views"
|
||||
FirstViewAgainTool = "vault_first_view_again"
|
||||
)
|
||||
@@ -38,7 +40,7 @@ var placementView *views.Follower
|
||||
// it is held (a first view's keys too).
|
||||
func newPlacementView(ask func(key string, body any) (json.RawMessage, error), gone func(key string), changed func(),
|
||||
findings views.FindingWriter) *views.Follower {
|
||||
return &views.Follower{Owner: assignerclient.Module, Tool: PlacementTool, State: "placement", Logf: say,
|
||||
return &views.Follower{Owner: assignerModule, Tool: PlacementTool, State: "placement", Logf: say,
|
||||
Ask: func(_ context.Context, after string) (views.Answer, error) {
|
||||
body := map[string]any{"values": true}
|
||||
if after != "" {
|
||||
|
||||
Reference in New Issue
Block a user