mirror of
https://github.com/DeBrosOfficial/orama.git
synced 2026-03-17 16:06:58 +00:00
Replaces plaintext password-based SSH authentication (sshpass) across the entire Go CLI with wallet-derived ed25519 keys via RootWallet. - Add `rw vault ssh agent-load` command to RootWallet CLI for SSH agent forwarding in push fanout - Create wallet.go bridge: PrepareNodeKeys resolves keys from `rw vault ssh get --priv`, writes temp PEMs (0600), zero-overwrites on cleanup - Remove Password field from Node struct, update config parser to new 3-field format (env|user@host|role) - Remove all sshpass branches from inspector/ssh.go and remotessh/ssh.go, require SSHKey on all SSH paths - Add WithAgentForward() option to RunSSHStreaming for hub fanout - Add PrepareNodeKeys + defer cleanup to all 7 entry points: inspect, monitor, push, upgrade, clean, recover, install - Update push fanout to use SSH agent forwarding instead of sshpass on hub - Delete install/ssh.go duplicate, replace with remotessh calls - Create nodes.conf from remote-nodes.conf (topology only, no secrets) - Update all config defaults and help text from remote-nodes.conf to nodes.conf - Use StrictHostKeyChecking=accept-new consistently everywhere
174 lines
3.9 KiB
Go
174 lines
3.9 KiB
Go
package inspector
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
func TestLoadNodes(t *testing.T) {
|
|
content := `# Comment line
|
|
devnet|ubuntu@1.2.3.4|node
|
|
devnet|ubuntu@1.2.3.5|node
|
|
devnet|ubuntu@5.6.7.8|nameserver-ns1
|
|
`
|
|
path := writeTempFile(t, content)
|
|
|
|
nodes, err := LoadNodes(path)
|
|
if err != nil {
|
|
t.Fatalf("LoadNodes: %v", err)
|
|
}
|
|
if len(nodes) != 3 {
|
|
t.Fatalf("want 3 nodes, got %d", len(nodes))
|
|
}
|
|
|
|
// First node
|
|
n := nodes[0]
|
|
if n.Environment != "devnet" {
|
|
t.Errorf("node[0].Environment = %q, want devnet", n.Environment)
|
|
}
|
|
if n.User != "ubuntu" {
|
|
t.Errorf("node[0].User = %q, want ubuntu", n.User)
|
|
}
|
|
if n.Host != "1.2.3.4" {
|
|
t.Errorf("node[0].Host = %q, want 1.2.3.4", n.Host)
|
|
}
|
|
if n.Role != "node" {
|
|
t.Errorf("node[0].Role = %q, want node", n.Role)
|
|
}
|
|
if n.SSHKey != "" {
|
|
t.Errorf("node[0].SSHKey = %q, want empty (set at runtime)", n.SSHKey)
|
|
}
|
|
|
|
// Third node with nameserver role
|
|
n3 := nodes[2]
|
|
if n3.Role != "nameserver-ns1" {
|
|
t.Errorf("node[2].Role = %q, want nameserver-ns1", n3.Role)
|
|
}
|
|
}
|
|
|
|
func TestLoadNodes_EmptyLines(t *testing.T) {
|
|
content := `
|
|
# Full line comment
|
|
|
|
devnet|ubuntu@1.2.3.4|node
|
|
|
|
# Another comment
|
|
devnet|ubuntu@1.2.3.5|node
|
|
`
|
|
path := writeTempFile(t, content)
|
|
|
|
nodes, err := LoadNodes(path)
|
|
if err != nil {
|
|
t.Fatalf("LoadNodes: %v", err)
|
|
}
|
|
if len(nodes) != 2 {
|
|
t.Fatalf("want 2 nodes (blank/comment lines skipped), got %d", len(nodes))
|
|
}
|
|
}
|
|
|
|
func TestLoadNodes_InvalidFormat(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
content string
|
|
}{
|
|
{"too few fields", "devnet|ubuntu@1.2.3.4\n"},
|
|
{"no @ in userhost", "devnet|localhost|node\n"},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
path := writeTempFile(t, tt.content)
|
|
_, err := LoadNodes(path)
|
|
if err == nil {
|
|
t.Error("expected error for invalid format")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestLoadNodes_FileNotFound(t *testing.T) {
|
|
_, err := LoadNodes("/nonexistent/path/file.conf")
|
|
if err == nil {
|
|
t.Error("expected error for nonexistent file")
|
|
}
|
|
}
|
|
|
|
func TestFilterByEnv(t *testing.T) {
|
|
nodes := []Node{
|
|
{Environment: "devnet", Host: "1.1.1.1"},
|
|
{Environment: "testnet", Host: "2.2.2.2"},
|
|
{Environment: "devnet", Host: "3.3.3.3"},
|
|
}
|
|
filtered := FilterByEnv(nodes, "devnet")
|
|
if len(filtered) != 2 {
|
|
t.Fatalf("want 2 devnet nodes, got %d", len(filtered))
|
|
}
|
|
for _, n := range filtered {
|
|
if n.Environment != "devnet" {
|
|
t.Errorf("got env=%s, want devnet", n.Environment)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestFilterByRole(t *testing.T) {
|
|
nodes := []Node{
|
|
{Role: "node", Host: "1.1.1.1"},
|
|
{Role: "nameserver-ns1", Host: "2.2.2.2"},
|
|
{Role: "nameserver-ns2", Host: "3.3.3.3"},
|
|
{Role: "node", Host: "4.4.4.4"},
|
|
}
|
|
filtered := FilterByRole(nodes, "nameserver")
|
|
if len(filtered) != 2 {
|
|
t.Fatalf("want 2 nameserver nodes, got %d", len(filtered))
|
|
}
|
|
}
|
|
|
|
func TestRegularNodes(t *testing.T) {
|
|
nodes := []Node{
|
|
{Role: "node", Host: "1.1.1.1"},
|
|
{Role: "nameserver-ns1", Host: "2.2.2.2"},
|
|
{Role: "node", Host: "3.3.3.3"},
|
|
}
|
|
regular := RegularNodes(nodes)
|
|
if len(regular) != 2 {
|
|
t.Fatalf("want 2 regular nodes, got %d", len(regular))
|
|
}
|
|
}
|
|
|
|
func TestNode_Name(t *testing.T) {
|
|
n := Node{User: "ubuntu", Host: "1.2.3.4"}
|
|
if got := n.Name(); got != "ubuntu@1.2.3.4" {
|
|
t.Errorf("Name() = %q, want ubuntu@1.2.3.4", got)
|
|
}
|
|
}
|
|
|
|
func TestNode_IsNameserver(t *testing.T) {
|
|
tests := []struct {
|
|
role string
|
|
want bool
|
|
}{
|
|
{"nameserver-ns1", true},
|
|
{"nameserver-ns2", true},
|
|
{"node", false},
|
|
{"", false},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.role, func(t *testing.T) {
|
|
n := Node{Role: tt.role}
|
|
if got := n.IsNameserver(); got != tt.want {
|
|
t.Errorf("IsNameserver(%q) = %v, want %v", tt.role, got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func writeTempFile(t *testing.T, content string) string {
|
|
t.Helper()
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "test-nodes.conf")
|
|
if err := os.WriteFile(path, []byte(content), 0644); err != nil {
|
|
t.Fatalf("write temp file: %v", err)
|
|
}
|
|
return path
|
|
}
|