76 lines
2.5 KiB
Go
76 lines
2.5 KiB
Go
// Package health is the catalogue's count of long-running resources that do not say how they are ready (novox/hq ADR 0240 rule
|
|
// 8), kept in health-undeclared and judged by a test over the real tree (ADR 0529 clauses 2 and 4): it replaces the step of
|
|
// merge-check.sh that read it out of the controller's `module check`. Every manifest is decoded with mesh-sdk's catalogue
|
|
// package, the one the controller counts with, and judged by nothing here (novox/hq ADR 0578 §3): a resource's `health` is
|
|
// in the manifest itself, not in a template's text.
|
|
package health
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
|
)
|
|
|
|
// Count is the long-running resources without `health` of every module under root/modules.
|
|
func Count(root string) (int, error) {
|
|
manifests, err := filepath.Glob(filepath.Join(root, "modules", "*", "module.json"))
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
sort.Strings(manifests)
|
|
if len(manifests) == 0 {
|
|
return 0, fmt.Errorf("no manifest under %s: the count would be zero for any catalogue", filepath.Join(root, "modules"))
|
|
}
|
|
n := 0
|
|
for _, path := range manifests {
|
|
raw, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
m, err := catalogue.Decode(raw)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("%s: %w", path, err)
|
|
}
|
|
n += len(catalogue.Undeclared(m))
|
|
}
|
|
return n, nil
|
|
}
|
|
|
|
// Kept is the number the file keeps: the first number of its first line.
|
|
func Kept(path string) (int, error) {
|
|
raw, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
first, _, _ := strings.Cut(string(raw), "\n")
|
|
digits := ""
|
|
for _, r := range strings.TrimSpace(first) {
|
|
if r < '0' || r > '9' {
|
|
break
|
|
}
|
|
digits += string(r)
|
|
}
|
|
n, err := strconv.Atoi(digits)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("%s does not begin with the number it keeps: %q", path, first)
|
|
}
|
|
return n, nil
|
|
}
|
|
|
|
// Judge says what is wrong with a count against the kept number; empty when they agree. It only goes down: a rise is refused,
|
|
// and a fall asks the new number written in health-undeclared, so the count cannot rise again.
|
|
func Judge(kept, counted int) string {
|
|
switch {
|
|
case counted > kept:
|
|
return fmt.Sprintf("the long-running resources without health rose from %d to %d: declare how each new one is ready (ADR 0240 rule 8)", kept, counted)
|
|
case counted < kept:
|
|
return fmt.Sprintf("the long-running resources without health fell from %d to %d: write %d in health-undeclared, so the count cannot rise again", kept, counted, counted)
|
|
}
|
|
return ""
|
|
}
|