ADR 0593 §7: the composer with its refusals leaves mesh-sdk, so mesh-sdk carries no composition rule. The catalogue functions only the node-engine's composition reaches (Resolve, Resolution's Compose, Declaration, DataItems, LeftOut, Rules and the rest, the settings, placeholder and template refusals, the rendering helpers) are copied with their tests into internal/compose/resolution; a method of a mesh-sdk shape becomes a function taking it first. The composition calls its own copies, and the captured catalogue its tests read lives here now, shared by mesh-compose's tests. Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
84 lines
3.9 KiB
Go
84 lines
3.9 KiB
Go
package resolution
|
|
|
|
import (
|
|
"testing"
|
|
|
|
mcat "git.novox.be/novox/mesh-sdk/go/catalogue"
|
|
)
|
|
|
|
func TestTheAgentAccountFactFallsBackToTheOperatorAndIsNeverRootOnlyWhenItsOwn(t *testing.T) {
|
|
facts := machineFacts(mcat.Resolution{Node: "anchor", Account: "ops"}, nil, "")
|
|
if facts["agent-account"] != "ops" || facts["agent-home"] != "/home/ops" || facts["agent-root"] != "" {
|
|
t.Errorf("with no agent account named, agents run as the operator: %v", facts)
|
|
}
|
|
facts = machineFacts(mcat.Resolution{Node: "anchor", Account: "ops", AccountHome: "/srv/ops"}, nil, "")
|
|
if facts["agent-home"] != "/srv/ops" {
|
|
t.Errorf("the operator's stated home is the agent's home when they are one account: %v", facts)
|
|
}
|
|
facts = machineFacts(mcat.Resolution{Node: "anchor", Account: "ops", AgentAccount: "agent"}, nil, "")
|
|
if facts["agent-account"] != "agent" || facts["agent-home"] != "/home/agent" || facts["agent-root"] != mcat.RootNever {
|
|
t.Errorf("a named agent account is the agents', never root: %v", facts)
|
|
}
|
|
if facts["account"] != "ops" {
|
|
t.Errorf("the operator account is still the operator's: %v", facts)
|
|
}
|
|
facts = machineFacts(mcat.Resolution{Node: "anchor", AgentAccount: "agent", AgentAccountHome: "/var/lib/agent"}, nil, "")
|
|
if facts["agent-home"] != "/var/lib/agent" || facts["agent-root"] != mcat.RootNever {
|
|
t.Errorf("an agent account with a stated home on a machine with no operator: %v", facts)
|
|
}
|
|
if _, has := machineFacts(mcat.Resolution{Node: "anchor"}, nil, "")["agent-account"]; has {
|
|
t.Error("a machine with no account at all names an agent account")
|
|
}
|
|
}
|
|
|
|
// The agent's module, in the shape the catalogue's declares it: the account, never root where it is its
|
|
// own; its directory under that home, owned by it.
|
|
const agentModule = `{"module": "agent", "version": "1", "resources": [
|
|
{"id": "account", "type": "user", "name": "${machine:agent-account}", "home": "${machine:agent-home}",
|
|
"root": "${machine:agent-root}"},
|
|
{"id": "home", "type": "directory", "path": "${machine:agent-home}/.agent", "mode": "0700",
|
|
"owner": "${machine:agent-account}"}
|
|
]}`
|
|
|
|
func TestTheAgentAccountIsDeclaredNeverRootOnlyToAnEngineThatJudgesIt(t *testing.T) {
|
|
m, err := decodeForTest([]byte(agentModule))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
compose := func(r mcat.Resolution, with mcat.Rendering) (user, home map[string]any) {
|
|
t.Helper()
|
|
r.Node, r.Modules = "anchor", []mcat.Manifest{m}
|
|
out, err := Declaration(r, with)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return fileNamed(out, "agent.account"), fileNamed(out, "agent.home")
|
|
}
|
|
|
|
user, home := compose(mcat.Resolution{Account: "ops", AgentAccount: "agent"}, mcat.Rendering{JudgesRoot: true})
|
|
if user["name"] != "agent" || user[mcat.RootField] != mcat.RootNever || user["home"] != "/home/agent" {
|
|
t.Errorf("an engine that judges root is sent the agent account never to become root: %v", user)
|
|
}
|
|
if home["path"] != "/home/agent/.agent" || home["owner"] != "agent" {
|
|
t.Errorf("the agent's directory is under its own home, its own: %v", home)
|
|
}
|
|
|
|
user, _ = compose(mcat.Resolution{Account: "ops", AgentAccount: "agent", AgentAccountHome: "/srv/agent"}, mcat.Rendering{JudgesRoot: true})
|
|
if user["home"] != "/srv/agent" {
|
|
t.Errorf("an agent account named with a home of its own is made there: %v", user)
|
|
}
|
|
|
|
user, _ = compose(mcat.Resolution{Account: "ops", AgentAccount: "agent"}, mcat.Rendering{})
|
|
if _, sent := user[mcat.RootField]; sent || user["name"] != "agent" {
|
|
t.Errorf("an older engine, which parses strictly, is sent root: %v", user)
|
|
}
|
|
|
|
user, home = compose(mcat.Resolution{Account: "ops"}, mcat.Rendering{JudgesRoot: true})
|
|
if _, sent := user[mcat.RootField]; sent || user["name"] != "ops" {
|
|
t.Errorf("where agents run as the operator, root asserts nothing and is not sent: %v", user)
|
|
}
|
|
if home["path"] != "/home/ops/.agent" || home["owner"] != "ops" {
|
|
t.Errorf("with no agent account, the agent's directory is the operator's: %v", home)
|
|
}
|
|
}
|