diff --git a/api/utils/route.go b/api/utils/route.go index 1ca83926e4ee1..af9311758e1ea 100644 --- a/api/utils/route.go +++ b/api/utils/route.go @@ -180,6 +180,14 @@ func (m *SSHRouteMatcher) RouteToServerScore(server RouteableServer) (score int) score = max(score, matchAddr(addr)) } + // Allow a match on name, even though it may not be a UUID or EC2 ID, + // to support agentless hosts that were created without their name being a UUID. + // The score however, is lower than a true direct match, to prevent any + // breaking changes to routing. + if server.GetName() == m.cfg.Host { + score = max(score, indirectMatch) + } + return score } diff --git a/api/utils/route_test.go b/api/utils/route_test.go index ce971e04e6ea3..2ab21fd3df79c 100644 --- a/api/utils/route_test.go +++ b/api/utils/route_test.go @@ -15,6 +15,7 @@ package utils import ( + "cmp" "context" "testing" @@ -273,6 +274,7 @@ func TestSSHRouteMatcherScoring(t *testing.T) { tts := []struct { desc string hostname string + name string addrs []string score int }{ @@ -309,12 +311,23 @@ func TestSSHRouteMatcherScoring(t *testing.T) { }, score: notMatch, }, + { + desc: "non-uuid name", + name: "foo.example.com", + hostname: "foo.com", + addrs: []string{ + "0.0.0.0:0", + "1.1.1.1:0", + }, + score: indirectMatch, + }, } for _, tt := range tts { t.Run(tt.desc, func(t *testing.T) { + name := cmp.Or(tt.name, uuid.NewString()) score := matcher.RouteToServerScore(mockRouteableServer{ - name: uuid.NewString(), + name: name, hostname: tt.hostname, publicAddr: tt.addrs, }) diff --git a/lib/proxy/router_test.go b/lib/proxy/router_test.go index f846151aec14b..4dd3609869453 100644 --- a/lib/proxy/router_test.go +++ b/lib/proxy/router_test.go @@ -133,6 +133,11 @@ func TestRouteScoring(t *testing.T) { hostname: "blue.example.com", addr: "2.3.4.5:22", }, + { + name: "not-a-uuid", + hostname: "test.example.com", + addr: "3.4.5.6:22", + }, }) // scoring behavior is independent of routing strategy so we just @@ -205,6 +210,11 @@ func TestRouteScoring(t *testing.T) { host: "red.example.com", expect: "blue.example.com", }, + { + desc: "non-uuid name", + host: "not-a-uuid", + expect: "test.example.com", + }, } for _, tt := range tts { @@ -326,6 +336,21 @@ func TestGetServers(t *testing.T) { }, }) + servers = append(servers, + &types.ServerV2{ + Kind: types.KindNode, + SubKind: types.SubKindOpenSSHNode, + Version: types.V2, + Metadata: types.Metadata{ + Name: "agentless-node-1", + }, + Spec: types.ServerSpecV2{ + Addr: "1.2.3.4:22", + Hostname: "agentless-1", + }, + }, + ) + // ensure tests don't have order-dependence rand.Shuffle(len(servers), func(i, j int) { servers[i], servers[j] = servers[j], servers[i] @@ -432,15 +457,6 @@ func TestGetServers(t *testing.T) { require.Equal(t, "alpaca", srv.GetHostname()) }, }, - { - name: "failure on invalid addresses", - site: testSite{cfg: &unambiguousCfg, nodes: servers}, - host: "lion", - errAssertion: require.NoError, - serverAssertion: func(t *testing.T, srv types.Server) { - require.Empty(t, srv) - }, - }, { name: "case-insensitive match", site: testSite{cfg: &unambiguousInsensitiveCfg, nodes: servers}, @@ -462,6 +478,17 @@ func TestGetServers(t *testing.T) { require.Empty(t, srv) }, }, + { + name: "agentless match by non-uuid name", + site: testSite{cfg: &unambiguousCfg, nodes: servers}, + host: "agentless-node-1", + errAssertion: require.NoError, + serverAssertion: func(t *testing.T, srv types.Server) { + require.NotNil(t, srv) + require.Equal(t, "agentless-1", srv.GetHostname()) + require.True(t, srv.IsOpenSSHNode()) + }, + }, } ctx := context.Background() @@ -627,7 +654,7 @@ func TestRouter_DialHost(t *testing.T) { SubKind: types.SubKindOpenSSHNode, Version: types.V2, Metadata: types.Metadata{ - Name: uuid.NewString(), + Name: "agentless", }, Spec: types.ServerSpecV2{ Addr: "127.0.0.1:9001",