G findings4 checker: the checker's check-could-not-run and its views reader's findings go through mesh-sdk's finding writer, which observes the raised ones
Builds: design 47 G §689, §686 (A-3), §639, §384, A-13 Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/nats-io/nats.go"
|
||||
"github.com/nats-io/nats.go/jetstream"
|
||||
|
||||
condseat "git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshconditions"
|
||||
"git.novox.be/novox/mesh-sdk/go/keys"
|
||||
"git.novox.be/novox/mesh-sdk/go/process"
|
||||
"git.novox.be/novox/mesh-sdk/go/views"
|
||||
|
||||
"mesh-delivery-checker/internal/check"
|
||||
)
|
||||
|
||||
type findingWriterOf = views.Writer[condseat.Finding, condseat.FindingCleared, condseat.FindingsStated, condseat.FindingsAskedEvent]
|
||||
|
||||
// checkerFindings is the checker's finding writer (design 47 G §461, §689): it owns the finding check-could-not-run beside
|
||||
// its views reader's, observes the raised ones of its kept checks, and writes both as the generated contract type. The
|
||||
// writer forms its subjects itself (A-13).
|
||||
type checkerFindings struct {
|
||||
w *findingWriterOf
|
||||
node string
|
||||
}
|
||||
|
||||
// newCheckerFindings makes and starts the writer; observe says the checks that could not run and whose finding is
|
||||
// written (check.Checker.RaisedCouldNotRun).
|
||||
func newCheckerFindings(ctx context.Context, cred process.Account, conn *nats.Conn, js jetstream.JetStream, state process.State,
|
||||
observe func(context.Context) ([]views.Finding, error), logf func(string, ...any)) (*checkerFindings, error) {
|
||||
c := &checkerFindings{node: cred.Node}
|
||||
w, err := views.NewWriter(views.WriterConfig[condseat.Finding, condseat.FindingCleared, condseat.FindingsStated, condseat.FindingsAskedEvent]{
|
||||
Module: cred.Module, State: state, Node: cred.Node, JS: js, Conn: conn, Logf: logf,
|
||||
Kinds: []string{check.CouldNotRunKind},
|
||||
Observe: func(ctx context.Context) (views.Observed[condseat.Finding], error) {
|
||||
found, err := observe(ctx)
|
||||
if err != nil {
|
||||
return views.Observed[condseat.Finding]{}, err
|
||||
}
|
||||
var out views.Observed[condseat.Finding]
|
||||
for _, f := range found {
|
||||
out.Findings = append(out.Findings, c.body(f))
|
||||
}
|
||||
return out, nil
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.w = w
|
||||
// A writer that cannot start (the stream of its asks is not there yet) is said; its findings are then kept in the
|
||||
// state alone and said by the next start (as the node-engine's is).
|
||||
if err := w.Start(ctx); err != nil {
|
||||
logf("the finding writer cannot start, its findings are kept in the state alone: %v", err)
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// body lays the checker's finding out as the seat's contract type: a finding of the module, about the module on its node.
|
||||
func (c *checkerFindings) body(f views.Finding) condseat.Finding {
|
||||
module, _ := c.w.ModuleNode()
|
||||
return condseat.Finding{Kind: f.Kind, Scope: views.FindingScope, ID: keys.EscapeName(f.ID), At: f.At, Since: f.At,
|
||||
Severity: condseat.FindingSeverityWarning, Source: f.Kind, Summary: f.Summary, Headline: f.Headline, Explanation: f.Explanation,
|
||||
Subject: condseat.Subject{Scope: views.FindingScope, ID: module, Machine: c.node}}
|
||||
}
|
||||
|
||||
// Put writes one finding; its `since` stays where its key exists.
|
||||
func (c *checkerFindings) Put(f views.Finding) error {
|
||||
return c.w.Write(context.Background(), c.body(f))
|
||||
}
|
||||
|
||||
// Delete clears one finding.
|
||||
func (c *checkerFindings) Delete(f views.Finding) error {
|
||||
return c.w.Clear(context.Background(), views.FindingScope, keys.EscapeName(f.ID), f.Kind, "the checker no longer observes it")
|
||||
}
|
||||
|
||||
var _ views.FindingWriter = (*checkerFindings)(nil)
|
||||
|
||||
// readerFindings is the views reader's findings through the same writer, as kinds of the reader's own.
|
||||
func (c *checkerFindings) readerFindings() views.FindingWriter {
|
||||
return views.WriterFindings{Writer: c.w}
|
||||
}
|
||||
@@ -15,11 +15,10 @@ import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
sdkfindings "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient/findings"
|
||||
|
||||
"github.com/nats-io/nats.go"
|
||||
"github.com/nats-io/nats.go/jetstream"
|
||||
|
||||
@@ -77,7 +76,6 @@ func durationEnv(getenv func(string) string, name string, def time.Duration) (ti
|
||||
// ModulesBucket, ManifestsBucket).
|
||||
var (
|
||||
firstViewsBucket = check.CheckerModule + "_" + views.StatusState
|
||||
findingsBucket = check.CheckerModule + "_findings"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -163,8 +161,15 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro
|
||||
hub := process.NewEventHub(logger.Printf)
|
||||
reader := &views.ProcessReader{Module: check.CheckerModule, Call: caller.Call, Logf: logger.Printf, Hub: hub}
|
||||
seats := seatList{reader: followSeatList(seatlist.ProcessBus(caller.Call, hub), logger.Printf)}
|
||||
findings := process.AwaitBucket(ctx, js, conn, findingsBucket, "a first view unanswered past its bound is said in the log alone", logger.Printf)
|
||||
reader.Findings = func() views.FindingWriter { return sdkfindings.Writer(ctx, findings.KV) }
|
||||
// The finding writer is made once the checker is (its observe reads the checker's kept checks); until then a first view
|
||||
// unanswered is said in the log alone.
|
||||
var findings atomic.Pointer[checkerFindings]
|
||||
reader.Findings = func() views.FindingWriter {
|
||||
if f := findings.Load(); f != nil {
|
||||
return f.readerFindings()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// Each by the owner's read tool and views family as the owner's manifest names them.
|
||||
var followed error
|
||||
followOwner := func(owner, state, tool, stream string) *views.Picture {
|
||||
@@ -236,13 +241,19 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro
|
||||
Owed: kvStore{buckets[check.CheckerOwedBucket]},
|
||||
// A check that could not run waits for its part's running, and is the finding check-could-not-run after its bound.
|
||||
CouldNotRun: awaitedKV{couldNotRunKV},
|
||||
Findings: sdkfindings.Writer(ctx, findings.KV),
|
||||
Trunks: trunks,
|
||||
Bound: checkBound,
|
||||
Link: link,
|
||||
Logf: logger.Printf,
|
||||
Settle: settleWindow,
|
||||
}
|
||||
cf, err := newCheckerFindings(ctx, cred, conn, js, caller.State("findings"), ch.RaisedCouldNotRun, logger.Printf)
|
||||
if err != nil {
|
||||
return fmt.Errorf("the checker's finding writer: %w", err)
|
||||
}
|
||||
defer cf.w.Close()
|
||||
ch.Findings = cf
|
||||
findings.Store(cf)
|
||||
kept := make(chan struct{}, 1)
|
||||
ch.CouldNotRunKept = func() {
|
||||
select {
|
||||
|
||||
@@ -44,7 +44,6 @@ replace git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/buildrun
|
||||
require git.novox.be/novox/mesh-delivery/sdk v0.0.0-00010101000000-000000000000
|
||||
|
||||
require (
|
||||
git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient v0.0.0-00010101000000-000000000000
|
||||
git.novox.be/novox/mesh-delivery/modules/mesh-delivery-checker/checkermsg v0.0.0-00010101000000-000000000000
|
||||
)
|
||||
|
||||
@@ -53,7 +52,7 @@ replace git.novox.be/novox/mesh-delivery/modules/mesh-delivery-checker/checkerms
|
||||
replace git.novox.be/novox/mesh-delivery/sdk => ../../sdk
|
||||
|
||||
require (
|
||||
git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshconditions v0.0.0-00010101000000-000000000000 // indirect
|
||||
git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshconditions v0.0.0-00010101000000-000000000000
|
||||
git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshhandacts v0.0.0-00010101000000-000000000000 // indirect
|
||||
git.novox.be/novox/mesh-delivery/contracts/go/clients/containerimageregistryclient v0.0.0-00010101000000-000000000000 // indirect
|
||||
git.novox.be/novox/mesh-delivery/contracts/go/clients/gopackageregistryclient v0.0.0-00010101000000-000000000000 // indirect
|
||||
|
||||
@@ -301,10 +301,13 @@ func TestACheckThatCouldNotRunIsAskedAgainWhenItsPartRuns(t *testing.T) { // N6/
|
||||
if due := r.ch.CouldNotRunDue(ctx, now.Add(CouldNotRunBound)); !due.IsZero() || found[key].Kind != CouldNotRunKind || !strings.Contains(found[key].Summary, "forge on g14") {
|
||||
t.Fatalf("at its bound: due %v, findings %+v, want %s naming the part", due, found, CouldNotRunKind)
|
||||
}
|
||||
if observed, err := r.ch.RaisedCouldNotRun(ctx); err != nil || len(observed) != 1 || observed[0].Kind != CouldNotRunKind || observed[0].ID != key {
|
||||
t.Fatalf("observed at its bound = %+v, %v, want its one finding, for the finding writer's statement", observed, err)
|
||||
}
|
||||
r.ch.PartRunning(ctx, "g14", "forge", "src2:1")
|
||||
r.ch.Wait()
|
||||
if len(found) != 0 {
|
||||
t.Errorf("checked again, the finding stays: %v", found)
|
||||
if observed, _ := r.ch.RaisedCouldNotRun(ctx); len(found) != 0 || len(observed) != 0 {
|
||||
t.Errorf("checked again, the finding stays: %v, observed %v", found, observed)
|
||||
}
|
||||
},
|
||||
"its heads replaced, or its pull request closed, the record and the finding go": func(t *testing.T) {
|
||||
|
||||
@@ -233,6 +233,26 @@ func (ch *Checker) CouldNotRunDue(ctx context.Context, now time.Time) time.Time
|
||||
return next
|
||||
}
|
||||
|
||||
// RaisedCouldNotRun is every finding the checker observes now (design 47 G §384, §461): the check that could not run
|
||||
// and whose finding is written. The finding writer states them at its start and when the keeper asks.
|
||||
func (ch *Checker) RaisedCouldNotRun(ctx context.Context) ([]views.Finding, error) {
|
||||
if ch.CouldNotRun == nil {
|
||||
return nil, nil
|
||||
}
|
||||
keys, err := ch.CouldNotRun.Keys(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("the checks that could not run cannot be listed: %w", err)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
var out []views.Finding
|
||||
for _, key := range keys {
|
||||
if r, _, ok := ch.couldNotRunOf(ctx, key); ok && r.Raised {
|
||||
out = append(out, couldNotRunFinding(key, r, r.Since))
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// couldNotRunFinding is the finding of the check of key that could not run.
|
||||
func couldNotRunFinding(key string, r CouldNotRun, at time.Time) views.Finding {
|
||||
return views.Finding{Observer: CheckerModule, Kind: CouldNotRunKind, ID: key,
|
||||
|
||||
Reference in New Issue
Block a user