Place the forge's known host, SSH Host and git rewrite (issue 955 S3)

This commit is contained in:
fix-955-ssh
2026-10-16 22:43:51 +02:00
parent a4cdd05cb2
commit ad5a0f8e44
5 changed files with 128 additions and 0 deletions
@@ -4,6 +4,7 @@ import (
"encoding/json"
"os"
"path/filepath"
"strings"
"testing"
)
@@ -88,3 +89,89 @@ func TestManifestKeepsKeysAndRequiresForgeAccount(t *testing.T) {
t.Errorf("provides = %v, want none (no ssh-key provision yet)", m.Provides)
}
}
// Issue 955, area S3: the three files the forge account fills in — known_hosts, the forge's Host, git's rewrite.
var sampleBound = map[string]string{
"known_host": "[novox.internal]:222 ssh-ed25519 AAAAC3Nza",
"ssh_host": "novox.internal",
"ssh_port": "222",
"ssh_base": "ssh://git@novox.internal:222/",
"https_base": "https://git.novox.be/",
}
// renderBound fills ${bound:forge-account:<key>} with the sample values, as the provisioner client does.
func renderBound(t *testing.T, s string) string {
t.Helper()
for k, v := range sampleBound {
s = strings.ReplaceAll(s, "${bound:forge-account:"+k+"}", v)
}
if strings.Contains(s, "${") {
t.Errorf("unfilled placeholder in %q", s)
}
return s
}
func resourceByPath(t *testing.T, path string) map[string]any {
t.Helper()
raw, err := os.ReadFile(filepath.Join(moduleDir, "module.json"))
if err != nil {
t.Fatal(err)
}
var m struct {
Resources []map[string]any `json:"resources"`
}
if err := json.Unmarshal(raw, &m); err != nil {
t.Fatal(err)
}
for _, r := range m.Resources {
if r["path"] == path {
if r["owner"] != "${machine:account}" {
t.Errorf("%s is owned by %v, not the machine's account", path, r["owner"])
}
return r
}
}
t.Fatalf("no resource at %s", path)
return nil
}
func TestForgeFilesRenderFromTheBoundValues(t *testing.T) {
home := "${machine:account-home}"
kh := resourceByPath(t, home+"/.ssh/known_hosts")
if kh["into"] != "block" {
t.Errorf("known_hosts is %v, want a block", kh["into"])
}
if got := strings.TrimSpace(renderBound(t, templateText(t, kh))); got != "[novox.internal]:222 ssh-ed25519 AAAAC3Nza" {
t.Errorf("known_hosts block = %q", got)
}
forge := resourceByPath(t, home+"/.ssh/config.d/10-forge")
if forge["into"] != nil {
t.Errorf("10-forge is a block, want the whole file")
}
want := "Host novox.internal\n Port 222\n User git\n IdentityFile ~/.ssh/mesh_ed25519\n IdentitiesOnly yes\n"
if got := renderBound(t, templateText(t, forge)); got != want {
t.Errorf("10-forge = %q, want %q", got, want)
}
resourceByPath(t, home+"/.config/git") // the directory
g := resourceByPath(t, home+"/.config/git/config")
if g["into"] != "block" {
t.Errorf("git config is %v, want a block", g["into"])
}
wantGit := "[url \"ssh://git@novox.internal:222/\"]\n\tinsteadOf = https://git.novox.be/\n"
if got := renderBound(t, templateText(t, g)); got != wantGit {
t.Errorf("git config block = %q, want %q", got, wantGit)
}
}
// The mesh's region in ~/.ssh/config brings in config.d/*, so 10-forge is read.
func TestConfigIncludesTheDropInDirectory(t *testing.T) {
raw, err := os.ReadFile(filepath.Join(moduleDir, "files", "config"))
if err != nil {
t.Fatal(err)
}
if !strings.Contains(string(raw), "Include ~/.ssh/config.d/*") {
t.Errorf("files/config does not include config.d/*")
}
}
+5
View File
@@ -0,0 +1,5 @@
Host ${bound:forge-account:ssh_host}
Port ${bound:forge-account:ssh_port}
User git
IdentityFile ~/.ssh/mesh_ed25519
IdentitiesOnly yes
+2
View File
@@ -0,0 +1,2 @@
[url "${bound:forge-account:ssh_base}"]
insteadOf = ${bound:forge-account:https_base}
+1
View File
@@ -0,0 +1 @@
${bound:forge-account:known_host}
+33
View File
@@ -51,6 +51,39 @@
"into": "block",
"at": "start",
"template": "files/config"
},
{
"id": "known-hosts",
"type": "file",
"path": "${machine:account-home}/.ssh/known_hosts",
"owner": "${machine:account}",
"mode": "0644",
"into": "block",
"template": "files/known_hosts"
},
{
"id": "forge-host",
"type": "file",
"path": "${machine:account-home}/.ssh/config.d/10-forge",
"owner": "${machine:account}",
"mode": "0600",
"template": "files/forge"
},
{
"id": "git-config-dir",
"type": "directory",
"path": "${machine:account-home}/.config/git",
"owner": "${machine:account}",
"mode": "0755"
},
{
"id": "git-config",
"type": "file",
"path": "${machine:account-home}/.config/git/config",
"owner": "${machine:account}",
"mode": "0644",
"into": "block",
"template": "files/gitconfig"
}
],
"facts": {