add ProxyJump function
This commit is contained in:
1 parent
56e20e5464
commit
7f0e0fdb23
48 files changed
+3875
-44
No files matched your search
@@ -0,0 +1,168 @@
|
||||
package keymgr
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"ssh-access-tui/internal/model"
|
||||
)
|
||||
|
||||
func TestHostAliases(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
forwards []model.Forward
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
forwards: nil,
|
||||
want: []string{},
|
||||
},
|
||||
{
|
||||
name: "single service per host uses bare host name",
|
||||
forwards: []model.Forward{
|
||||
{HostName: "db-prod-1", ServiceName: "ssh"},
|
||||
{HostName: "web-1", ServiceName: "https"},
|
||||
},
|
||||
want: []string{"db-prod-1", "web-1"},
|
||||
},
|
||||
{
|
||||
name: "multiple services on same host get host-service alias",
|
||||
forwards: []model.Forward{
|
||||
{HostName: "db-prod-1", ServiceName: "ssh"},
|
||||
{HostName: "db-prod-1", ServiceName: "web-admin"},
|
||||
},
|
||||
want: []string{"db-prod-1-ssh", "db-prod-1-web-admin"},
|
||||
},
|
||||
{
|
||||
name: "mixed: one host with a single service, another with several",
|
||||
forwards: []model.Forward{
|
||||
{HostName: "db-prod-1", ServiceName: "ssh"},
|
||||
{HostName: "db-prod-1", ServiceName: "web-admin"},
|
||||
{HostName: "cache-1", ServiceName: "redis"},
|
||||
},
|
||||
want: []string{"db-prod-1-ssh", "db-prod-1-web-admin", "cache-1"},
|
||||
},
|
||||
{
|
||||
name: "order is preserved, not sorted",
|
||||
forwards: []model.Forward{
|
||||
{HostName: "zzz-host", ServiceName: "ssh"},
|
||||
{HostName: "aaa-host", ServiceName: "ssh"},
|
||||
},
|
||||
want: []string{"zzz-host", "aaa-host"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := hostAliases(tt.forwards)
|
||||
if len(got) != len(tt.want) {
|
||||
t.Fatalf("hostAliases() = %v, want %v", got, tt.want)
|
||||
}
|
||||
for i := range got {
|
||||
if got[i] != tt.want[i] {
|
||||
t.Errorf("hostAliases()[%d] = %q, want %q (full: got=%v want=%v)", i, got[i], tt.want[i], got, tt.want)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenderClientConfigProxyJump(t *testing.T) {
|
||||
forwards := []model.Forward{
|
||||
{HostName: "db-prod-1", Address: "10.0.0.5", Port: 22, ServiceName: "ssh", Description: "primary db"},
|
||||
{HostName: "db-prod-1", Address: "10.0.0.5", Port: 8080, ServiceName: "web-admin"},
|
||||
{HostName: "cache-1", Address: "10.0.0.9", Port: 6379, ServiceName: "redis"},
|
||||
}
|
||||
|
||||
out := RenderClientConfigProxyJump("bastion.example.com", 2222, "a-makarov", "a-makarov_jump_key", forwards)
|
||||
|
||||
// The one always-present "jump" block.
|
||||
for _, want := range []string{
|
||||
"Host jump\n",
|
||||
" HostName bastion.example.com\n",
|
||||
" Port 2222\n",
|
||||
" User a-makarov\n",
|
||||
" IdentityFile ~/.ssh/a-makarov_jump_key\n",
|
||||
} {
|
||||
if !strings.Contains(out, want) {
|
||||
t.Errorf("output missing jump block line %q\nfull output:\n%s", want, out)
|
||||
}
|
||||
}
|
||||
|
||||
// LocalForward is the LocalForward-mode marker; it must never appear here.
|
||||
if strings.Contains(out, "LocalForward") {
|
||||
t.Errorf("ProxyJump-mode output must not contain LocalForward, got:\n%s", out)
|
||||
}
|
||||
|
||||
// Every forward gets its own Host block, with the expected alias
|
||||
// (deduplicated per hostAliases), ProxyJump jump, and no local port.
|
||||
wantBlocks := []struct {
|
||||
alias string
|
||||
addr string
|
||||
port string
|
||||
}{
|
||||
{"db-prod-1-ssh", "10.0.0.5", "22"},
|
||||
{"db-prod-1-web-admin", "10.0.0.5", "8080"},
|
||||
{"cache-1", "10.0.0.9", "6379"},
|
||||
}
|
||||
for _, wb := range wantBlocks {
|
||||
if !strings.Contains(out, "Host "+wb.alias+"\n") {
|
||||
t.Errorf("output missing %q Host block, got:\n%s", wb.alias, out)
|
||||
}
|
||||
}
|
||||
if !strings.Contains(out, " ProxyJump jump\n") {
|
||||
t.Errorf("output missing ProxyJump jump directive, got:\n%s", out)
|
||||
}
|
||||
proxyJumpCount := strings.Count(out, "ProxyJump jump")
|
||||
if proxyJumpCount != len(forwards) {
|
||||
t.Errorf("expected %d ProxyJump jump directives (one per forward), got %d", len(forwards), proxyJumpCount)
|
||||
}
|
||||
identitiesOnlyCount := strings.Count(out, "IdentitiesOnly yes")
|
||||
// One for the "jump" block plus one per forward block.
|
||||
if identitiesOnlyCount != len(forwards)+1 {
|
||||
t.Errorf("expected %d IdentitiesOnly yes lines, got %d", len(forwards)+1, identitiesOnlyCount)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenderClientConfigProxyJump_NoForwards(t *testing.T) {
|
||||
out := RenderClientConfigProxyJump("bastion.example.com", 22, "nobody", "nobody_jump_key", nil)
|
||||
|
||||
if !strings.Contains(out, "Host jump\n") {
|
||||
t.Errorf("expected jump block even with no forwards, got:\n%s", out)
|
||||
}
|
||||
if strings.Contains(out, "ProxyJump jump") {
|
||||
t.Errorf("no forwards means no per-service blocks, but found ProxyJump jump in:\n%s", out)
|
||||
}
|
||||
}
|
||||
|
||||
// TestRenderClientConfig_LocalForwardModeUnaffected pins down that the
|
||||
// pre-existing LocalForward-mode renderer still behaves as before adding
|
||||
// ProxyJump mode alongside it (regression guard).
|
||||
func TestRenderClientConfig_LocalForwardModeUnaffected(t *testing.T) {
|
||||
forwards := []AllocatedForward{
|
||||
{Forward: model.Forward{HostName: "db-prod-1", Address: "10.0.0.5", Port: 22, ServiceName: "ssh", Description: "primary db"}, LocalPort: 20000},
|
||||
{Forward: model.Forward{HostName: "cache-1", Address: "10.0.0.9", Port: 6379, ServiceName: "redis"}, LocalPort: 20001},
|
||||
}
|
||||
|
||||
out := RenderClientConfig("bastion.example.com", 2222, "a-makarov", "a-makarov_jump_key", forwards)
|
||||
|
||||
if strings.Contains(out, "ProxyJump") {
|
||||
t.Errorf("LocalForward-mode output must not contain ProxyJump, got:\n%s", out)
|
||||
}
|
||||
if !strings.Contains(out, "Host jump\n") {
|
||||
t.Errorf("expected single jump block, got:\n%s", out)
|
||||
}
|
||||
if strings.Contains(out, "Host db-prod-1\n") || strings.Contains(out, "Host cache-1\n") {
|
||||
t.Errorf("LocalForward mode must not emit per-service Host blocks, got:\n%s", out)
|
||||
}
|
||||
wantLines := []string{
|
||||
" LocalForward 20000 10.0.0.5:22 # db-prod-1/ssh: primary db\n",
|
||||
" LocalForward 20001 10.0.0.9:6379 # cache-1/redis\n",
|
||||
}
|
||||
for _, want := range wantLines {
|
||||
if !strings.Contains(out, want) {
|
||||
t.Errorf("missing expected LocalForward line %q, got:\n%s", want, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user