node-engine: the verbs seat-list and seat-list-again (the operator and the healers) and the state findings, with fixtures (ADR 0636 §1, §7; issue 1966)
This commit is contained in:
@@ -25,6 +25,8 @@ const (
|
||||
VerbCompose = "compose"
|
||||
VerbComposeCandidate = "compose-candidate"
|
||||
VerbManifestReads = "manifest-reads"
|
||||
VerbSeatList = "seat-list"
|
||||
VerbSeatListAgain = "seat-list-again"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -36,7 +38,7 @@ const (
|
||||
|
||||
// Verbs is every verb the seat serves, in its definition's order: a new copy each call.
|
||||
func Verbs() []string {
|
||||
return []string{VerbHandOver, VerbAccessAct, VerbJudgeDirectories, VerbSetuidSearch, VerbRootFree, VerbActs, VerbPackages, VerbComposition, VerbCompose, VerbComposeCandidate, VerbManifestReads}
|
||||
return []string{VerbHandOver, VerbAccessAct, VerbJudgeDirectories, VerbSetuidSearch, VerbRootFree, VerbActs, VerbPackages, VerbComposition, VerbCompose, VerbComposeCandidate, VerbManifestReads, VerbSeatList, VerbSeatListAgain}
|
||||
}
|
||||
|
||||
// Events is every event published on the seat's subject: a new copy each call.
|
||||
@@ -70,6 +72,7 @@ const (
|
||||
StateData = "data"
|
||||
StatePackages = "packages"
|
||||
StateHealth = "health"
|
||||
StateFindings = "findings"
|
||||
)
|
||||
|
||||
// HaveByNodeModuleKey is the key of one value of the state have under the form "<node>.<module>".
|
||||
@@ -280,6 +283,37 @@ func DecodeHealthByNodeValue(raw []byte) (HealthByNodeValue, error) {
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// FindingsByKeyKey is the key of one value of the state findings under the form "<key>".
|
||||
func FindingsByKeyKey(key string) string { return key }
|
||||
|
||||
// ParseFindingsByKeyKey reads the parts of a key of the form "<key>", in the order of the form; the last result is false for a key of another form.
|
||||
func ParseFindingsByKeyKey(key string) (string, bool) {
|
||||
rest := key
|
||||
if rest == "" {
|
||||
return "", false
|
||||
}
|
||||
v0 := rest
|
||||
rest = ""
|
||||
if rest != "" {
|
||||
return "", false
|
||||
}
|
||||
return v0, true
|
||||
}
|
||||
|
||||
// DecodeFindingsByKeyValue reads one value of the state findings under the form "<key>", refusing a field FindingsByKeyValue does not know and any text after the value.
|
||||
func DecodeFindingsByKeyValue(raw []byte) (FindingsByKeyValue, error) {
|
||||
var v FindingsByKeyValue
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if dec.More() {
|
||||
return v, errors.New("text after the value")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// Act is one act asked through the seat, as acts reads it (ADR 0563 §9).
|
||||
type Act struct {
|
||||
// the act: hand-over, access-act, judge-directories or setuid-search
|
||||
@@ -371,6 +405,40 @@ type Asked struct {
|
||||
Says string `json:"says"`
|
||||
}
|
||||
|
||||
// FindingAction is one answer the operator may give from a notification.
|
||||
type FindingAction struct {
|
||||
Arguments map[string]string `json:"arguments,omitempty"`
|
||||
Label string `json:"label"`
|
||||
Level string `json:"level,omitempty"`
|
||||
Machine string `json:"machine,omitempty"`
|
||||
// the seat verb the operator channel calls
|
||||
Verb string `json:"verb"`
|
||||
}
|
||||
|
||||
// The values of FindingAction's level.
|
||||
const (
|
||||
FindingActionLevelAcknowledge = "acknowledge"
|
||||
FindingActionLevelApprove = "approve"
|
||||
)
|
||||
|
||||
// FindingEvidence is one observation, as it was said.
|
||||
type FindingEvidence struct {
|
||||
At string `json:"at"`
|
||||
Said string `json:"said"`
|
||||
}
|
||||
|
||||
// FindingSubject is what the condition is about.
|
||||
type FindingSubject struct {
|
||||
// the other machines it concerns
|
||||
Also []string `json:"also,omitempty"`
|
||||
// its id within the scope
|
||||
ID string `json:"id"`
|
||||
// the machine it concerns, when there is one
|
||||
Machine string `json:"machine,omitempty"`
|
||||
// machine, plan, call, build, merge, provider, seat, bus, core, probe, mesh, delivery or module
|
||||
Scope string `json:"scope"`
|
||||
}
|
||||
|
||||
// ManifestReadsChanged is what a node-engine says when what it reads changed or its node returned (ADR 0618 §4).
|
||||
type ManifestReadsChanged struct {
|
||||
// when it says it, RFC 3339
|
||||
@@ -450,6 +518,42 @@ type Said struct {
|
||||
Said string `json:"said"`
|
||||
}
|
||||
|
||||
// SeatListStatus is a type of the contract.
|
||||
type SeatListStatus struct {
|
||||
// the seat mesh-catalog has answered
|
||||
Heard bool `json:"heard"`
|
||||
Held []SeatListHeld `json:"held"`
|
||||
LastError string `json:"last-error,omitempty"`
|
||||
// a trunk definition that does not parse, by seat: the one held stands
|
||||
Refused map[string]string `json:"refused,omitempty"`
|
||||
Revision int64 `json:"revision,omitempty"`
|
||||
// the seats held from the bootstrap alone
|
||||
Waiting []string `json:"waiting,omitempty"`
|
||||
}
|
||||
|
||||
// SeatList is what an engine holds of the seat list (novox/hq ADR 0636 §7).
|
||||
type SeatList struct {
|
||||
// why the read failed, for seat-list-again
|
||||
Error string `json:"error,omitempty"`
|
||||
// the one line the engine says of it; empty when nothing waits
|
||||
Health string `json:"health"`
|
||||
Status SeatListStatus `json:"status"`
|
||||
}
|
||||
|
||||
// SeatListHeld is a type of the contract.
|
||||
type SeatListHeld struct {
|
||||
// the digest of the definition held; empty for a bootstrapped one
|
||||
Digest string `json:"digest,omitempty"`
|
||||
Seat string `json:"seat"`
|
||||
Source string `json:"source"`
|
||||
}
|
||||
|
||||
// The values of SeatListHeld's source.
|
||||
const (
|
||||
SeatListHeldSourceTrunk = "trunk"
|
||||
SeatListHeldSourceBootstrap = "bootstrap"
|
||||
)
|
||||
|
||||
// HandOverInput is what hand-over takes.
|
||||
type HandOverInput struct {
|
||||
// the directory's absolute path, as the engine states it
|
||||
@@ -773,6 +877,20 @@ type ManifestReadsAnswer struct {
|
||||
V int64 `json:"v"`
|
||||
}
|
||||
|
||||
// SeatListInput is what seat-list takes.
|
||||
type SeatListInput struct {
|
||||
}
|
||||
|
||||
// SeatListAnswer is what seat-list answers.
|
||||
type SeatListAnswer = SeatList
|
||||
|
||||
// SeatListAgainInput is what seat-list-again takes.
|
||||
type SeatListAgainInput struct {
|
||||
}
|
||||
|
||||
// SeatListAgainAnswer is what seat-list-again answers.
|
||||
type SeatListAgainAnswer = SeatList
|
||||
|
||||
// AskedEvent is the body of the event asked.
|
||||
type AskedEvent = ActSaid
|
||||
|
||||
@@ -885,3 +1003,40 @@ type HealthByNodeValue struct {
|
||||
Units map[string]any `json:"units,omitempty"`
|
||||
V int64 `json:"v"`
|
||||
}
|
||||
|
||||
// FindingsByKeyValue is one value of the state findings under the key <key>.
|
||||
type FindingsByKeyValue struct {
|
||||
Actions []FindingAction `json:"actions,omitempty"`
|
||||
At string `json:"at"`
|
||||
// newest first, at most 10
|
||||
Evidence []FindingEvidence `json:"evidence,omitempty"`
|
||||
Explanation string `json:"explanation,omitempty"`
|
||||
Headline string `json:"headline,omitempty"`
|
||||
// scope.id.token
|
||||
Key string `json:"key"`
|
||||
Kind string `json:"kind"`
|
||||
Needs string `json:"needs,omitempty"`
|
||||
// the module that wrote it
|
||||
Observer string `json:"observer"`
|
||||
// the key's last part when it is not the kind
|
||||
Ref string `json:"ref,omitempty"`
|
||||
Resolved string `json:"resolved,omitempty"`
|
||||
// self, operator, agent or healer:<name>
|
||||
Resolver string `json:"resolver,omitempty"`
|
||||
// what of its own saw it last
|
||||
Run string `json:"run,omitempty"`
|
||||
Severity string `json:"severity"`
|
||||
// the row, probe or event that saw it
|
||||
Source string `json:"source"`
|
||||
Subject FindingSubject `json:"subject"`
|
||||
// one line
|
||||
Summary string `json:"summary"`
|
||||
// layout version, 1
|
||||
V int64 `json:"v"`
|
||||
}
|
||||
|
||||
// The values of FindingsByKeyValue's severity.
|
||||
const (
|
||||
FindingsByKeyValueSeverityUrgent = "urgent"
|
||||
FindingsByKeyValueSeverityWarning = "warning"
|
||||
)
|
||||
|
||||
@@ -35,6 +35,8 @@ func TestFixtures(t *testing.T) {
|
||||
err := json.Unmarshal(b, &v)
|
||||
return v, err
|
||||
}, subjectIs: regexp.MustCompile("^mesh\\.seat\\.node-engine\\.event\\.manifest-reads-changed\\.[A-Za-z0-9_-]+$")},
|
||||
{name: "the finding of a seat list unread, as the engine writes it", subject: "", given: "{\"at\":\"2026-11-05T14:00:00Z\",\"key\":\"machine.ace.seat-list-unread\",\"kind\":\"seat-list-unread\",\"observer\":\"mesh-host\",\"severity\":\"warning\",\"source\":\"node-engine.seat-list\",\"subject\":{\"id\":\"ace\",\"machine\":\"ace\",\"scope\":\"machine\"},\"summary\":\"the seat list has not been read for five minutes\",\"v\":1}", wire: "{\"at\":\"2026-11-05T14:00:00Z\",\"key\":\"machine.ace.seat-list-unread\",\"kind\":\"seat-list-unread\",\"observer\":\"mesh-host\",\"severity\":\"warning\",\"source\":\"node-engine.seat-list\",\"subject\":{\"id\":\"ace\",\"machine\":\"ace\",\"scope\":\"machine\"},\"summary\":\"the seat list has not been read for five minutes\",\"v\":1}",
|
||||
decode: func(b []byte) (any, error) { var v FindingsByKeyValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "manifest-reads answers what the engine reads and when it stated it", subject: "", given: "{\"manifest-reads\":[\"form:seat-reference\",\"word:series\"],\"node\":\"ace\",\"stated-at\":\"2026-11-04T10:00:00Z\",\"v\":1}", wire: "{\"manifest-reads\":[\"form:seat-reference\",\"word:series\"],\"node\":\"ace\",\"stated-at\":\"2026-11-04T10:00:00Z\",\"v\":1}",
|
||||
decode: func(b []byte) (any, error) { var v ManifestReadsAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "manifest-reads of an engine built before it said them is the baseline", subject: "", given: "{\"node\":\"g14\",\"stated-at\":\"2026-11-04T10:05:00Z\",\"v\":1}", wire: "{\"node\":\"g14\",\"stated-at\":\"2026-11-04T10:05:00Z\",\"v\":1}",
|
||||
@@ -45,6 +47,10 @@ func TestFixtures(t *testing.T) {
|
||||
decode: func(b []byte) (any, error) { var v ComposeCandidateAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "acts answers each act kept", subject: "", given: "{\"acts\":[{\"act\":\"hand-over\",\"ask\":\"hand-over-1\",\"at\":\"2026-11-04T09:00:00Z\",\"caller\":\"mesh-tools\",\"expires\":\"2026-11-05T09:00:00Z\",\"id\":\"hand-over-1\",\"state\":\"asked\",\"subject\":\"/srv/media\"}],\"node\":\"ace\"}", wire: "{\"acts\":[{\"act\":\"hand-over\",\"ask\":\"hand-over-1\",\"at\":\"2026-11-04T09:00:00Z\",\"caller\":\"mesh-tools\",\"expires\":\"2026-11-05T09:00:00Z\",\"id\":\"hand-over-1\",\"state\":\"asked\",\"subject\":\"/srv/media\"}],\"node\":\"ace\"}",
|
||||
decode: func(b []byte) (any, error) { var v ActsAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "seat-list answers what the engine holds and what it waits for", subject: "", given: "{\"health\":\"the seat list is held from the bootstrap and nothing new is acted on\",\"status\":{\"heard\":false,\"held\":[{\"seat\":\"node-engine\",\"source\":\"bootstrap\"},{\"digest\":\"3f2a9c\",\"seat\":\"node-files\",\"source\":\"trunk\"}],\"last-error\":\"no answer\",\"waiting\":[\"node-engine\"]}}", wire: "{\"health\":\"the seat list is held from the bootstrap and nothing new is acted on\",\"status\":{\"heard\":false,\"held\":[{\"seat\":\"node-engine\",\"source\":\"bootstrap\"},{\"digest\":\"3f2a9c\",\"seat\":\"node-files\",\"source\":\"trunk\"}],\"last-error\":\"no answer\",\"waiting\":[\"node-engine\"]}}",
|
||||
decode: func(b []byte) (any, error) { var v SeatListAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "seat-list-again answers the error when the read failed", subject: "", given: "{\"error\":\"seatlist: no answer\",\"health\":\"the seat list is held from the bootstrap\",\"status\":{\"heard\":false,\"held\":[{\"seat\":\"node-engine\",\"source\":\"bootstrap\"}],\"waiting\":[\"node-engine\"]}}", wire: "{\"error\":\"seatlist: no answer\",\"health\":\"the seat list is held from the bootstrap\",\"status\":{\"heard\":false,\"held\":[{\"seat\":\"node-engine\",\"source\":\"bootstrap\"}],\"waiting\":[\"node-engine\"]}}",
|
||||
decode: func(b []byte) (any, error) { var v SeatListAgainAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
}
|
||||
for _, c := range cases {
|
||||
v, err := c.decode([]byte(c.given))
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
[
|
||||
{
|
||||
"name": "the finding of a seat list unread, as the engine writes it",
|
||||
"of": "state:findings/<key>",
|
||||
"given": {
|
||||
"v": 1,
|
||||
"key": "machine.ace.seat-list-unread",
|
||||
"kind": "seat-list-unread",
|
||||
"subject": {"scope": "machine", "id": "ace", "machine": "ace"},
|
||||
"severity": "warning",
|
||||
"summary": "the seat list has not been read for five minutes",
|
||||
"source": "node-engine.seat-list",
|
||||
"observer": "mesh-host",
|
||||
"at": "2026-11-05T14:00:00Z"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -72,5 +72,50 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "seat-list answers what the engine holds and what it waits for",
|
||||
"of": "verb:seat-list.answer",
|
||||
"given": {
|
||||
"status": {
|
||||
"heard": false,
|
||||
"held": [
|
||||
{
|
||||
"seat": "node-engine",
|
||||
"source": "bootstrap"
|
||||
},
|
||||
{
|
||||
"seat": "node-files",
|
||||
"source": "trunk",
|
||||
"digest": "3f2a9c"
|
||||
}
|
||||
],
|
||||
"waiting": [
|
||||
"node-engine"
|
||||
],
|
||||
"last-error": "no answer"
|
||||
},
|
||||
"health": "the seat list is held from the bootstrap and nothing new is acted on"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "seat-list-again answers the error when the read failed",
|
||||
"of": "verb:seat-list-again.answer",
|
||||
"given": {
|
||||
"status": {
|
||||
"heard": false,
|
||||
"held": [
|
||||
{
|
||||
"seat": "node-engine",
|
||||
"source": "bootstrap"
|
||||
}
|
||||
],
|
||||
"waiting": [
|
||||
"node-engine"
|
||||
]
|
||||
},
|
||||
"health": "the seat list is held from the bootstrap",
|
||||
"error": "seatlist: no answer"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -674,6 +674,30 @@
|
||||
],
|
||||
"description": "what this machine's engine reads of a manifest (ADR 0618 §3)"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "seat-list",
|
||||
"description": "The seat list this engine holds and what it waits for (novox/hq ADR 0636 §7): each seat's source (trunk or bootstrap) and digest, the seats held from the bootstrap alone, the definitions refused, the last error and whether the seat mesh-catalog has answered. Read-only.",
|
||||
"callers": "the operator and the healers",
|
||||
"input": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
},
|
||||
"answer": {
|
||||
"$ref": "#/defs/SeatList"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "seat-list-again",
|
||||
"description": "Make this engine read the seat list from the seat mesh-catalog now (novox/hq ADR 0636 §7), the recovery of a list unread; it answers what it holds afterwards, and the error when the read failed. A read that changed nothing still ends the engine's wait.",
|
||||
"callers": "the operator and the healers",
|
||||
"input": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
},
|
||||
"answer": {
|
||||
"$ref": "#/defs/SeatList"
|
||||
}
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
@@ -1069,6 +1093,109 @@
|
||||
}
|
||||
],
|
||||
"description": "each node's health as its engine reports it"
|
||||
},
|
||||
{
|
||||
"name": "findings",
|
||||
"template": "state",
|
||||
"writer": "holder",
|
||||
"key-forms": [
|
||||
{
|
||||
"key": "<key>",
|
||||
"value": {
|
||||
"type": "object",
|
||||
"description": "one thing an observer sees wrong now",
|
||||
"properties": {
|
||||
"v": {
|
||||
"type": "integer",
|
||||
"description": "layout version, 1"
|
||||
},
|
||||
"key": {
|
||||
"type": "string",
|
||||
"description": "scope.id.token"
|
||||
},
|
||||
"kind": {
|
||||
"type": "string"
|
||||
},
|
||||
"subject": {
|
||||
"$ref": "#/defs/FindingSubject"
|
||||
},
|
||||
"severity": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"urgent",
|
||||
"warning"
|
||||
]
|
||||
},
|
||||
"summary": {
|
||||
"type": "string",
|
||||
"description": "one line"
|
||||
},
|
||||
"headline": {
|
||||
"type": "string"
|
||||
},
|
||||
"explanation": {
|
||||
"type": "string"
|
||||
},
|
||||
"resolved": {
|
||||
"type": "string"
|
||||
},
|
||||
"needs": {
|
||||
"type": "string"
|
||||
},
|
||||
"actions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/defs/FindingAction"
|
||||
}
|
||||
},
|
||||
"evidence": {
|
||||
"type": "array",
|
||||
"description": "newest first, at most 10",
|
||||
"items": {
|
||||
"$ref": "#/defs/FindingEvidence"
|
||||
}
|
||||
},
|
||||
"source": {
|
||||
"type": "string",
|
||||
"description": "the row, probe or event that saw it"
|
||||
},
|
||||
"resolver": {
|
||||
"type": "string",
|
||||
"description": "self, operator, agent or healer:<name>"
|
||||
},
|
||||
"ref": {
|
||||
"type": "string",
|
||||
"description": "the key's last part when it is not the kind"
|
||||
},
|
||||
"observer": {
|
||||
"type": "string",
|
||||
"description": "the module that wrote it"
|
||||
},
|
||||
"run": {
|
||||
"type": "string",
|
||||
"description": "what of its own saw it last"
|
||||
},
|
||||
"at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"v",
|
||||
"key",
|
||||
"kind",
|
||||
"subject",
|
||||
"severity",
|
||||
"summary",
|
||||
"source",
|
||||
"observer",
|
||||
"at"
|
||||
]
|
||||
},
|
||||
"description": "one finding of the observer that wrote it"
|
||||
}
|
||||
],
|
||||
"description": "the findings this node's engine raises about itself, one key per finding, in the layout of the seat mesh-conditions (its findings state): a key written raises the condition of the same key, a key deleted clears it (novox/hq ADR 0564 §2, ADR 0636 §1); today `machine.<node>.seat-list-unread`"
|
||||
}
|
||||
],
|
||||
"defs": {
|
||||
@@ -1412,6 +1539,165 @@
|
||||
"said"
|
||||
],
|
||||
"description": "the answer of a read-only judgement"
|
||||
},
|
||||
"SeatListHeld": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"seat": {
|
||||
"type": "string"
|
||||
},
|
||||
"source": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"trunk",
|
||||
"bootstrap"
|
||||
]
|
||||
},
|
||||
"digest": {
|
||||
"type": "string",
|
||||
"description": "the digest of the definition held; empty for a bootstrapped one"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"seat",
|
||||
"source"
|
||||
]
|
||||
},
|
||||
"SeatList": {
|
||||
"type": "object",
|
||||
"description": "what an engine holds of the seat list (novox/hq ADR 0636 §7)",
|
||||
"properties": {
|
||||
"status": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"heard": {
|
||||
"type": "boolean",
|
||||
"description": "the seat mesh-catalog has answered"
|
||||
},
|
||||
"held": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/defs/SeatListHeld"
|
||||
}
|
||||
},
|
||||
"waiting": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "the seats held from the bootstrap alone"
|
||||
},
|
||||
"refused": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "a trunk definition that does not parse, by seat: the one held stands"
|
||||
},
|
||||
"last-error": {
|
||||
"type": "string"
|
||||
},
|
||||
"revision": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"heard",
|
||||
"held"
|
||||
]
|
||||
},
|
||||
"health": {
|
||||
"type": "string",
|
||||
"description": "the one line the engine says of it; empty when nothing waits"
|
||||
},
|
||||
"error": {
|
||||
"type": "string",
|
||||
"description": "why the read failed, for seat-list-again"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"status",
|
||||
"health"
|
||||
]
|
||||
},
|
||||
"FindingSubject": {
|
||||
"type": "object",
|
||||
"description": "what the condition is about",
|
||||
"properties": {
|
||||
"scope": {
|
||||
"type": "string",
|
||||
"description": "machine, plan, call, build, merge, provider, seat, bus, core, probe, mesh, delivery or module"
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "its id within the scope"
|
||||
},
|
||||
"machine": {
|
||||
"type": "string",
|
||||
"description": "the machine it concerns, when there is one"
|
||||
},
|
||||
"also": {
|
||||
"type": "array",
|
||||
"description": "the other machines it concerns",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"scope",
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"FindingAction": {
|
||||
"type": "object",
|
||||
"description": "one answer the operator may give from a notification",
|
||||
"properties": {
|
||||
"label": {
|
||||
"type": "string"
|
||||
},
|
||||
"verb": {
|
||||
"type": "string",
|
||||
"description": "the seat verb the operator channel calls"
|
||||
},
|
||||
"machine": {
|
||||
"type": "string"
|
||||
},
|
||||
"arguments": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"level": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"acknowledge",
|
||||
"approve"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"label",
|
||||
"verb"
|
||||
]
|
||||
},
|
||||
"FindingEvidence": {
|
||||
"type": "object",
|
||||
"description": "one observation, as it was said",
|
||||
"properties": {
|
||||
"at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"said": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"at",
|
||||
"said"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ export const verbs = {
|
||||
compose: "compose",
|
||||
composeCandidate: "compose-candidate",
|
||||
manifestReads: "manifest-reads",
|
||||
seatList: "seat-list",
|
||||
seatListAgain: "seat-list-again",
|
||||
} as const;
|
||||
|
||||
export const events = {
|
||||
@@ -49,6 +51,7 @@ export const states = {
|
||||
data: "data",
|
||||
packages: "packages",
|
||||
health: "health",
|
||||
findings: "findings",
|
||||
} as const;
|
||||
|
||||
/** The key of one value of the state have under the form "<node>.<module>". */
|
||||
@@ -158,6 +161,21 @@ export function parseHealthByNodeKey(key: string): { node: string } | undefined
|
||||
return { node };
|
||||
}
|
||||
|
||||
/** The key of one value of the state findings under the form "<key>". */
|
||||
export function findingsByKeyKey(key: string): string {
|
||||
return key;
|
||||
}
|
||||
|
||||
/** The parts of a key of the form "<key>", or undefined for a key of another form. */
|
||||
export function parseFindingsByKeyKey(key: string): { key: string } | undefined {
|
||||
let rest = key;
|
||||
if (rest === "") return undefined;
|
||||
const key = rest;
|
||||
rest = "";
|
||||
if (rest !== "") return undefined;
|
||||
return { key };
|
||||
}
|
||||
|
||||
/** one act asked through the seat, as acts reads it (ADR 0563 §9) */
|
||||
export interface Act {
|
||||
/** the act: hand-over, access-act, judge-directories or setuid-search */
|
||||
@@ -234,6 +252,34 @@ export interface Asked {
|
||||
"says": string;
|
||||
}
|
||||
|
||||
/** one answer the operator may give from a notification */
|
||||
export interface FindingAction {
|
||||
"arguments"?: Record<string, string>;
|
||||
"label": string;
|
||||
"level"?: "acknowledge" | "approve";
|
||||
"machine"?: string;
|
||||
/** the seat verb the operator channel calls */
|
||||
"verb": string;
|
||||
}
|
||||
|
||||
/** one observation, as it was said */
|
||||
export interface FindingEvidence {
|
||||
"at": string;
|
||||
"said": string;
|
||||
}
|
||||
|
||||
/** what the condition is about */
|
||||
export interface FindingSubject {
|
||||
/** the other machines it concerns */
|
||||
"also"?: string[];
|
||||
/** its id within the scope */
|
||||
"id": string;
|
||||
/** the machine it concerns, when there is one */
|
||||
"machine"?: string;
|
||||
/** machine, plan, call, build, merge, provider, seat, bus, core, probe, mesh, delivery or module */
|
||||
"scope": string;
|
||||
}
|
||||
|
||||
/** what a node-engine says when what it reads changed or its node returned (ADR 0618 §4) */
|
||||
export interface ManifestReadsChanged {
|
||||
/** when it says it, RFC 3339 */
|
||||
@@ -300,6 +346,34 @@ export interface Said {
|
||||
"said": string;
|
||||
}
|
||||
|
||||
export interface SeatListStatus {
|
||||
/** the seat mesh-catalog has answered */
|
||||
"heard": boolean;
|
||||
"held": SeatListHeld[];
|
||||
"last-error"?: string;
|
||||
/** a trunk definition that does not parse, by seat: the one held stands */
|
||||
"refused"?: Record<string, string>;
|
||||
"revision"?: number;
|
||||
/** the seats held from the bootstrap alone */
|
||||
"waiting"?: string[];
|
||||
}
|
||||
|
||||
/** what an engine holds of the seat list (novox/hq ADR 0636 §7) */
|
||||
export interface SeatList {
|
||||
/** why the read failed, for seat-list-again */
|
||||
"error"?: string;
|
||||
/** the one line the engine says of it; empty when nothing waits */
|
||||
"health": string;
|
||||
"status": SeatListStatus;
|
||||
}
|
||||
|
||||
export interface SeatListHeld {
|
||||
/** the digest of the definition held; empty for a bootstrapped one */
|
||||
"digest"?: string;
|
||||
"seat": string;
|
||||
"source": "trunk" | "bootstrap";
|
||||
}
|
||||
|
||||
/** is what hand-over takes. */
|
||||
export interface HandOverInput {
|
||||
/** the directory's absolute path, as the engine states it */
|
||||
@@ -608,6 +682,20 @@ export interface ManifestReadsAnswer {
|
||||
"v": number;
|
||||
}
|
||||
|
||||
/** is what seat-list takes. */
|
||||
export interface SeatListInput {
|
||||
}
|
||||
|
||||
/** is what seat-list answers. */
|
||||
export type SeatListAnswer = SeatList;
|
||||
|
||||
/** is what seat-list-again takes. */
|
||||
export interface SeatListAgainInput {
|
||||
}
|
||||
|
||||
/** is what seat-list-again answers. */
|
||||
export type SeatListAgainAnswer = SeatList;
|
||||
|
||||
/** is the body of the event asked. */
|
||||
export type AskedEvent = ActSaid;
|
||||
|
||||
@@ -720,3 +808,34 @@ export interface HealthByNodeValue {
|
||||
"v": number;
|
||||
}
|
||||
|
||||
/** is one value of the state findings under the key <key>. */
|
||||
export interface FindingsByKeyValue {
|
||||
"actions"?: FindingAction[];
|
||||
"at": string;
|
||||
/** newest first, at most 10 */
|
||||
"evidence"?: FindingEvidence[];
|
||||
"explanation"?: string;
|
||||
"headline"?: string;
|
||||
/** scope.id.token */
|
||||
"key": string;
|
||||
"kind": string;
|
||||
"needs"?: string;
|
||||
/** the module that wrote it */
|
||||
"observer": string;
|
||||
/** the key's last part when it is not the kind */
|
||||
"ref"?: string;
|
||||
"resolved"?: string;
|
||||
/** self, operator, agent or healer:<name> */
|
||||
"resolver"?: string;
|
||||
/** what of its own saw it last */
|
||||
"run"?: string;
|
||||
"severity": "urgent" | "warning";
|
||||
/** the row, probe or event that saw it */
|
||||
"source": string;
|
||||
"subject": FindingSubject;
|
||||
/** one line */
|
||||
"summary": string;
|
||||
/** layout version, 1 */
|
||||
"v": number;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user