v2.31.1 f37fe67

traffic_test.go RawHistoryFolder

1package app
2
3import (
4	"context"
5	"net"
6	"testing"
7
8	"github.com/stretchr/testify/assert"
9	"github.com/stretchr/testify/require"
10
11	trafficadapter "github.com/bnema/gordon/internal/adapters/in/traffic"
12	inmocks "github.com/bnema/gordon/internal/boundaries/in/mocks"
13	"github.com/bnema/gordon/internal/domain"
14	servicecfg "github.com/bnema/gordon/internal/usecase/services"
15	"github.com/bnema/gordon/internal/usecase/traffic"
16)
17
18func TestTrafficRuntimeGraphKeepsSmartTCPHTTPAndTLSPassthroughRouters(t *testing.T) {
19	graph := domain.TrafficGraph{
20		EntryPoints: []domain.EntryPoint{
21			{Name: traffic.DefaultEdgeEntryPointName, Address: "127.0.0.1:8443", Protocol: domain.EntryPointProtocolSmartTCP, RawFallback: "ssh", RawFallbackTrustedCIDRs: []string{"127.0.0.0/8"}},
22		},
23		Routers: []domain.TrafficRouter{
24			{Name: "app", EntryPoint: traffic.DefaultEdgeEntryPointName, Protocol: domain.RouterProtocolHTTP, Rule: domain.TrafficRule{Host: "app.example.com"}, Service: "route:app.example.com"},
25			{Name: "tls-raw", EntryPoint: traffic.DefaultEdgeEntryPointName, Protocol: domain.RouterProtocolTLSPassthrough, Rule: domain.TrafficRule{SNI: "raw.example.com"}, Service: "network_service:raw:tls"},
26			{Name: "ssh", EntryPoint: traffic.DefaultEdgeEntryPointName, Protocol: domain.RouterProtocolTCP, Service: "network_service:ssh:ssh"},
27		},
28		Services: []domain.TrafficService{
29			{Name: "route:app.example.com"},
30			{Name: "network_service:raw:tls", Backends: []domain.TrafficBackend{{Name: "raw:tls", Host: "127.0.0.1", Port: 443, Protocol: domain.NetworkProtocolTCP}}},
31			{Name: "network_service:ssh:ssh", Backends: []domain.TrafficBackend{{Name: "ssh:ssh", Host: "127.0.0.1", Port: 22, Protocol: domain.NetworkProtocolTCP}}},
32		},
33	}
34
35	filtered, err := trafficRuntimeGraph(graph)
36	require.NoError(t, err)
37	assert.ElementsMatch(t, []string{traffic.DefaultEdgeEntryPointName}, entryPointNames(filtered.EntryPoints))
38	assert.ElementsMatch(t, []string{"app", "tls-raw", "ssh"}, routerNames(filtered.Routers))
39	assert.ElementsMatch(t, []string{"route:app.example.com", "network_service:raw:tls", "network_service:ssh:ssh"}, serviceNames(filtered.Services))
40}
41
42func TestTrafficRuntimeGraphHTTPEntrypointIsInvalidRatherThanSilentlyFiltered(t *testing.T) {
43	graph := domain.TrafficGraph{
44		EntryPoints: []domain.EntryPoint{{Name: "edge", Address: "127.0.0.1:8443", Protocol: domain.EntryPointProtocolHTTP}},
45		Routers:     []domain.TrafficRouter{{Name: "app", EntryPoint: "edge", Protocol: domain.RouterProtocolHTTP, Service: "route:app.example.com"}},
46		Services:    []domain.TrafficService{{Name: "route:app.example.com"}},
47	}
48
49	require.ErrorContains(t, graph.Validate(), "unsupported traffic entrypoint protocol \"http\"")
50}
51
52func TestTrafficRuntimeGraphKeepsDefaultTLSEntrypoint(t *testing.T) {
53	graph := domain.TrafficGraph{
54		EntryPoints: []domain.EntryPoint{
55			{Name: "websecure", Address: "127.0.0.1:8443", Protocol: domain.EntryPointProtocolTLSMux},
56			{Name: "postgres", Address: "127.0.0.1:15432", Protocol: domain.EntryPointProtocolTCP},
57		},
58		Routers: []domain.TrafficRouter{
59			{Name: "app-secure", EntryPoint: "websecure", Protocol: domain.RouterProtocolHTTP, Rule: domain.TrafficRule{Host: "app.example.com"}, Service: "route:app.example.com"},
60			{Name: "postgres", EntryPoint: "postgres", Protocol: domain.RouterProtocolTCP, Service: "network_service:postgres:db"},
61		},
62		Services: []domain.TrafficService{
63			{Name: "route:app.example.com"},
64			{Name: "network_service:postgres:db", Backends: []domain.TrafficBackend{{Name: "db", Host: "127.0.0.1", Port: 5432, Protocol: domain.NetworkProtocolTCP}}},
65		},
66	}
67
68	filtered, err := trafficRuntimeGraph(graph)
69	require.NoError(t, err)
70	assert.ElementsMatch(t, []string{"websecure", "postgres"}, entryPointNames(filtered.EntryPoints))
71	assert.ElementsMatch(t, []string{"app-secure", "postgres"}, routerNames(filtered.Routers))
72	assert.ElementsMatch(t, []string{"route:app.example.com", "network_service:postgres:db"}, serviceNames(filtered.Services))
73}
74
75func TestTrafficRuntimeGraphAllowsTLSPassthroughOnDefaultTLS(t *testing.T) {
76	graph := domain.TrafficGraph{
77		EntryPoints: []domain.EntryPoint{{Name: "websecure", Address: "127.0.0.1:8443", Protocol: domain.EntryPointProtocolTLSMux}},
78		Routers:     []domain.TrafficRouter{{Name: "raw", EntryPoint: "websecure", Protocol: domain.RouterProtocolTLSPassthrough, Rule: domain.TrafficRule{SNI: "raw.example.com"}, Service: "network_service:raw:tls"}},
79		Services:    []domain.TrafficService{{Name: "network_service:raw:tls", Backends: []domain.TrafficBackend{{Name: "raw:tls", Host: "127.0.0.1", Port: 443, Protocol: domain.NetworkProtocolTCP}}}},
80	}
81
82	filtered, err := trafficRuntimeGraph(graph)
83	require.NoError(t, err)
84	require.Len(t, filtered.EntryPoints, 1)
85	assert.Equal(t, "websecure", filtered.EntryPoints[0].Name)
86	require.Len(t, filtered.Routers, 1)
87	assert.Equal(t, "raw", filtered.Routers[0].Name)
88}
89
90func TestApplyTrafficRuntimeConfigAppliesSmartTCPEntrypointWithRawFallbackPolicy(t *testing.T) {
91	manager := trafficadapter.NewManager()
92	defer func() { require.NoError(t, manager.Shutdown(context.Background())) }()
93	cfg := Config{
94		EntryPoints: map[string]traffic.EntryPointConfig{
95			traffic.DefaultEdgeEntryPointName: {
96				Address:                 freeTCPAddress(t),
97				Protocol:                domain.EntryPointProtocolSmartTCP,
98				RawFallback:             "ssh",
99				RawFallbackTrustedCIDRs: []string{"127.0.0.0/8"},
100			},
101		},
102		NetworkServices: []traffic.NetworkServiceConfig{{
103			Name:  "ssh",
104			Ports: []traffic.PortConfig{{Name: "ssh", Container: 22, Protocol: domain.NetworkProtocolTCP}},
105		}},
106	}
107	cfg.Traffic.TCP.Routers = []traffic.RouterConfig{{Name: "ssh", EntryPoint: traffic.DefaultEdgeEntryPointName, Service: "network_service:ssh:ssh"}}
108
109	configSvc := inmocks.NewMockConfigService(t)
110	configSvc.EXPECT().GetRoutes(context.Background()).Return([]domain.Route{{Domain: "app.example.com", HTTPS: true}})
111	configSvc.EXPECT().GetExternalRoutes().Return(nil)
112
113	require.NoError(t, applyTrafficRuntimeConfig(context.Background(), manager, cfg, configSvc))
114	status := manager.Status()
115	require.Len(t, status.EntryPoints, 1)
116	assert.Equal(t, traffic.DefaultEdgeEntryPointName, status.EntryPoints[0].Name)
117	assert.True(t, status.EntryPoints[0].Active)
118	assert.Equal(t, domain.EntryPointProtocolSmartTCP, status.EntryPoints[0].Protocol)
119}
120
121func TestApplyTrafficRuntimeConfigPassesStandaloneServicesToBuilder(t *testing.T) {
122	manager := trafficadapter.NewManager()
123	defer func() { require.NoError(t, manager.Shutdown(context.Background())) }()
124	cfg := Config{
125		EntryPoints: map[string]traffic.EntryPointConfig{
126			"rust": {Address: freeUDPAddress(t), Protocol: domain.EntryPointProtocolUDP},
127		},
128		Services: []servicecfg.Config{{
129			Name:    "rust",
130			Image:   "localhost/rust:latest",
131			Enabled: true,
132			Ports:   []servicecfg.PortConfig{{Name: "game", Container: 28015, Protocol: domain.NetworkProtocolUDP, Publish: "127.0.0.1:38015"}},
133		}},
134	}
135	cfg.Traffic.UDP.Routers = []traffic.RouterConfig{{Name: "rust-game", EntryPoint: "rust", Service: "service:rust:game"}}
136
137	configSvc := inmocks.NewMockConfigService(t)
138	configSvc.EXPECT().GetRoutes(context.Background()).Return(nil)
139	configSvc.EXPECT().GetExternalRoutes().Return(nil)
140
141	require.NoError(t, applyTrafficRuntimeConfig(context.Background(), manager, cfg, configSvc))
142	status := manager.Status()
143	require.Len(t, status.EntryPoints, 1)
144	assert.Equal(t, "rust", status.EntryPoints[0].Name)
145	assert.True(t, status.EntryPoints[0].Active)
146}
147
148func TestApplyTrafficRuntimeConfigAppliesCustomL4Entrypoint(t *testing.T) {
149	manager := trafficadapter.NewManager()
150	defer func() { require.NoError(t, manager.Shutdown(context.Background())) }()
151	cfg := Config{
152		EntryPoints: map[string]traffic.EntryPointConfig{
153			"postgres": {Address: freeTCPAddress(t), Protocol: domain.EntryPointProtocolTCP},
154		},
155		NetworkServices: []traffic.NetworkServiceConfig{{
156			Name:  "postgres",
157			Ports: []traffic.PortConfig{{Name: "db", Container: 5432, Protocol: domain.NetworkProtocolTCP}},
158		}},
159	}
160	cfg.Traffic.TCP.Routers = []traffic.RouterConfig{{Name: "postgres", EntryPoint: "postgres", Service: "network_service:postgres:db"}}
161
162	configSvc := inmocks.NewMockConfigService(t)
163	configSvc.EXPECT().GetRoutes(context.Background()).Return(nil)
164	configSvc.EXPECT().GetExternalRoutes().Return(nil)
165
166	require.NoError(t, applyTrafficRuntimeConfig(context.Background(), manager, cfg, configSvc))
167	status := manager.Status()
168	require.Len(t, status.EntryPoints, 1)
169	assert.Equal(t, "postgres", status.EntryPoints[0].Name)
170	assert.True(t, status.EntryPoints[0].Active)
171}
172
173func entryPointNames(entries []domain.EntryPoint) []string {
174	names := make([]string, 0, len(entries))
175	for _, entry := range entries {
176		names = append(names, entry.Name)
177	}
178	return names
179}
180
181func routerNames(routers []domain.TrafficRouter) []string {
182	names := make([]string, 0, len(routers))
183	for _, router := range routers {
184		names = append(names, router.Name)
185	}
186	return names
187}
188
189func serviceNames(services []domain.TrafficService) []string {
190	names := make([]string, 0, len(services))
191	for _, service := range services {
192		names = append(names, service.Name)
193	}
194	return names
195}
196
197func freeTCPAddress(t *testing.T) string {
198	t.Helper()
199	listener, err := net.Listen("tcp", "127.0.0.1:0")
200	require.NoError(t, err)
201	address := listener.Addr().String()
202	require.NoError(t, listener.Close())
203	return address
204}
205
206func freeUDPAddress(t *testing.T) string {
207	t.Helper()
208	packetConn, err := net.ListenPacket("udp", "127.0.0.1:0")
209	require.NoError(t, err)
210	address := packetConn.LocalAddr().String()
211	require.NoError(t, packetConn.Close())
212	return address
213}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close