diff --git a/checks/readable/go.mod b/checks/readable/go.mod index 87c149ca9..8aee3980f 100644 --- a/checks/readable/go.mod +++ b/checks/readable/go.mod @@ -11,4 +11,4 @@ replace git.novox.be/novox/mesh-catalog/modules/disk-load/diskreadings => ../../ replace git.novox.be/novox/mesh-catalog/modules/restic/backupgood => ../../modules/restic/backupgood -require git.novox.be/novox/mesh-sdk/go v0.0.0-00010101000000-000000000000 // indirect +require git.novox.be/novox/mesh-sdk/go v0.0.0-00010101000000-000000000000 diff --git a/checks/readable/main.go b/checks/readable/main.go index b455f0f35..51671cb04 100644 --- a/checks/readable/main.go +++ b/checks/readable/main.go @@ -31,6 +31,7 @@ import ( "git.novox.be/novox/mesh-catalog/modules/disk-load/diskreadings" "git.novox.be/novox/mesh-catalog/modules/restic/backupgood" + "git.novox.be/novox/mesh-sdk/go/catalogue" ) // Known are the constants of the owners' published packages an announcement may name a state by, by the selector the @@ -53,6 +54,7 @@ func Findings(root string) ([]string, error) { } dirs := map[string]string{} reads := map[string][]string{} + holders := map[string][]string{} // seat -> the modules of the catalogue that claim it for _, path := range manifests { raw, err := os.ReadFile(path) if err != nil { @@ -61,11 +63,17 @@ func Findings(root string) ([]string, error) { var m struct { Module string `json:"module"` Reads []json.RawMessage `json:"reads"` + Claims []struct { + Name string `json:"name"` + } `json:"claims"` } if err := json.Unmarshal(raw, &m); err != nil { return nil, fmt.Errorf("%s: %w", path, err) } dirs[m.Module] = filepath.Dir(path) + for _, c := range m.Claims { + holders[c.Name] = append(holders[c.Name], m.Module) + } for _, r := range m.Reads { var s string if json.Unmarshal(r, &s) == nil { @@ -77,32 +85,45 @@ func Findings(root string) ([]string, error) { owners := map[string]Announced{} for reader, rs := range reads { for _, r := range rs { - owner, state, ok := strings.Cut(r, ".") - dir, inCatalogue := dirs[owner] - if !ok || !inCatalogue || owner == reader { - continue - } - a, seen := owners[owner] - if !seen { - var problems []string - a, problems, err = AnnouncedBy(dir) + // A read in the seat form `seat:.` (design 47 G A-116, A-128) is of the holder of the seat, found where it + // is read: each module of the catalogue claiming the seat is judged for the state, as a read of it by name is. + var targets []string + state := "" + if seat, local, isSeat, err := catalogue.SeatRead(r); isSeat { if err != nil { - return nil, err + continue // the manifest validator names a malformed seat read } - owners[owner] = a - for _, p := range problems { - out = append(out, owner+": "+p) + targets, state = holders[seat], local + } else if owner, local, ok := strings.Cut(r, "."); ok { + targets, state = []string{owner}, local + } + for _, owner := range targets { + dir, inCatalogue := dirs[owner] + if !inCatalogue || owner == reader { + continue + } + a, seen := owners[owner] + if !seen { + var problems []string + a, problems, err = AnnouncedBy(dir) + if err != nil { + return nil, err + } + owners[owner] = a + for _, p := range problems { + out = append(out, owner+": "+p) + } + } + if a == nil { + continue // announces nothing from code: read as before + } + readable, keeps := a[state] + switch { + case !keeps: + out = append(out, fmt.Sprintf("%s reads %s.%s, which %s's code does not announce: the read is refused on the mesh", reader, owner, state, owner)) + case !readable: + out = append(out, fmt.Sprintf("%s reads %s.%s, which %s's code announces not readable: the read is refused on the mesh; announce it readable", reader, owner, state, owner)) } - } - if a == nil { - continue // announces nothing from code: read as before - } - readable, keeps := a[state] - switch { - case !keeps: - out = append(out, fmt.Sprintf("%s reads %s.%s, which %s's code does not announce: the read is refused on the mesh", reader, owner, state, owner)) - case !readable: - out = append(out, fmt.Sprintf("%s reads %s.%s, which %s's code announces not readable: the read is refused on the mesh; announce it readable", reader, owner, state, owner)) } } } diff --git a/checks/readable/main_test.go b/checks/readable/main_test.go index 808b5326e..e0b6e069c 100644 --- a/checks/readable/main_test.go +++ b/checks/readable/main_test.go @@ -50,6 +50,15 @@ func TestFindings(t *testing.T) { "modules/r/module.json": `{"module":"r","reads":["o.bound"]}`, "modules/o/module.json": `{"module":"o"}`, "modules/o/index.ts": `await keeps([{ name: "bound", readable: true }]);`}, ""}, + {"seat form, the holder's state not readable", map[string]string{ + "modules/r/module.json": `{"module":"r","reads":["seat:the-seat.s"]}`, + "modules/o/module.json": `{"module":"o","claims":[{"name":"the-seat","scope":"mesh"}]}`, + "modules/o/cmd/o/main.go": goOwner + "var _ = announce.State{Name: \"s\"}\n"}, + "r reads o.s, which o's code announces not readable"}, + {"seat form, the holder's state readable, and a seat no module of the catalogue holds", map[string]string{ + "modules/r/module.json": `{"module":"r","reads":["seat:the-seat.s","seat:elsewhere.s"]}`, + "modules/o/module.json": `{"module":"o","claims":[{"name":"the-seat","scope":"mesh"}]}`, + "modules/o/cmd/o/main.go": goOwner + "var _ = announce.State{Name: \"s\", Readable: true}\n"}, ""}, {"an owner that announces nothing, and one outside the catalogue", map[string]string{ "modules/r/module.json": `{"module":"r","reads":["o.s","mesh-controller.lease"]}`, "modules/o/module.json": `{"module":"o"}`,