mesh/delivery merged: source.main moved to novox/mesh-host@9d8647aa0f56
mesh/check checking aa649d85a065 on 9d8647aa0f56
Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
77 lines
3.7 KiB
Go
77 lines
3.7 KiB
Go
package resolution
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
mcat "git.novox.be/novox/mesh-sdk/go/catalogue"
|
|
)
|
|
|
|
type writesHosts struct{}
|
|
|
|
func (writesHosts) Resources(string) ([]map[string]any, bool, error) {
|
|
return []map[string]any{{"id": "hosts", "type": "file", "path": "/etc/hosts", "into": "block",
|
|
"content": "10.42.0.1 anchor\n"}}, true, nil
|
|
}
|
|
|
|
// The controller is bound too: what it generates for a machine on a seat's path is refused at composition.
|
|
func TestAGeneratedResourceOnASeatsPathIsRefused(t *testing.T) {
|
|
r := mcat.Resolution{Node: "workstation", Modules: []mcat.Manifest{
|
|
{Module: "mesh-names", Computed: "mesh-names"},
|
|
}}
|
|
_, err := Declaration(r, mcat.Rendering{Generators: map[string]mcat.Generator{"mesh-names": writesHosts{}}})
|
|
if err == nil {
|
|
t.Fatal("a generated resource on /etc/hosts was composed")
|
|
}
|
|
for _, want := range []string{"mesh-names", "/etc/hosts", "node-hostname"} {
|
|
if !strings.Contains(err.Error(), want) {
|
|
t.Errorf("the refusal does not name %s: %v", want, err)
|
|
}
|
|
}
|
|
// And the paths a module owns do not change with its build (issue 1966): the holder of node-files is told them in a
|
|
// word of the tool runner's unit, so a place named for each build restarted the tool runner with each build.
|
|
digest := "sha256:" + strings.Repeat("ab12", 16)
|
|
for _, row := range []struct {
|
|
name string
|
|
res map[string]any
|
|
owns string
|
|
}{
|
|
{"a build's own place", map[string]any{"id": "next", "type": "archive", "digest": digest, "path": "/usr/lib/x/versions/ab12ab12ab12"}, "/usr/lib/x/versions"},
|
|
{"another build's place", map[string]any{"id": "next", "type": "archive", "digest": "sha256:" + strings.Repeat("cd34", 16), "path": "/usr/lib/x/versions/cd34cd34cd34"}, "/usr/lib/x/versions"},
|
|
{"a fixed place", map[string]any{"id": "app", "type": "archive", "digest": digest, "path": "/opt/app"}, "/opt/app"},
|
|
{"a file", map[string]any{"id": "f", "type": "file", "path": "/etc/f"}, "/etc/f"},
|
|
} {
|
|
t.Run(row.name, func(t *testing.T) {
|
|
got := DeclaredPaths(mcat.Manifest{Module: "m", Resources: []map[string]any{row.res}})
|
|
if len(got) != 1 || got[0] != row.owns {
|
|
t.Fatalf("owns %v, want [%s]", got, row.owns)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// The stepping back comes from the seat, and is of a rendered file only: an uplink holder declaring the
|
|
// resolver file as a resource beside a node-resolver holder is two writers of one path, refused when the
|
|
// machine is resolved — never composed twice — as before the seat named its paths.
|
|
func TestAnUplinkDeclaringTheResolverFileAsAResourceIsRefusedBesideTheResolver(t *testing.T) {
|
|
shelf := resolverShelf(t)
|
|
uplink := shelf["systemd-networkd"]
|
|
uplink.Facts = nil
|
|
uplink.Resources = append(append([]map[string]any(nil), uplink.Resources...),
|
|
map[string]any{"id": "resolvers", "type": "file", "path": mcat.ResolverFile, "content": "nameserver 10.42.0.1\n"})
|
|
shelf["systemd-networkd"] = uplink
|
|
shelf["local-resolver"] = localResolver()
|
|
_, err := Resolve(shelf, []string{"dnsmasq", "systemd-networkd", "local-resolver"}, splitDNSLaptop(), mcat.World{})
|
|
if err == nil || !strings.Contains(err.Error(), `both declare the path "/etc/resolv.conf"`) {
|
|
t.Fatalf("an uplink declaring the resolver file as a resource beside the resolver was not refused: %v", err)
|
|
}
|
|
// Its rendered file steps back, by the seat's definition, and nothing else of it does.
|
|
rendered := mcat.Manifest{Module: "uplink", Version: "1", Claims: []mcat.Claim{{Name: "node-uplink"}},
|
|
Facts: map[string]mcat.RosterFile{"resolvers": {Path: "/etc//resolv.conf", Template: "x"},
|
|
"other": {Path: "/etc/uplink.conf", Template: "x"}}}
|
|
got := stepsBack(rendered, []mcat.Manifest{rendered, localResolver()})
|
|
if len(got.Facts) != 1 || got.Facts["other"].Path == "" {
|
|
t.Errorf("the uplink's holder renders %v beside the resolver", got.Facts)
|
|
}
|
|
}
|