Files
mesh-controller/internal/catalogue/secret_family_test.go
T
jochen d5f6b67b94
mesh/merge-gate pass: builds build-agent, mesh-controller → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without …
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery delivered
Say a part waiting for the operator as needs-operator and pass its gate; add secret families (issue 386, hq ADR 0283)
A module waiting for the operator's secret failed its first-node gate, held
every later walk and was said as 'nothing for you to do'. The node-engine's
new waiting state is checked against the manifest and the secrets given,
read by the gate as a wait for a person, and raised as needs-operator naming
the act. A secret family gives each part its own one-line secret, which the
mesh never makes, so the desk prompt can take each password.
2026-10-10 21:19:58 +02:00

101 lines
4.1 KiB
Go

package catalogue
import (
"strings"
"testing"
)
// A secret family (novox/hq ADR 0283): one own secret per part the settings name, issued outside the mesh, each
// given by its full name, never made by the mesh.
func familyManifest(t *testing.T, ownSecrets string) (Manifest, error) {
t.Helper()
return ParseManifest([]byte(`{"module":"mounts","version":"1","own-secrets":{` + ownSecrets + `}}`))
}
func TestAFamilyIsDeclaredIssuedOutsideWithOneStar(t *testing.T) {
m, err := familyManifest(t, `"smb-password-*":{"path":"/var/lib/mounts/smb-password-*.secret","issued-by":"outside"}`)
if err != nil {
t.Fatalf("a well-formed family was refused: %v", err)
}
if got := m.OwnSecrets.Families(); len(got) != 1 || got[0] != "smb-password-*" {
t.Fatalf("families: %v", got)
}
if len(m.OwnSecrets.Plain()) != 0 {
t.Fatalf("a family counted as a secret declared by name: %v", m.OwnSecrets.Plain())
}
}
func TestAMalformedFamilyIsRefused(t *testing.T) {
for name, c := range map[string]struct{ own, says string }{
"made by the mesh": {`"smb-password-*":{"path":"/s/smb-password-*.secret","taken":"at-start"}`, "issued-by"},
"no star in its path": {`"smb-password-*":{"path":"/s/smb-password.secret","issued-by":"outside"}`,
"exactly one *"},
"two stars in its path": {`"smb-password-*":{"path":"/s/*/smb-password-*.secret","issued-by":"outside"}`,
"exactly one *"},
"a star inside a name": {`"smb*password":{"path":"/s/x","issued-by":"outside"}`, "only ends a family"},
"a name that is also a member": {`"smb-password-*":{"path":"/s/p-*","issued-by":"outside"},
"smb-password-games":{"path":"/s/games","issued-by":"outside"}`, "also a member"},
} {
if _, err := familyManifest(t, c.own); err == nil || !strings.Contains(err.Error(), c.says) {
t.Errorf("%s: %v; want a refusal saying %q", name, err, c.says)
}
}
}
func TestAMemberIsFoundByItsFullNameAndLandsWhereTheStarIs(t *testing.T) {
o := OwnSecrets{
"smb-password-*": {Path: "/s/smb-password-*.secret", IssuedBy: IssuedOutside},
"smb-password-big-*": {Path: "/big/*.secret", IssuedBy: IssuedOutside},
"token": {Path: "/s/token", IssuedBy: IssuedOutside},
}
s, family, ok := o.Lookup("smb-password-games")
if !ok || family != "smb-password-*" || s.Path != "/s/smb-password-games.secret" || s.IssuedBy != IssuedOutside {
t.Fatalf("a member: %+v %q %v", s, family, ok)
}
if s, family, ok := o.Lookup("smb-password-big-one"); !ok || family != "smb-password-big-*" || s.Path != "/big/one.secret" {
t.Fatalf("the longest family that fits: %+v %q %v", s, family, ok)
}
if s, family, ok := o.Lookup("token"); !ok || family != "" || s.Path != "/s/token" {
t.Fatalf("a secret declared by name: %+v %q %v", s, family, ok)
}
for _, name := range []string{"smb-password-*", "smb-password-", "smb-password-../etc", "smb-password-a/b",
"smb-password-a.b", "smb-password-Games", "smb-password--x", "other"} {
if _, _, ok := o.Lookup(name); ok {
t.Errorf("%q was found as a secret", name)
}
}
}
// A member given is one file at its path; one not given is nothing, and the machine still composes — the mesh never
// makes a member.
func TestAMemberGivenIsPlacedAndOneNotGivenIsNothing(t *testing.T) {
m, err := familyManifest(t, `"smb-password-*":{"path":"/var/lib/mounts/smb-password-*.secret","issued-by":"outside"}`)
if err != nil {
t.Fatal(err)
}
r := Resolution{Node: "workstation", Modules: []Manifest{m}}
out, err := r.Declaration(Rendering{Needed: map[string]map[string]string{"mounts": {"smb-password-games": "sealed"}}})
if err != nil {
t.Fatalf("a module with one member given did not compose: %v", err)
}
var paths []string
for _, res := range out {
if res["sealed"] != nil {
paths = append(paths, res["path"].(string))
}
}
if len(paths) != 1 || paths[0] != "/var/lib/mounts/smb-password-games.secret" {
t.Fatalf("placed: %v", paths)
}
out, err = r.Declaration(Rendering{})
if err != nil {
t.Fatalf("a module with no member given did not compose: %v", err)
}
for _, res := range out {
if res["sealed"] != nil {
t.Fatalf("a member nobody gave was placed: %v", res)
}
}
}