mesh/delivery-group group feat/774-l3-reland rejected: composed together: the checker's verdict at the members' heads (b48c90fea31f): integration: mesh-self-ch…
mesh/delivery closed: a newer head of the same pull request
mesh/check failed: integration: mesh-self-check/cmd/mesh-self-check.TestTheSelfCheckWholeRunsOnTheBus: the run answered {Run:doctor-1793351737-1 At:20…
One commit with exactly the tree of the reviewed head35b4509c0a, on main12c2574c9d, so two compiled programs added and removed on the branch leave no trace in history (issue 1577).
171 lines
5.9 KiB
Go
171 lines
5.9 KiB
Go
// Command noplacement refuses what the repository path of placement left behind (novox/hq ADR 0567 §10, §12).
|
|
//
|
|
// Placement, which module runs on which node, is bus state held by the seat mesh-placement; no file of the
|
|
// catalogue says it, and no manifest reads it from the delivery indexer, which no longer keeps it. It refuses:
|
|
//
|
|
// 1. a `placement` directory (or file) at the root of the catalogue;
|
|
//
|
|
// 2. a `particulars` file at the root (the mesh's node names come from the checker's run, §16);
|
|
//
|
|
// 3. a manifest naming a state, stream, event or bucket of the indexer's placement (`mesh-delivery-indexer.placement…`
|
|
// or `mesh-delivery-indexer_placement…`) in any string value, such as `reads`;
|
|
//
|
|
// 4. a manifest, other than mesh-controller's, that reads the controller's assignments (`mesh-controller.assignments`):
|
|
// every reader takes placement from the seat mesh-placement (novox/hq ADR 0549 §3, ADR 0567), and the lease write is
|
|
// refused while one still reads them (ADR 0549 §6; issue 774). The modules that still read them for what the
|
|
// placement does not carry are named in checks/noplacement/assignments-readers, each with why; that list only
|
|
// shrinks: an entry whose module no longer reads them is refused too.
|
|
//
|
|
// go run . <catalogue root>
|
|
//
|
|
// Exits 1 on a finding.
|
|
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
// Prefixes are the names of the indexer's placement parts; a manifest string starting with one names them.
|
|
var Prefixes = []string{"mesh-delivery-indexer.placement", "mesh-delivery-indexer_placement"}
|
|
|
|
// Findings is every refusal in the catalogue at root, sorted.
|
|
func Findings(root string) ([]string, error) {
|
|
var out []string
|
|
if _, err := os.Lstat(filepath.Join(root, "placement")); err == nil {
|
|
out = append(out, "placement: the repository holds no placement; it is bus state of the seat mesh-placement (novox/hq ADR 0567 §10)")
|
|
}
|
|
if _, err := os.Lstat(filepath.Join(root, "particulars")); err == nil {
|
|
out = append(out, "particulars: the repository lists no node names; the checker hands them to the check (novox/hq ADR 0567 §16)")
|
|
}
|
|
manifests, err := filepath.Glob(filepath.Join(root, "modules", "*", "module.json"))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
sort.Strings(manifests)
|
|
for _, file := range manifests {
|
|
raw, err := os.ReadFile(file)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var doc any
|
|
if err := json.Unmarshal(raw, &doc); err != nil {
|
|
return nil, fmt.Errorf("%s: %w", file, err)
|
|
}
|
|
module := filepath.Base(filepath.Dir(file))
|
|
for _, s := range named(doc, nil) {
|
|
out = append(out, fmt.Sprintf("%s: names %q, which the indexer no longer keeps; placement is the seat mesh-placement's (novox/hq ADR 0567 §12)", module, s))
|
|
}
|
|
}
|
|
readers, err := assignmentsReaders(root)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out = append(out, readers...)
|
|
sort.Strings(out)
|
|
return out, nil
|
|
}
|
|
|
|
// AssignmentsRead is the controller's assignments as a manifest reads them, and Allowed the file, relative to the
|
|
// catalogue's root, that names the modules still reading them: `<module> <why>` per line, `#` a comment.
|
|
const (
|
|
AssignmentsRead = "mesh-controller.assignments"
|
|
Allowed = "checks/noplacement/assignments-readers"
|
|
)
|
|
|
|
// assignmentsReaders is every finding on the reads of the controller's assignments: a module other than the controller
|
|
// reading them that Allowed does not name, and a module Allowed names that no longer reads them (issue 774).
|
|
func assignmentsReaders(root string) ([]string, error) {
|
|
allowed := map[string]bool{}
|
|
raw, err := os.ReadFile(filepath.Join(root, Allowed))
|
|
if err != nil && !os.IsNotExist(err) {
|
|
return nil, err
|
|
}
|
|
for _, line := range strings.Split(string(raw), "\n") {
|
|
if f := strings.Fields(line); len(f) > 0 && !strings.HasPrefix(f[0], "#") {
|
|
allowed[f[0]] = true
|
|
}
|
|
}
|
|
manifests, err := filepath.Glob(filepath.Join(root, "modules", "*", "module.json"))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var found []string
|
|
reads := map[string]bool{}
|
|
for _, path := range manifests {
|
|
raw, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var m struct {
|
|
Module string `json:"module"`
|
|
Reads []string `json:"reads"`
|
|
}
|
|
if json.Unmarshal(raw, &m) != nil || m.Module == "mesh-controller" {
|
|
continue // a manifest that does not read is the manifest checks' to refuse
|
|
}
|
|
for _, r := range m.Reads {
|
|
if strings.TrimSpace(r) != AssignmentsRead {
|
|
continue
|
|
}
|
|
reads[m.Module] = true
|
|
if !allowed[m.Module] {
|
|
found = append(found, fmt.Sprintf("%s reads %s: read where modules run from mesh-delivery-assigner.placement, "+
|
|
"the seat mesh-placement's (novox/hq ADR 0549 §3, ADR 0567); the lease write is refused while a module reads "+
|
|
"the assignments (ADR 0549 §6)", m.Module, AssignmentsRead))
|
|
}
|
|
}
|
|
}
|
|
for module := range allowed {
|
|
if !reads[module] {
|
|
found = append(found, fmt.Sprintf("%s names %s, which no longer reads %s: take it off the list, which only shrinks",
|
|
Allowed, module, AssignmentsRead))
|
|
}
|
|
}
|
|
return found, nil
|
|
}
|
|
|
|
// named collects the strings of a decoded manifest that start with one of the Prefixes.
|
|
func named(v any, into []string) []string {
|
|
switch v := v.(type) {
|
|
case string:
|
|
for _, p := range Prefixes {
|
|
if strings.HasPrefix(v, p) {
|
|
return append(into, v)
|
|
}
|
|
}
|
|
case []any:
|
|
for _, e := range v {
|
|
into = named(e, into)
|
|
}
|
|
case map[string]any:
|
|
for _, e := range v {
|
|
into = named(e, into)
|
|
}
|
|
}
|
|
return into
|
|
}
|
|
|
|
func main() {
|
|
if len(os.Args) != 2 {
|
|
fmt.Fprintln(os.Stderr, "usage: noplacement <catalogue root>")
|
|
os.Exit(2)
|
|
}
|
|
found, err := Findings(os.Args[1])
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
os.Exit(1)
|
|
}
|
|
for _, f := range found {
|
|
fmt.Println(f)
|
|
}
|
|
if len(found) > 0 {
|
|
os.Exit(1)
|
|
}
|
|
fmt.Println("no placement directory, no manifest reads the indexer's placement, and only the modules listed read the controller's assignments")
|
|
}
|