CAT-RD6: mesh-self-check asks the owners' placement, seat-holders, modules and manifests once and follows their events, no reads
This commit is contained in:
@@ -13,14 +13,22 @@ import (
|
||||
"time"
|
||||
|
||||
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
|
||||
"git.novox.be/novox/mesh-delivery/sdk/views"
|
||||
)
|
||||
|
||||
func logf(format string, a ...any) { fmt.Fprintf(os.Stderr, format+"\n", a...) }
|
||||
|
||||
// runtimeStore is the bus, through the runtime that launched this bundle.
|
||||
type runtimeStore struct{}
|
||||
type runtimeStore struct{ views *views.Reader }
|
||||
|
||||
func (runtimeStore) Get(state, key string) ([]byte, bool, error) {
|
||||
func (r runtimeStore) followed(state string) bool { return r.views != nil && r.views.Follows(state) }
|
||||
|
||||
func (r runtimeStore) Get(state, key string) ([]byte, bool, error) {
|
||||
if r.followed(state) {
|
||||
return r.views.Get(state, key)
|
||||
}
|
||||
e, err := stdio.State(state).Get(key)
|
||||
if err != nil || e == nil {
|
||||
return nil, false, err
|
||||
@@ -35,9 +43,17 @@ func (runtimeStore) Put(state, key string, value any) error {
|
||||
|
||||
func (runtimeStore) Delete(state, key string) error { return stdio.State(state).Delete(key) }
|
||||
|
||||
func (runtimeStore) Keys(state string) ([]string, error) { return stdio.State(state).Keys() }
|
||||
func (r runtimeStore) Keys(state string) ([]string, error) {
|
||||
if r.followed(state) {
|
||||
return r.views.Keys(state)
|
||||
}
|
||||
return stdio.State(state).Keys()
|
||||
}
|
||||
|
||||
func (runtimeStore) Watch(state string, handle func(key string, value []byte, gone bool)) error {
|
||||
func (r runtimeStore) Watch(state string, handle func(key string, value []byte, gone bool)) error {
|
||||
if r.followed(state) {
|
||||
return r.views.Watch(state, handle)
|
||||
}
|
||||
return stdio.State(state).Watch("", func(ch stdio.StateChange) error {
|
||||
handle(ch.Key, ch.Value, ch.Op != stdio.OpPut)
|
||||
return nil
|
||||
@@ -46,20 +62,43 @@ func (runtimeStore) Watch(state string, handle func(key string, value []byte, go
|
||||
|
||||
// NewChecker is the self-check over the runtime.
|
||||
func NewChecker() *Checker {
|
||||
return &Checker{Store: runtimeStore{}, Registry: Registry(), Now: time.Now, Logf: logf, Host: os.Getenv("MESH_NODE"),
|
||||
store := runtimeStore{views: newViews()}
|
||||
return &Checker{Store: store, Registry: Registry(), Now: time.Now, Logf: logf, Host: os.Getenv("MESH_NODE"),
|
||||
Enabled: enabledProbes,
|
||||
Ask: func(key string, body any) (json.RawMessage, error) { return stdio.Ask(key, body) },
|
||||
Publish: stdio.SeatPublish}
|
||||
}
|
||||
|
||||
// newViews is the self-check's pictures of the indexer's and the assigner's states it probes (ADR 0615): each asked once of
|
||||
// its owner's read tool, then followed by the owner's events. A first view unanswered past its bound is a finding of the
|
||||
// self-check's own (first-view-unanswered), written into its state findings.
|
||||
func newViews() *views.Reader {
|
||||
r := &views.Reader{Module: Module, Logf: logf, Findings: findings{}}
|
||||
r.Follow(delivery.AssignerModule, delivery.PlacementState, PlacementTool)
|
||||
r.Follow(delivery.AssignerModule, delivery.SeatHoldersState, SeatHoldersTool)
|
||||
r.Follow(delivery.IndexerModule, "modules", ModulesTool)
|
||||
r.Follow(delivery.IndexerModule, "manifests", ManifestsTool)
|
||||
return r
|
||||
}
|
||||
|
||||
// findings writes the self-check's own state findings for the reader.
|
||||
type findings struct{}
|
||||
|
||||
func (findings) Put(key string, value []byte) error {
|
||||
_, err := stdio.State(FindingsState).Put(key, json.RawMessage(value))
|
||||
return err
|
||||
}
|
||||
func (findings) Delete(key string) error { return stdio.State(FindingsState).Delete(key) }
|
||||
|
||||
func main() {
|
||||
c := NewChecker()
|
||||
go func() {
|
||||
<-stdio.Served()
|
||||
c.Store.(runtimeStore).views.Start(context.Background())
|
||||
c.WatchKept(context.Background())
|
||||
c.Keep(context.Background())
|
||||
}()
|
||||
if err := stdio.Serve("", Tools(c)); err != nil {
|
||||
if err := stdio.Serve("", append(Tools(c), c.Store.(runtimeStore).views.Tools("mesh_self_check")...)); err != nil {
|
||||
logf("%v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ type manifest struct {
|
||||
Emits []string `json:"emits"`
|
||||
Invokes []string `json:"invokes"`
|
||||
Reads []string `json:"reads"`
|
||||
Consume []string `json:"consumes"`
|
||||
Tools []string `json:"tools"`
|
||||
State []json.RawMessage `json:"state"`
|
||||
}
|
||||
|
||||
@@ -57,6 +59,9 @@ func TestTheManifestSaysWhatTheCodeDoes(t *testing.T) {
|
||||
if !slices.Equal(m.Reads, Reads) {
|
||||
t.Errorf("reads %v, want what the probes read, %v", m.Reads, Reads)
|
||||
}
|
||||
if !slices.Equal(m.Consume, Consumes) {
|
||||
t.Errorf("consumes %v, want the views families the probes follow, %v", m.Consume, Consumes)
|
||||
}
|
||||
states := map[string]int{}
|
||||
for _, s := range m.State {
|
||||
var d struct {
|
||||
|
||||
@@ -33,8 +33,20 @@ const (
|
||||
// Invokes are the tools and seat verbs the probes ask, and Reads the states they read: the manifest's `invokes` and
|
||||
// `reads`, which its test holds to these.
|
||||
var (
|
||||
Invokes = []string{StreamsTool, ClosedTool, SignalsVerb, BannedVerb}
|
||||
Reads = []string{PlacementRead, SeatHoldersRead, PresenceRead, HealthRead, ReadingsRead, GoodRead, ModulesRead, ManifestsRead}
|
||||
Invokes = []string{StreamsTool, ClosedTool, SignalsVerb, BannedVerb, PlacementTool, SeatHoldersTool, ModulesTool, ManifestsTool}
|
||||
Reads = []string{PresenceRead, HealthRead, ReadingsRead, GoodRead}
|
||||
// Consumes are the views families the probes follow (ADR 0615): the indexer's and the assigner's states are asked
|
||||
// once, by their owners' read tools, and then followed by their events, never read.
|
||||
Consumes = []string{"mesh-delivery-assigner.views.placement", "mesh-delivery-assigner.views.seat-holders",
|
||||
"mesh-delivery-indexer.views.modules", "mesh-delivery-indexer.views.manifests"}
|
||||
)
|
||||
|
||||
// The owners' read tools of the states the probes follow, which the manifest invokes.
|
||||
const (
|
||||
PlacementTool = "mesh-delivery-assigner.assigner_read_placement"
|
||||
SeatHoldersTool = "mesh-delivery-assigner.assigner_read_seat_holders"
|
||||
ModulesTool = "mesh-delivery-indexer.indexer_read_modules"
|
||||
ManifestsTool = "mesh-delivery-indexer.indexer_read_manifests"
|
||||
)
|
||||
|
||||
// Registry is the self-check's registry, in to-be 45's order. **The registry is the design's live form**: a probe
|
||||
|
||||
@@ -16,17 +16,23 @@
|
||||
"nats.nats_streams",
|
||||
"nats.nats_closed_connections",
|
||||
"seat:mesh-watchdogs.signals",
|
||||
"seat:node-intrusion-prevention.banned"
|
||||
"seat:node-intrusion-prevention.banned",
|
||||
"mesh-delivery-assigner.assigner_read_placement",
|
||||
"mesh-delivery-assigner.assigner_read_seat_holders",
|
||||
"mesh-delivery-indexer.indexer_read_modules",
|
||||
"mesh-delivery-indexer.indexer_read_manifests"
|
||||
],
|
||||
"reads": [
|
||||
"mesh-delivery-assigner.placement",
|
||||
"mesh-delivery-assigner.seat-holders",
|
||||
"mesh-delivery-watchman.presence",
|
||||
"mesh-host.health",
|
||||
"disk-load.readings",
|
||||
"restic.good",
|
||||
"mesh-delivery-indexer.modules",
|
||||
"mesh-delivery-indexer.manifests"
|
||||
"restic.good"
|
||||
],
|
||||
"consumes": [
|
||||
"mesh-delivery-assigner.views.placement",
|
||||
"mesh-delivery-assigner.views.seat-holders",
|
||||
"mesh-delivery-indexer.views.modules",
|
||||
"mesh-delivery-indexer.views.manifests"
|
||||
],
|
||||
"state": [
|
||||
"findings",
|
||||
|
||||
Reference in New Issue
Block a user