Make daemon startup sync a managed `Host *.vm` block into `~/.ssh/config` so plain `ssh root@<vm>.vm` uses banger's managed key and the same publickey-only options as `banger vm ssh`. Write the block directly instead of relying on a separate include file so it still applies when a user's SSH config ends inside another `Host` stanza, and remove the legacy managed include path. Add daemon tests that cover fresh config creation and managed-block replacement while preserving user entries. Validate with `go test ./...`, `make build`, `ssh -G alp.vm`, and `ssh alp.vm true`.
95 lines
2.6 KiB
Go
95 lines
2.6 KiB
Go
package daemon
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"banger/internal/paths"
|
|
)
|
|
|
|
func TestSyncVMSSHClientConfigCreatesManagedBlock(t *testing.T) {
|
|
homeDir := t.TempDir()
|
|
t.Setenv("HOME", homeDir)
|
|
|
|
layout := paths.Layout{
|
|
ConfigDir: filepath.Join(homeDir, ".config", "banger"),
|
|
}
|
|
keyPath := filepath.Join(layout.ConfigDir, "ssh", "id_ed25519")
|
|
|
|
if err := syncVMSSHClientConfig(layout, keyPath); err != nil {
|
|
t.Fatalf("syncVMSSHClientConfig: %v", err)
|
|
}
|
|
|
|
userConfigPath := filepath.Join(homeDir, ".ssh", "config")
|
|
userConfig, err := os.ReadFile(userConfigPath)
|
|
if err != nil {
|
|
t.Fatalf("ReadFile(user config): %v", err)
|
|
}
|
|
userContent := string(userConfig)
|
|
if !strings.Contains(userContent, vmSSHConfigIncludeBegin) {
|
|
t.Fatalf("user config = %q, want begin marker", userContent)
|
|
}
|
|
for _, want := range []string{
|
|
"Host *.vm",
|
|
"User root",
|
|
"IdentityFile " + keyPath,
|
|
"IdentitiesOnly yes",
|
|
"BatchMode yes",
|
|
"PasswordAuthentication no",
|
|
"UserKnownHostsFile /dev/null",
|
|
} {
|
|
if !strings.Contains(userContent, want) {
|
|
t.Fatalf("user config = %q, want %q", userContent, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSyncVMSSHClientConfigReplacesManagedIncludeBlock(t *testing.T) {
|
|
homeDir := t.TempDir()
|
|
t.Setenv("HOME", homeDir)
|
|
|
|
layout := paths.Layout{
|
|
ConfigDir: filepath.Join(homeDir, ".config", "banger"),
|
|
}
|
|
keyPath := filepath.Join(layout.ConfigDir, "ssh", "id_ed25519")
|
|
|
|
sshDir := filepath.Join(homeDir, ".ssh")
|
|
if err := os.MkdirAll(sshDir, 0o700); err != nil {
|
|
t.Fatalf("MkdirAll(.ssh): %v", err)
|
|
}
|
|
initial := strings.Join([]string{
|
|
"ServerAliveInterval 120",
|
|
"",
|
|
vmSSHConfigIncludeBegin,
|
|
"Include /tmp/old-banger-config",
|
|
vmSSHConfigIncludeEnd,
|
|
"",
|
|
"Host other",
|
|
" HostName 192.0.2.5",
|
|
"",
|
|
}, "\n")
|
|
if err := os.WriteFile(filepath.Join(sshDir, "config"), []byte(initial), 0o644); err != nil {
|
|
t.Fatalf("WriteFile(user config): %v", err)
|
|
}
|
|
|
|
if err := syncVMSSHClientConfig(layout, keyPath); err != nil {
|
|
t.Fatalf("syncVMSSHClientConfig: %v", err)
|
|
}
|
|
|
|
userConfig, err := os.ReadFile(filepath.Join(sshDir, "config"))
|
|
if err != nil {
|
|
t.Fatalf("ReadFile(user config): %v", err)
|
|
}
|
|
userContent := string(userConfig)
|
|
if strings.Count(userContent, vmSSHConfigIncludeBegin) != 1 {
|
|
t.Fatalf("user config = %q, want one managed block", userContent)
|
|
}
|
|
if !strings.Contains(userContent, "ServerAliveInterval 120") || !strings.Contains(userContent, "Host other") {
|
|
t.Fatalf("user config = %q, want existing entries preserved", userContent)
|
|
}
|
|
if !strings.Contains(userContent, "Host *.vm") || !strings.Contains(userContent, "IdentityFile "+keyPath) {
|
|
t.Fatalf("user config = %q, want refreshed managed vm block", userContent)
|
|
}
|
|
}
|