94 lines
3.5 KiB
Go
94 lines
3.5 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient"
|
|
"git.novox.be/novox/mesh-sdk/go/views"
|
|
)
|
|
|
|
type findingWriter struct {
|
|
st memStates
|
|
puts []string
|
|
dels []string
|
|
}
|
|
|
|
func (w *findingWriter) Put(f views.Finding) error {
|
|
key := meshconditionsclient.Key(meshconditionsclient.ScopeModule, f.ID, f.Kind)
|
|
w.puts = append(w.puts, key)
|
|
return w.st.Put(StateFindings, key, map[string]string{"summary": f.Summary})
|
|
}
|
|
func (w *findingWriter) Delete(f views.Finding) error {
|
|
key := meshconditionsclient.Key(meshconditionsclient.ScopeModule, f.ID, f.Kind)
|
|
w.dels = append(w.dels, key)
|
|
return w.st.Delete(StateFindings, key)
|
|
}
|
|
|
|
// §18, §27, A-16: the apply is started once at the follower's start and on scrub_finish, resilver_finish and a
|
|
// declared device's udev add, through the service manager; a device not ONLINE is a finding, written once,
|
|
// deleted when ONLINE again or gone.
|
|
func TestTheFollowerStartsTheApplyOnTheEventsThatEndAWaitAndKeepsTheFindings(t *testing.T) {
|
|
for class, want := range map[string]bool{"sysevent.fs.zfs.scrub_finish": true, "sysevent.fs.zfs.resilver_finish": true,
|
|
"sysevent.fs.zfs.scrub_start": false, "ereport.fs.zfs.io": false} {
|
|
if EndsAWait(class) != want {
|
|
t.Errorf("%s: %v", class, !want)
|
|
}
|
|
}
|
|
f := newFake(t)
|
|
f.uid = 0
|
|
f.files[ConfigFile] = `{"pool":"tank","cache-devices":["c1"]}`
|
|
f.files[MemInfoFile] = "MemTotal: 16384000 kB\n"
|
|
f.links["/dev/disk/by-id/c1"] = "/dev/nvme1n1"
|
|
m := f.machine()
|
|
var key string
|
|
var body any
|
|
m.Node = "ace"
|
|
m.Call = func(k string, b any) (json.RawMessage, error) { key, body = k, b; return nil, nil }
|
|
udev := map[string]bool{
|
|
"UDEV [12.3] add /devices/pci0000:00/nvme/nvme1/nvme1n1 (block)": true,
|
|
"UDEV [12.3] add /devices/pci0000:00/nvme/nvme1/nvme1n1/nvme1n1p1 (block)": true,
|
|
"UDEV [12.3] add /devices/pci0000:00/nvme/nvme2/nvme2n1 (block)": false,
|
|
"UDEV [12.3] remove /devices/pci0000:00/nvme/nvme1/nvme1n1 (block)": false,
|
|
"KERNEL[12.3] add /devices/pci0000:00/nvme/nvme1/nvme1n1 (block)": false,
|
|
}
|
|
for line, want := range udev {
|
|
if m.DeclaredAdded(line) != want {
|
|
t.Errorf("%q: %v", line, !want)
|
|
}
|
|
}
|
|
if err := m.FollowUdev(strings.NewReader("UDEV [1] add /devices/x/nvme2n1 (block)\nUDEV [2] add /devices/x/nvme1n1 (block)\n")); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
args, _ := body.(map[string]any)
|
|
if key != "seat:node-service-manager.start@ace" || args["unit"] != ApplyUnit || args["scope"] != "system" {
|
|
t.Errorf("called %s with %v", key, body)
|
|
}
|
|
|
|
// The findings: a degraded pool, then healthy again.
|
|
st := memStates{}
|
|
w := &findingWriter{st: st}
|
|
m.States, m.Findings = st, w
|
|
f.on(line("zpool", StatusPathsArgv()), fixture(t, "zpool-status-degraded.txt"))
|
|
if err := m.SyncFindings(context.Background()); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, k := range []string{"pool_tank_pool", "pool_tank_raidz2-0", "pool_tank_ata-WDC_WD120EFBX-68B0EN0_D7JA0002"} {
|
|
if _, ok := st["findings/module.zfs."+k]; !ok {
|
|
t.Errorf("no finding %s in %v", k, st)
|
|
}
|
|
}
|
|
if len(w.puts) != 4 {
|
|
t.Errorf("puts %v", w.puts)
|
|
}
|
|
if err := m.SyncFindings(context.Background()); err != nil || len(w.puts) != 4 {
|
|
t.Errorf("an unchanged finding was written again: %v %v", err, w.puts)
|
|
}
|
|
f.on(line("zpool", StatusPathsArgv()), fixture(t, "zpool-status.txt"))
|
|
if err := m.SyncFindings(context.Background()); err != nil || len(w.dels) != 4 || len(st) != 0 {
|
|
t.Errorf("deleted %v, left %v, %v", w.dels, st, err)
|
|
}
|
|
}
|