A node carries its operator account (name + home; migration 0036, Node.Account,
SetAccount, 'node account' CLI). The account and its home are offered as
machine facts ${machine:account} / ${machine:account-home}, and machineInto
now resolves placeholders in a resource's path and owner (not just content), so
a module writes into a person's home naming what it cannot know. A RosterFile
gains Home: the file is placed under the account's home and chowned to it, its
template sees each node's Account, and a machine with no account gets none —
this is how the ssh Host blocks for every node reach a person's ~/.ssh. Roster
carries per-node accounts (Rendering.Accounts). Tested, including ssh-client
composed end-to-end. Not deployed.
47 lines
1.7 KiB
Go
47 lines
1.7 KiB
Go
package catalogue
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// The ssh-client module, composed as a machine receives it (novox/hq to-be 29): every other node's
|
|
// Host block written into a marked region of the operator's ~/.ssh/config, owned by the account,
|
|
// with ~/.ssh created 0700 — the operator's own config kept.
|
|
func TestSSHClientOwnsTheOperatorsSSHConfig(t *testing.T) {
|
|
shelf := shelf(catalogueManifest(t, "ssh-client"))
|
|
got, err := Resolve(shelf, []string{"ssh-client"},
|
|
Node{Name: "homer", At: "homer.internal", Account: "jo"}, World{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
names := map[string]string{"homer.internal": "10.10.0.1", "marge.internal": "10.10.0.2"}
|
|
out, err := got.Declaration(Rendering{
|
|
Names: names, Machines: names, Accounts: map[string]string{"homer": "jo", "marge": "jo"},
|
|
Suffix: "internal",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
by := map[string]map[string]any{}
|
|
for _, r := range out {
|
|
by[r["id"].(string)] = r
|
|
}
|
|
|
|
dir := by["ssh-client.ssh-dir"]
|
|
if dir == nil || dir["path"] != "/home/jo/.ssh" || dir["owner"] != "jo" || dir["mode"] != "0700" {
|
|
t.Fatalf("~/.ssh is not created 0700 owned by the account: %v", dir)
|
|
}
|
|
cfg := by["ssh-client.fact-ssh-config"]
|
|
if cfg == nil || cfg["path"] != "/home/jo/.ssh/config" || cfg["owner"] != "jo" || cfg["into"] != "block" {
|
|
t.Fatalf("the ssh config is not written into the operator's ~/.ssh/config as a region: %v", cfg)
|
|
}
|
|
body := cfg["content"].(string)
|
|
if !strings.Contains(body, "Host marge marge.internal") || !strings.Contains(body, "User jo") {
|
|
t.Fatalf("the config does not name the peer node and its account:\n%s", body)
|
|
}
|
|
if strings.Contains(body, "Host homer ") {
|
|
t.Fatalf("the config names the machine itself, not only its peers:\n%s", body)
|
|
}
|
|
}
|