mesh/merge-gate error: builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step; the check could not run: a throwaway postgr…
mesh/repo-check error: the check could not run: a throwaway postgres:17-alpine could not be raised: docker run --label mesh.build=build-1791331512096605198…
mesh/delivery superseded: a newer head of the same pull request
The snapshot promises no address, and every module's repository, reads and sources carried the URL the mesh clones from. A check matches repositories by owner and name, so nothing it reads is lost (novox/hq issue 288).
190 lines
6.4 KiB
Go
190 lines
6.4 KiB
Go
package facts
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"regexp"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// A pseudonym keeps what a limit meets — the length, and letters where letters were — and nothing else.
|
|
func TestAPseudonymKeepsTheLengthAndShapeAndIsStable(t *testing.T) {
|
|
for _, name := range []string{"ace", "g14", "novox", "shanks", "home-server", "a"} {
|
|
p := Pseudonym("machine", name)
|
|
if len(p) != len(name) {
|
|
t.Errorf("%s became %s: %d characters for %d", name, p, len(p), len(name))
|
|
}
|
|
if p == name {
|
|
t.Errorf("%s was kept as itself", name)
|
|
}
|
|
if p != Pseudonym("machine", name) {
|
|
t.Errorf("%s is not stable", name)
|
|
}
|
|
for i := range name {
|
|
isLetter := func(c byte) bool { return c >= 'a' && c <= 'z' }
|
|
isDigit := func(c byte) bool { return c >= '0' && c <= '9' }
|
|
if isLetter(name[i]) != isLetter(p[i]) || isDigit(name[i]) != isDigit(p[i]) {
|
|
t.Errorf("%s became %s: the shape moved at %d", name, p, i)
|
|
}
|
|
}
|
|
}
|
|
if Pseudonym("machine", "ace") == Pseudonym("site", "ace") {
|
|
t.Error("a site and a machine of one name share a pseudonym, so a snapshot says they are one thing")
|
|
}
|
|
}
|
|
|
|
// Nothing of the installation survives the scrubber: names, domains, accounts, addresses, mail, secrets.
|
|
func TestTheScrubberLeavesNothingOfTheInstallation(t *testing.T) {
|
|
s := NewScrubber()
|
|
machine := s.Machine("homeserver")
|
|
s.Account("jochens")
|
|
domain := s.Domain("zurag.be")
|
|
if len(domain) != len("zurag.be") || !strings.HasSuffix(domain, ".be") || domain == "zurag.be" {
|
|
t.Errorf("the domain became %q", domain)
|
|
}
|
|
in := map[string]any{
|
|
"hub": "homeserver",
|
|
"listen": "10.42.0.7:51820",
|
|
"upstream": []any{"192.168.1.135", "fd00::1"},
|
|
"site": "https://grafana.zurag.be/login",
|
|
"admin": "jschoubben@gmail.com",
|
|
"home": "/home/jochens/.ssh",
|
|
"api_key": "sk-live-abcdef",
|
|
"nested": map[string]any{"password": "hunter2", "port": float64(5432)},
|
|
"opaque": "a8F3kQ9zL2mX7vB4nC6dE1rT5yU0iO8pA3sD",
|
|
"dsn": "postgres://app:s3cr3tpass@db.internal:5432/app",
|
|
"pem": "-----BEGIN PRIVATE KEY-----\nMIIB",
|
|
"plain": "a-long-plain-module-directory-name",
|
|
"digest": "sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a",
|
|
"version": "2.11.17",
|
|
"homeserver": true,
|
|
}
|
|
out := s.Values(in)
|
|
flat := flatten(out)
|
|
for _, leaked := range []string{"homeserver\"", "10.42.0.7", "192.168.1.135", "fd00::1", "zurag", "jschoubben",
|
|
"gmail", "jochens", "sk-live", "hunter2", "a8F3kQ9zL2mX7vB4nC6dE1rT5yU0iO8pA3sD", "s3cr3tpass", "MIIB"} {
|
|
if strings.Contains(flat, leaked) {
|
|
t.Errorf("%q survived the scrubber:\n%s", leaked, flat)
|
|
}
|
|
}
|
|
if out["hub"] != machine {
|
|
t.Errorf("a machine named in a setting became %v, not its pseudonym %s", out["hub"], machine)
|
|
}
|
|
for _, kept := range []string{"a-long-plain-module-directory-name", "2.11.17", "sha256:44136fa3", "5432"} {
|
|
if !strings.Contains(flat, kept) {
|
|
t.Errorf("%q was scrubbed, and it is not the installation's:\n%s", kept, flat)
|
|
}
|
|
}
|
|
// Every address left is a documentation address.
|
|
for _, a := range regexp.MustCompile(`[0-9a-f:.]{7,}`).FindAllString(flat, -1) {
|
|
ip := net.ParseIP(strings.Trim(a, ".:"))
|
|
if ip == nil {
|
|
continue
|
|
}
|
|
doc := false
|
|
for _, cidr := range []string{"192.0.2.0/24", "198.51.100.0/24", "203.0.113.0/24", "2001:db8::/32"} {
|
|
_, n, _ := net.ParseCIDR(cidr)
|
|
doc = doc || n.Contains(ip)
|
|
}
|
|
if !doc {
|
|
t.Errorf("%s is not a documentation address", a)
|
|
}
|
|
}
|
|
// The same address is always the same stand-in.
|
|
if s.Text("10.42.0.7") != s.Text("at 10.42.0.7")[3:] {
|
|
t.Error("one address became two stand-ins")
|
|
}
|
|
}
|
|
|
|
func flatten(v any) string {
|
|
var b strings.Builder
|
|
var walk func(any)
|
|
walk = func(v any) {
|
|
switch t := v.(type) {
|
|
case map[string]any:
|
|
for k, e := range t {
|
|
b.WriteString(k + "\"=")
|
|
walk(e)
|
|
b.WriteString("\n")
|
|
}
|
|
case []any:
|
|
for _, e := range t {
|
|
walk(e)
|
|
b.WriteString(",")
|
|
}
|
|
case string:
|
|
b.WriteString(t + "\"")
|
|
default:
|
|
b.WriteString(fmt.Sprint(t))
|
|
}
|
|
}
|
|
walk(v)
|
|
return b.String()
|
|
}
|
|
|
|
// Two snapshots of one mesh, taken apart, are one content.
|
|
func TestASnapshotOfAnUnchangedMeshIsTheSameContentAnotherDay(t *testing.T) {
|
|
f := Facts{Format: Format, Taken: time.Now(), Machines: []Machine{{Name: "b"}, {Name: "a"}}}
|
|
g := f
|
|
g.Taken = f.Taken.Add(24 * time.Hour)
|
|
g.Machines = []Machine{{Name: "a"}, {Name: "b"}}
|
|
a, err := f.Content()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
b, _ := g.Content()
|
|
if a != b {
|
|
t.Error("the same mesh a day later reads as a change")
|
|
}
|
|
g.Machines = append(g.Machines, Machine{Name: "c"})
|
|
if c, _ := g.Content(); c == a {
|
|
t.Error("a machine added reads as no change")
|
|
}
|
|
}
|
|
|
|
// A reader refuses a snapshot it cannot read whole.
|
|
func TestANewerSnapshotIsRefusedNotHalfRead(t *testing.T) {
|
|
if _, err := Decode([]byte(`{"facts": 99}`)); err == nil || !strings.Contains(err.Error(), "newer controller") {
|
|
t.Errorf("a newer format read as %v", err)
|
|
}
|
|
if _, err := Decode([]byte(`{"machines": []}`)); err == nil {
|
|
t.Error("a document with no format read as a snapshot")
|
|
}
|
|
f, err := Decode([]byte(`{"facts": 1, "machines": [{"name": "abc", "length": 3}, {"name": "defgh", "length": 5}]}`))
|
|
if err != nil || f.Longest() != 5 {
|
|
t.Errorf("read %+v, %v", f, err)
|
|
}
|
|
}
|
|
|
|
// A path that reads as a key is withheld, and keeps a path's shape: an access or a place must be an
|
|
// absolute path, and a bare word left a machine the mesh composes refused where a check raised it.
|
|
func TestAWithheldPathStaysAPath(t *testing.T) {
|
|
s := NewScrubber()
|
|
if got := s.Text("/storage/media/Formula1-Season-2026"); got != "/"+Withheld {
|
|
t.Errorf("a path reading as a key became %q", got)
|
|
}
|
|
if got := s.Text("/storage/media/movies"); got != "/storage/media/movies" {
|
|
t.Errorf("a plain path became %q", got)
|
|
}
|
|
if got := s.Text("Hunter2Hunter2Hunter2Hunter2xx"); got != Withheld {
|
|
t.Errorf("a key became %q", got)
|
|
}
|
|
}
|
|
|
|
// **Issue 288**: a repository was kept as the URL it was cloned from, the forge's address with it.
|
|
func TestARepositoryIsNamedWithoutTheForge(t *testing.T) {
|
|
for in, want := range map[string]string{
|
|
"http://forge.internal:3000/owner/repo.git": "owner/repo",
|
|
"ssh://git@forge.internal:222/owner/repo": "owner/repo",
|
|
"git@forge.internal:owner/repo.git": "owner/repo",
|
|
"owner/repo": "owner/repo",
|
|
"": "",
|
|
} {
|
|
if got := RepositoryName(in); got != want {
|
|
t.Errorf("%q is named %q, not %q", in, got, want)
|
|
}
|
|
}
|
|
}
|