Files
llmgw/internal/onboard/sshconfig_test.go
T
Ilker ManapandClaude Opus 5.5 0d78f9bb34 A machine given by address is reached through its ssh config Host
node add (and server add, mirror peer, patroni init) is usually given an
address, while the ssh config names the machine: `Host gpu01` with
`HostName 10.0.0.21` and the User, IdentityFile or ProxyJump it needs. That
block did not match the address, so the address had to be added to its
Host line by hand. Now the concrete Host names in the config and its
Includes are read, ssh -G says each one's HostName, and the first that is
the address (or resolves to it) is connected through.

Under sudo, the caller's name is now a fallback: a User in the config's
block for the machine comes first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 10:42:23 +01:00

88 lines
2.7 KiB
Go

package onboard
import (
"context"
"os"
"os/exec"
"os/user"
"path/filepath"
"testing"
)
// withSSHConfig points configAlias and ssh -G at one file, and nothing else.
func withSSHConfig(t *testing.T, config string, extra map[string]string) {
t.Helper()
if _, err := exec.LookPath("ssh"); err != nil {
t.Skip("no ssh")
}
dir := t.TempDir()
path := filepath.Join(dir, "config")
if err := os.WriteFile(path, []byte(config), 0o600); err != nil {
t.Fatal(err)
}
for name, body := range extra {
if err := os.WriteFile(filepath.Join(dir, name), []byte(body), 0o600); err != nil {
t.Fatal(err)
}
}
oldUser, oldSys, oldArgs := userSSHConfig, systemSSHConfig, sshExtraArgs
userSSHConfig = func() string { return path }
systemSSHConfig = ""
sshExtraArgs = []string{"-F", path}
t.Cleanup(func() { userSSHConfig, systemSSHConfig, sshExtraArgs = oldUser, oldSys, oldArgs })
}
// A machine given by address is reached through the Host block whose
// HostName it is, with that block's user — not the sudo caller's.
func TestSSHTargetUsesConfigAlias(t *testing.T) {
withSSHConfig(t, `
Host *.example
User nobody-wild
Host gpu01 gpu01-alt
HostName 10.9.8.7
User ops
Host gpu02
HostName=10.9.8.8
`, nil)
ctx := context.Background()
cases := []struct {
s *SSH
want string
}{
// The config's User applies by ssh itself: no fallback is added.
{&SSH{Addr: "10.9.8.7", FallbackUser: "ilker"}, "gpu01"},
{&SSH{Addr: "10.9.8.7", User: "root", FallbackUser: "ilker"}, "root@gpu01"},
{&SSH{Addr: "10.9.8.8", FallbackUser: "ilker"}, "ilker@gpu02"},
{&SSH{Addr: "gpu01", FallbackUser: "ilker"}, "gpu01"},
{&SSH{Addr: "10.9.8.9", FallbackUser: "ilker"}, "ilker@10.9.8.9"},
{&SSH{Addr: "10.9.8.9"}, "10.9.8.9"},
}
for _, c := range cases {
if got := c.s.Target(ctx); got != c.want {
t.Errorf("%+v: target %q, want %q", c.s.Addr, got, c.want)
}
}
}
// A block in an included file counts, as it does for ssh.
func TestSSHTargetFollowsInclude(t *testing.T) {
withSSHConfig(t, "Include nodes.conf\n", map[string]string{
"nodes.conf": "Host \"gpu03\"\n HostName 10.9.8.10\n",
})
// ssh resolves a relative Include against ~/.ssh, not -F's directory;
// the scan does the same, so point base at the test's directory.
names := hostNames(userSSHConfig(), filepath.Dir(userSSHConfig()), map[string]bool{}, 0)
if len(names) != 1 || names[0] != "gpu03" {
t.Fatalf("names %q", names)
}
}
func TestConfigUserIgnoresLocalDefault(t *testing.T) {
withSSHConfig(t, "Host x\n HostName 10.9.8.11\n", nil)
if cur, err := user.Current(); err == nil {
if u := configUser(context.Background(), "x"); u != "" {
t.Errorf("config user %q for a block naming none (local %q)", u, cur.Username)
}
}
}