Files
mesh-catalog/modules/mesh-self-check/cmd/mesh-self-check/main.go
T
jschoubben c9d8950fd0 Every module follows the assigner's new places: the contracts' go/ path, mesh-wants in the delivery contracts, the state and tool assignments (issue 1966)
The legacy key form placement.<node> is gone from the client, so the readers and their fixtures lose it. The check noplacement
now also refuses the assigner's retired names in a manifest.

Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
2026-11-05 22:29:07 +01:00

116 lines
4.1 KiB
Go

// mesh-self-check: the holder of the seat mesh-self-check (novox/hq ADR 0564 §6). A Go bundle served by a runtime of
// its own, on its own bus account (`runs-as`), so that its account alone writes its buckets: `findings` (what it finds
// wrong now, which the condition keeper raises and clears conditions from), `verdict` (the last run) and `verdicts`
// (every run, kept a week). It runs the probes every five minutes and says `checked` at the end of each run. stdout is
// the MCP channel; what this module says, it says on stderr.
package main
import (
"context"
"encoding/json"
"fmt"
"os"
"time"
placing "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner"
"git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient"
stdio "git.novox.be/novox/mesh-sdk/go"
"git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshhandactsclient"
"git.novox.be/novox/mesh-sdk/go/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{ views *views.Reader }
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
}
return e.Value, true, nil
}
func (runtimeStore) Put(state, key string, value any) error {
_, err := stdio.State(state).Put(key, value)
return err
}
func (runtimeStore) Delete(state, key string) error { return stdio.State(state).Delete(key) }
func (r runtimeStore) Keys(state string) ([]string, error) {
if r.followed(state) {
return r.views.Keys(state)
}
return stdio.State(state).Keys()
}
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
})
}
// NewChecker is the self-check over the runtime.
func NewChecker() *Checker {
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: meshconditionsclient.FindingWriter{State: stdio.State("findings")}, Acts: meshhandactsclient.Recorder{Ask: stdio.Ask}}
r.Follow(assignerModule, placing.StateAssignments, AssignmentsTool)
r.Follow(assignerModule, placing.StateSeatHolders, SeatHoldersTool)
r.Follow(indexerModule, "modules", ModulesTool)
r.Follow(indexerModule, "manifests", ManifestsTool)
return r
}
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("", append(Tools(c), c.Store.(runtimeStore).views.Tools("mesh_self_check")...)); err != nil {
logf("%v", err)
os.Exit(1)
}
}
// enabledProbes is the setting `probes`, read once at each run so a change applies at the next; with it unreadable, the
// default list.
func enabledProbes() []string {
raw, err := stdio.Config("probes")
if err != nil {
logf("the setting probes cannot be read (%v); the default list runs", err)
return DefaultProbes
}
var list []string
if json.Unmarshal(raw, &list) != nil {
logf("the setting probes is not a list of probe ids (%s); the default list runs", raw)
return DefaultProbes
}
return list
}