Three of novox/hq's group-4 leftovers, one branch.
Issue 173: a module's settings reached every route it contributed, every database it asked for and
every served fact its consumers read — a mail server's site name arrived at the proxy as a route
fact. A setting now overrides a key a contribution or served fact declares and adds none; a file
still merges any key, and a key nothing takes is named as stray instead of dropped silently.
Issue 174: the mesh's own files for a module — its bus credential, its merged config, its bindings —
were placed by the definition under /var/lib/mesh/<module>, 232 host paths in 50 definitions. A
directory may now say `place: "mesh"` and resolves to <root>/mesh/<module>; a directory beneath a
placed one may state its path as `${dir:<id>}/<rest>` and moves with it. The proof test resolves
both catalogues and compares: 48 definitions, no path moved. The controller's own manifest is
converted here; the catalogue in mesh-catalog.
ADR 0155: the installation check moves to registration. `module add` and a build's result both
refuse a definition that names an installation, in the check's words, with the way out; the build
stays recorded.
136 lines
4.2 KiB
Go
136 lines
4.2 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
|
|
"github.com/novox/mesh-controller/internal/catalogue"
|
|
)
|
|
|
|
// moduleCheck judges manifests where they are written, with no mesh (novox/hq ADR 0037, issue 148).
|
|
//
|
|
// **The same functions registration runs, and nothing the command line adds** (ADR 0035): the strict
|
|
// parse with every per-manifest problem, then the rules no single manifest can be judged against,
|
|
// over exactly the manifests given. Somebody describing their own application in their own
|
|
// repository runs this before pushing and finds out there, rather than when a running mesh refuses
|
|
// the registration or, later, when a machine applies something that resolved and should not have.
|
|
//
|
|
// **What it cannot know without a store, it says.** The mesh's own seat set is the store's (ADR
|
|
// 0122); this binary carries a compiled copy that the store overrides when loaded, so a claim on a
|
|
// mesh seat is judged fully only at registration. A seat another module declares is unknown unless
|
|
// that module's manifest is passed too. Both are printed as a note, not as a problem — a check that
|
|
// refused what it could not see would teach people to ignore it.
|
|
func moduleCheck(paths []string, out io.Writer) error {
|
|
if len(paths) == 0 {
|
|
return errors.New("module check <manifest.json>... — one file per module; pass every " +
|
|
"manifest of a repository together so the rules between them are checked too")
|
|
}
|
|
shelf := catalogue.Shelf{}
|
|
faulted := map[string]bool{}
|
|
failed := 0
|
|
for _, path := range paths {
|
|
raw, err := os.ReadFile(path)
|
|
if err != nil {
|
|
fmt.Fprintf(out, "%s: %v\n", path, err)
|
|
failed++
|
|
continue
|
|
}
|
|
m, err := catalogue.ParseManifest(raw)
|
|
if err != nil {
|
|
fmt.Fprintf(out, "%s: %v\n", path, err)
|
|
failed++
|
|
continue
|
|
}
|
|
if first, twice := shelf[m.Module]; twice {
|
|
_ = first
|
|
fmt.Fprintf(out, "%s: %s was already given; two manifests name one module\n", path, m.Module)
|
|
failed++
|
|
continue
|
|
}
|
|
// A definition names no installation (novox/hq ADR 0112, ADR 0155): judged here, in the
|
|
// catalogue-wide test, and at registration, which refuses in the same words.
|
|
if named := catalogue.InstallationProblems(m); len(named) > 0 {
|
|
for _, p := range named {
|
|
fmt.Fprintf(out, "%s: %s\n", path, p)
|
|
}
|
|
failed += len(named)
|
|
faulted[m.Module] = true
|
|
}
|
|
shelf[m.Module] = m
|
|
}
|
|
|
|
// Between the manifests: a seat declared twice, a use of a seat nothing declares, a claim on
|
|
// a seat that does not exist. Run only over what parsed, because a problem inside one manifest
|
|
// has already been said and would be said again here in a worse form.
|
|
problems := catalogue.CatalogueProblems(shelf)
|
|
sort.Strings(problems)
|
|
for _, p := range problems {
|
|
fmt.Fprintln(out, p)
|
|
}
|
|
failed += len(problems)
|
|
|
|
var names []string
|
|
for name := range shelf {
|
|
names = append(names, name)
|
|
}
|
|
sort.Strings(names)
|
|
for _, name := range names {
|
|
m := shelf[name]
|
|
if faulted[name] {
|
|
continue
|
|
}
|
|
fmt.Fprintf(out, "%s: ok", name)
|
|
if n := len(m.Tools); n > 0 {
|
|
fmt.Fprintf(out, ", %d tool(s)", n)
|
|
}
|
|
if len(m.Invokes) > 0 {
|
|
fmt.Fprintf(out, ", invokes %s", joinInvokes(m.Invokes))
|
|
}
|
|
fmt.Fprintln(out)
|
|
}
|
|
if failed > 0 {
|
|
return fmt.Errorf("%d problem(s) in %d manifest(s)", failed, len(paths))
|
|
}
|
|
fmt.Fprintf(out, "%d manifest(s) checked. Judged against the seats this binary carries; a claim on "+
|
|
"one of the mesh's own seats is judged fully at registration, and a seat declared by a "+
|
|
"module not given here reads as unknown\n", len(paths))
|
|
return nil
|
|
}
|
|
|
|
func joinInvokes(invokes []string) string {
|
|
if len(invokes) == 1 && invokes[0] == "*" {
|
|
return "every tool"
|
|
}
|
|
s := ""
|
|
for i, t := range invokes {
|
|
if i > 0 {
|
|
s += ", "
|
|
}
|
|
s += t
|
|
}
|
|
return s
|
|
}
|
|
|
|
// manifestsUnder lists every module.json below a directory, for `module check <dir>`.
|
|
func manifestsUnder(dir string) ([]string, error) {
|
|
var found []string
|
|
err := filepath.WalkDir(dir, func(path string, d os.DirEntry, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if d.IsDir() && (d.Name() == "node_modules" || d.Name() == ".git" || d.Name() == "dist") {
|
|
return filepath.SkipDir
|
|
}
|
|
if !d.IsDir() && d.Name() == "module.json" {
|
|
found = append(found, path)
|
|
}
|
|
return nil
|
|
})
|
|
sort.Strings(found)
|
|
return found, err
|
|
}
|