Until now the six machine alerts came from a health message passing to the controller. The report is now kept in the engine's declared state `health`, key `<node>`, created by the grants part as `acts` is. Only this node's engine writes it, after comparing it on every look and only when what it says changed; it is read first after a restart. It carries: - the network parts (machine-network; network-rewritten through the resolver file part's writer and owner); - the tunnel's peers, each seen within five minutes or not (network-unreachable); - the failed units no module owns (machine-units); - the accounts marked relogin needed (relogin-needed); - the directories used as found (directory-used-as-found). The streaks and the time of the look are left out, so a look alone writes nothing. A peer is reported seen or not, never its handshake time, so a renewed handshake writes nothing. The layout is internal/link's (NodeHealth) until it moves into mesh-sdk. Pins mesh-sdk 58a8324. Tests: - rows in TestOnTheHubAnyFreshPeerIsAHealthyTunnel: the peers, and a change only when one is seen or lost; - a case in TestAHealthStatementIsSaidOnTheLinkOpenNow: what the report takes, and one write per change across a restart; - the manifest's states in TestTheManifestConsumesTheBackupEvent.
84 lines
2.9 KiB
Go
84 lines
2.9 KiB
Go
package packaging_test
|
|
|
|
import (
|
|
"encoding/json"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
|
)
|
|
|
|
// The operator's public keys reach every node as a file beside the state file, fed by a setting of the node-engine
|
|
// (novox/hq ADR 0539 clauses 3 and 11): the node agent reads it, and a composition made by hand is applied only when one
|
|
// of these keys signed it. The setting may be empty, and then no operator key is trusted.
|
|
func TestTheManifestDeclaresTheOperatorKeysFileFedByASetting(t *testing.T) {
|
|
raw, err := os.ReadFile("../module.json")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := catalogue.ParseManifest(raw); err != nil {
|
|
t.Fatalf("the manifest no longer parses: %v", err)
|
|
}
|
|
var m struct {
|
|
Config map[string]struct {
|
|
Type string `json:"type"`
|
|
Default any `json:"default"`
|
|
} `json:"config"`
|
|
Resources []struct {
|
|
ID string `json:"id"`
|
|
Type string `json:"type"`
|
|
Path string `json:"path"`
|
|
Mode string `json:"mode"`
|
|
Content string `json:"content"`
|
|
} `json:"resources"`
|
|
}
|
|
if err := json.Unmarshal(raw, &m); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
setting, ok := m.Config["operator-signers"]
|
|
if !ok || setting.Type != "string" || setting.Default != "" {
|
|
t.Fatalf("the setting operator-signers is %+v: a string that may be empty", setting)
|
|
}
|
|
for _, r := range m.Resources {
|
|
if r.ID != "operator-keys" {
|
|
continue
|
|
}
|
|
if r.Type != "file" || r.Path != "/var/lib/mesh-host/operator-keys" || r.Mode != "0644" ||
|
|
!strings.Contains(r.Content, "${config:operator-signers}") {
|
|
t.Fatalf("the file is %+v: it is declared beside the state file, 0644, its content the setting", r)
|
|
}
|
|
return
|
|
}
|
|
t.Fatal("module.json declares no operator-keys file")
|
|
}
|
|
|
|
// The node agent hears the end of a backup on the event of node-backup's holder, so the manifest consumes it.
|
|
func TestTheManifestConsumesTheBackupEvent(t *testing.T) {
|
|
raw, err := os.ReadFile("../module.json")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var m struct {
|
|
Consumes []string `json:"consumes"`
|
|
}
|
|
if err := json.Unmarshal(raw, &m); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(m.Consumes) != 1 || m.Consumes[0] != "restic.backed-up" {
|
|
t.Fatalf("consumes %v", m.Consumes)
|
|
}
|
|
// 774 (ADR 0563 §2, §3): the acts of the node seat and the engine's own asks are its declared state acts, the bucket
|
|
// mesh-host_acts the grants part creates and each node's engine writes under its own keys.
|
|
parsed, err := catalogue.ParseManifest(raw)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// 774 (the alert table): beside it the state health, one key per node, the node's own health report, kept until
|
|
// written again (no TTL: a node that stops writing keeps its last word, which the watchman reads with its time).
|
|
if len(parsed.State) != 2 || parsed.State[0].Name != "acts" || parsed.State[0].TTLSeconds != 90*24*60*60 ||
|
|
parsed.State[1].Name != "health" || parsed.State[1].TTLSeconds != 0 {
|
|
t.Fatalf("the manifest declares the state %+v", parsed.State)
|
|
}
|
|
}
|