fix(traffic): use boundary mocks and context logging
- commit
- 1955dc9c4e2fcb19a7e69c9fb590c66a5b441ccd
- author
- brice · · 2026-06-30 02:22 UTC
- parent
- e891c6e
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
14 files · +219 -71
M .mockery.yaml +1 -0
| @@ -45,6 +45,7 @@ packages: | |||
| 45 | 45 | LogService: | |
| 46 | 46 | VolumeService: | |
| 47 | 47 | PublicTLSService: | |
| 48 | + | TrafficStatusService: | |
| 48 | 49 | # Exception: pushImageOps is a CLI-local interface, not a boundary port. | |
| 49 | 50 | # Mocked here because it abstracts Docker SDK calls that require a running | |
| 50 | 51 | # daemon, making unit/integration tests impractical without a test double. | |
| @@ -45,6 +45,7 @@ packages: | |||||
| 45 | LogService: | 45 | LogService: | ||
| 46 | VolumeService: | 46 | VolumeService: | ||
| 47 | PublicTLSService: | 47 | PublicTLSService: | ||
| 48 | + | TrafficStatusService: | |||
| 48 | # Exception: pushImageOps is a CLI-local interface, not a boundary port. | 49 | # Exception: pushImageOps is a CLI-local interface, not a boundary port. | ||
| 49 | # Mocked here because it abstracts Docker SDK calls that require a running | 50 | # Mocked here because it abstracts Docker SDK calls that require a running | ||
| 50 | # daemon, making unit/integration tests impractical without a test double. | 51 | # daemon, making unit/integration tests impractical without a test double. | ||
M internal/adapters/in/http/admin/handler.go +2 -6
| @@ -38,10 +38,6 @@ type reloadTrigger interface { | |||
| 38 | 38 | Trigger(ctx context.Context) error | |
| 39 | 39 | } | |
| 40 | 40 | ||
| 41 | - | type trafficStatusService interface { | |
| 42 | - | Status() domain.TrafficStatus | |
| 43 | - | } | |
| 44 | - | ||
| 45 | 41 | // Handler implements the HTTP handler for the admin API. | |
| 46 | 42 | type Handler struct { | |
| 47 | 43 | configSvc in.ConfigService | |
| @@ -58,7 +54,7 @@ type Handler struct { | |||
| 58 | 54 | previewSvc previewService | |
| 59 | 55 | reloadTrigger reloadTrigger | |
| 60 | 56 | publicTLSSvc in.PublicTLSService | |
| 61 | - | trafficSvc trafficStatusService | |
| 57 | + | trafficSvc in.TrafficStatusService | |
| 62 | 58 | log zerowrap.Logger | |
| 63 | 59 | } | |
| 64 | 60 | ||
| @@ -207,7 +203,7 @@ type HandlerDeps struct { | |||
| 207 | 203 | VolumeSvc in.VolumeService | |
| 208 | 204 | ReloadTrigger reloadTrigger | |
| 209 | 205 | PublicTLSSvc in.PublicTLSService | |
| 210 | - | TrafficSvc trafficStatusService | |
| 206 | + | TrafficSvc in.TrafficStatusService | |
| 211 | 207 | } | |
| 212 | 208 | ||
| 213 | 209 | // NewHandler creates a new admin HTTP handler. | |
| @@ -38,10 +38,6 @@ type reloadTrigger interface { | |||||
| 38 | Trigger(ctx context.Context) error | 38 | Trigger(ctx context.Context) error | ||
| 39 | } | 39 | } | ||
| 40 | 40 | ||||
| 41 | - | type trafficStatusService interface { | |||
| 42 | - | Status() domain.TrafficStatus | |||
| 43 | - | } | |||
| 44 | - | ||||
| 45 | // Handler implements the HTTP handler for the admin API. | 41 | // Handler implements the HTTP handler for the admin API. | ||
| 46 | type Handler struct { | 42 | type Handler struct { | ||
| 47 | configSvc in.ConfigService | 43 | configSvc in.ConfigService | ||
| @@ -58,7 +54,7 @@ type Handler struct { | |||||
| 58 | previewSvc previewService | 54 | previewSvc previewService | ||
| 59 | reloadTrigger reloadTrigger | 55 | reloadTrigger reloadTrigger | ||
| 60 | publicTLSSvc in.PublicTLSService | 56 | publicTLSSvc in.PublicTLSService | ||
| 61 | - | trafficSvc trafficStatusService | 57 | + | trafficSvc in.TrafficStatusService |
| 62 | log zerowrap.Logger | 58 | log zerowrap.Logger | ||
| 63 | } | 59 | } | ||
| 64 | 60 | ||||
| @@ -207,7 +203,7 @@ type HandlerDeps struct { | |||||
| 207 | VolumeSvc in.VolumeService | 203 | VolumeSvc in.VolumeService | ||
| 208 | ReloadTrigger reloadTrigger | 204 | ReloadTrigger reloadTrigger | ||
| 209 | PublicTLSSvc in.PublicTLSService | 205 | PublicTLSSvc in.PublicTLSService | ||
| 210 | - | TrafficSvc trafficStatusService | 206 | + | TrafficSvc in.TrafficStatusService |
| 211 | } | 207 | } | ||
| 212 | 208 | ||||
| 213 | // NewHandler creates a new admin HTTP handler. | 209 | // NewHandler creates a new admin HTTP handler. | ||
M internal/adapters/in/http/admin/handler_traffic_test.go +13 -13
| @@ -9,23 +9,22 @@ import ( | |||
| 9 | 9 | "github.com/stretchr/testify/require" | |
| 10 | 10 | ||
| 11 | 11 | "github.com/bnema/gordon/internal/adapters/dto" | |
| 12 | + | inmocks "github.com/bnema/gordon/internal/boundaries/in/mocks" | |
| 12 | 13 | "github.com/bnema/gordon/internal/domain" | |
| 13 | 14 | ) | |
| 14 | 15 | ||
| 15 | - | type fakeTrafficStatusService struct{ status domain.TrafficStatus } | |
| 16 | - | ||
| 17 | - | func (f fakeTrafficStatusService) Status() domain.TrafficStatus { return f.status } | |
| 18 | - | ||
| 19 | 16 | func TestHandler_TrafficStatus(t *testing.T) { | |
| 17 | + | trafficSvc := inmocks.NewMockTrafficStatusService(t) | |
| 18 | + | trafficSvc.EXPECT().Status().Return(domain.TrafficStatus{ | |
| 19 | + | LastReloadStatus: "ok", | |
| 20 | + | EntryPoints: []domain.EntryPointStatus{{ | |
| 21 | + | Name: "postgres", Address: "127.0.0.1:5432", Protocol: domain.EntryPointProtocolTCP, | |
| 22 | + | Active: true, ActiveTCPConnections: 2, TotalAccepted: 3, BytesIn: 11, BytesOut: 22, | |
| 23 | + | }}, | |
| 24 | + | Counters: domain.TrafficCounters{ActiveTCPConnections: 2, TotalAccepted: 3, BytesIn: 11, BytesOut: 22}, | |
| 25 | + | }) | |
| 20 | 26 | handler := newTestHandler(t, func(d *HandlerDeps) { | |
| 21 | - | d.TrafficSvc = fakeTrafficStatusService{status: domain.TrafficStatus{ | |
| 22 | - | LastReloadStatus: "ok", | |
| 23 | - | EntryPoints: []domain.EntryPointStatus{{ | |
| 24 | - | Name: "postgres", Address: "127.0.0.1:5432", Protocol: domain.EntryPointProtocolTCP, | |
| 25 | - | Active: true, ActiveTCPConnections: 2, TotalAccepted: 3, BytesIn: 11, BytesOut: 22, | |
| 26 | - | }}, | |
| 27 | - | Counters: domain.TrafficCounters{ActiveTCPConnections: 2, TotalAccepted: 3, BytesIn: 11, BytesOut: 22}, | |
| 28 | - | }} | |
| 27 | + | d.TrafficSvc = trafficSvc | |
| 29 | 28 | }) | |
| 30 | 29 | server := newScopedTestServer(t, handler, "admin:status:read") | |
| 31 | 30 | ||
| @@ -44,7 +43,8 @@ func TestHandler_TrafficStatus(t *testing.T) { | |||
| 44 | 43 | } | |
| 45 | 44 | ||
| 46 | 45 | func TestHandler_TrafficStatusRequiresStatusRead(t *testing.T) { | |
| 47 | - | handler := newTestHandler(t, func(d *HandlerDeps) { d.TrafficSvc = fakeTrafficStatusService{} }) | |
| 46 | + | trafficSvc := inmocks.NewMockTrafficStatusService(t) | |
| 47 | + | handler := newTestHandler(t, func(d *HandlerDeps) { d.TrafficSvc = trafficSvc }) | |
| 48 | 48 | server := newScopedTestServer(t, handler, "admin:config:read") | |
| 49 | 49 | ||
| 50 | 50 | resp, err := http.Get(server.URL + "/admin/traffic/status") | |
| @@ -9,23 +9,22 @@ import ( | |||||
| 9 | "github.com/stretchr/testify/require" | 9 | "github.com/stretchr/testify/require" | ||
| 10 | 10 | ||||
| 11 | "github.com/bnema/gordon/internal/adapters/dto" | 11 | "github.com/bnema/gordon/internal/adapters/dto" | ||
| 12 | + | inmocks "github.com/bnema/gordon/internal/boundaries/in/mocks" | |||
| 12 | "github.com/bnema/gordon/internal/domain" | 13 | "github.com/bnema/gordon/internal/domain" | ||
| 13 | ) | 14 | ) | ||
| 14 | 15 | ||||
| 15 | - | type fakeTrafficStatusService struct{ status domain.TrafficStatus } | |||
| 16 | - | ||||
| 17 | - | func (f fakeTrafficStatusService) Status() domain.TrafficStatus { return f.status } | |||
| 18 | - | ||||
| 19 | func TestHandler_TrafficStatus(t *testing.T) { | 16 | func TestHandler_TrafficStatus(t *testing.T) { | ||
| 17 | + | trafficSvc := inmocks.NewMockTrafficStatusService(t) | |||
| 18 | + | trafficSvc.EXPECT().Status().Return(domain.TrafficStatus{ | |||
| 19 | + | LastReloadStatus: "ok", | |||
| 20 | + | EntryPoints: []domain.EntryPointStatus{{ | |||
| 21 | + | Name: "postgres", Address: "127.0.0.1:5432", Protocol: domain.EntryPointProtocolTCP, | |||
| 22 | + | Active: true, ActiveTCPConnections: 2, TotalAccepted: 3, BytesIn: 11, BytesOut: 22, | |||
| 23 | + | }}, | |||
| 24 | + | Counters: domain.TrafficCounters{ActiveTCPConnections: 2, TotalAccepted: 3, BytesIn: 11, BytesOut: 22}, | |||
| 25 | + | }) | |||
| 20 | handler := newTestHandler(t, func(d *HandlerDeps) { | 26 | handler := newTestHandler(t, func(d *HandlerDeps) { | ||
| 21 | - | d.TrafficSvc = fakeTrafficStatusService{status: domain.TrafficStatus{ | 27 | + | d.TrafficSvc = trafficSvc |
| 22 | - | LastReloadStatus: "ok", | |||
| 23 | - | EntryPoints: []domain.EntryPointStatus{{ | |||
| 24 | - | Name: "postgres", Address: "127.0.0.1:5432", Protocol: domain.EntryPointProtocolTCP, | |||
| 25 | - | Active: true, ActiveTCPConnections: 2, TotalAccepted: 3, BytesIn: 11, BytesOut: 22, | |||
| 26 | - | }}, | |||
| 27 | - | Counters: domain.TrafficCounters{ActiveTCPConnections: 2, TotalAccepted: 3, BytesIn: 11, BytesOut: 22}, | |||
| 28 | - | }} | |||
| 29 | }) | 28 | }) | ||
| 30 | server := newScopedTestServer(t, handler, "admin:status:read") | 29 | server := newScopedTestServer(t, handler, "admin:status:read") | ||
| 31 | 30 | ||||
| @@ -44,7 +43,8 @@ func TestHandler_TrafficStatus(t *testing.T) { | |||||
| 44 | } | 43 | } | ||
| 45 | 44 | ||||
| 46 | func TestHandler_TrafficStatusRequiresStatusRead(t *testing.T) { | 45 | func TestHandler_TrafficStatusRequiresStatusRead(t *testing.T) { | ||
| 47 | - | handler := newTestHandler(t, func(d *HandlerDeps) { d.TrafficSvc = fakeTrafficStatusService{} }) | 46 | + | trafficSvc := inmocks.NewMockTrafficStatusService(t) |
| 47 | + | handler := newTestHandler(t, func(d *HandlerDeps) { d.TrafficSvc = trafficSvc }) | |||
| 48 | server := newScopedTestServer(t, handler, "admin:config:read") | 48 | server := newScopedTestServer(t, handler, "admin:config:read") | ||
| 49 | 49 | ||||
| 50 | resp, err := http.Get(server.URL + "/admin/traffic/status") | 50 | resp, err := http.Get(server.URL + "/admin/traffic/status") | ||
M internal/adapters/in/traffic/manager.go +65 -9
| @@ -12,6 +12,7 @@ import ( | |||
| 12 | 12 | "time" | |
| 13 | 13 | ||
| 14 | 14 | "github.com/bnema/zerowrap" | |
| 15 | + | "github.com/rs/zerolog" | |
| 15 | 16 | ||
| 16 | 17 | "github.com/bnema/gordon/internal/domain" | |
| 17 | 18 | ) | |
| @@ -21,10 +22,30 @@ const ( | |||
| 21 | 22 | reloadStatusError = "error" | |
| 22 | 23 | ) | |
| 23 | 24 | ||
| 25 | + | func trafficLog(ctx context.Context) zerowrap.Logger { | |
| 26 | + | return zerowrap.FromCtxWithFields(ctx, map[string]any{ | |
| 27 | + | zerowrap.FieldLayer: "adapter", | |
| 28 | + | zerowrap.FieldAdapter: "traffic", | |
| 29 | + | }) | |
| 30 | + | } | |
| 31 | + | ||
| 32 | + | func trafficInfo(ctx context.Context) *zerolog.Event { | |
| 33 | + | log := trafficLog(ctx) | |
| 34 | + | return log.Info() | |
| 35 | + | } | |
| 36 | + | ||
| 37 | + | func trafficDebug(ctx context.Context) *zerolog.Event { | |
| 38 | + | log := trafficLog(ctx) | |
| 39 | + | return log.Debug() | |
| 40 | + | } | |
| 41 | + | ||
| 42 | + | func trafficWarn(ctx context.Context) *zerolog.Event { | |
| 43 | + | log := trafficLog(ctx) | |
| 44 | + | return log.Warn() | |
| 45 | + | } | |
| 46 | + | ||
| 24 | 47 | // Manager owns runtime entrypoints for the traffic plane. | |
| 25 | 48 | type Manager struct { | |
| 26 | - | log zerowrap.Logger | |
| 27 | - | ||
| 28 | 49 | mu sync.Mutex | |
| 29 | 50 | snapshot atomic.Pointer[domain.TrafficGraph] | |
| 30 | 51 | listeners map[string]*entryPointRuntime | |
| @@ -36,9 +57,8 @@ type Manager struct { | |||
| 36 | 57 | } | |
| 37 | 58 | ||
| 38 | 59 | // NewManager creates a traffic manager. | |
| 39 | - | func NewManager(log zerowrap.Logger) *Manager { | |
| 60 | + | func NewManager() *Manager { | |
| 40 | 61 | manager := &Manager{ | |
| 41 | - | log: log, | |
| 42 | 62 | listeners: map[string]*entryPointRuntime{}, | |
| 43 | 63 | udpListeners: map[string]*udpEntryPointRuntime{}, | |
| 44 | 64 | lastReloadStatus: reloadStatusOK, | |
| @@ -50,11 +70,12 @@ func NewManager(log zerowrap.Logger) *Manager { | |||
| 50 | 70 | // Apply validates and applies a new traffic graph snapshot. | |
| 51 | 71 | func (m *Manager) Apply(ctx context.Context, graph *domain.TrafficGraph) error { | |
| 52 | 72 | if graph == nil { | |
| 53 | - | return m.recordReloadError(errors.New("traffic graph is required")) | |
| 73 | + | return m.recordReloadError(ctx, errors.New("traffic graph is required")) | |
| 54 | 74 | } | |
| 55 | 75 | if err := graph.Validate(); err != nil { | |
| 56 | - | return m.recordReloadError(err) | |
| 76 | + | return m.recordReloadError(ctx, err) | |
| 57 | 77 | } | |
| 78 | + | trafficDebug(ctx).Int("entrypoints", len(graph.EntryPoints)).Int("routers", len(graph.Routers)).Int("services", len(graph.Services)).Msg("applying traffic graph") | |
| 58 | 79 | ||
| 59 | 80 | nextGraph := cloneTrafficGraph(*graph) | |
| 60 | 81 | m.mu.Lock() | |
| @@ -64,6 +85,7 @@ func (m *Manager) Apply(ctx context.Context, graph *domain.TrafficGraph) error { | |||
| 64 | 85 | if err != nil { | |
| 65 | 86 | m.lastReloadStatus = reloadStatusError | |
| 66 | 87 | m.lastReloadError = err.Error() | |
| 88 | + | trafficWarn(ctx).Err(err).Msg("failed to prepare tcp traffic listeners") | |
| 67 | 89 | return err | |
| 68 | 90 | } | |
| 69 | 91 | newUDPListeners, createdUDPListeners, err := m.prepareUDPListeners(ctx, &nextGraph) | |
| @@ -73,6 +95,7 @@ func (m *Manager) Apply(ctx context.Context, graph *domain.TrafficGraph) error { | |||
| 73 | 95 | } | |
| 74 | 96 | m.lastReloadStatus = reloadStatusError | |
| 75 | 97 | m.lastReloadError = err.Error() | |
| 98 | + | trafficWarn(ctx).Err(err).Msg("failed to prepare udp traffic listeners") | |
| 76 | 99 | return err | |
| 77 | 100 | } | |
| 78 | 101 | ||
| @@ -91,15 +114,18 @@ func (m *Manager) Apply(ctx context.Context, graph *domain.TrafficGraph) error { | |||
| 91 | 114 | m.lastReloadError = "" | |
| 92 | 115 | ||
| 93 | 116 | tcpDrainTimeout := effectiveTCPOptions(snapshotTCPOptions(&nextGraph)).DrainTimeout | |
| 117 | + | stoppedTCP := 0 | |
| 94 | 118 | for name, runtime := range oldListeners { | |
| 95 | 119 | if newListeners[name] == runtime { | |
| 96 | 120 | continue | |
| 97 | 121 | } | |
| 98 | 122 | if runtime.shouldStopWith(newListeners[name]) { | |
| 123 | + | stoppedTCP++ | |
| 99 | 124 | runtime.stop(ctx, tcpDrainTimeout) | |
| 100 | 125 | } | |
| 101 | 126 | } | |
| 102 | 127 | udpDrainTimeout := effectiveUDPOptions(snapshotUDPOptions(&nextGraph)).DrainTimeout | |
| 128 | + | stoppedUDP := 0 | |
| 103 | 129 | for name, runtime := range oldUDPListeners { | |
| 104 | 130 | if newUDPListeners[name] == runtime { | |
| 105 | 131 | if _, ok := runtime.resolveUDPBackend(); !ok { | |
| @@ -107,13 +133,39 @@ func (m *Manager) Apply(ctx context.Context, graph *domain.TrafficGraph) error { | |||
| 107 | 133 | } | |
| 108 | 134 | continue | |
| 109 | 135 | } | |
| 136 | + | stoppedUDP++ | |
| 110 | 137 | runtime.stop(ctx, udpDrainTimeout) | |
| 111 | 138 | } | |
| 139 | + | logAppliedTrafficGraph(ctx, newListeners, newUDPListeners, createdListeners, createdUDPListeners, stoppedTCP, stoppedUDP) | |
| 112 | 140 | return nil | |
| 113 | 141 | } | |
| 114 | 142 | ||
| 115 | 143 | // Shutdown stops all listeners and active TCP streams. | |
| 144 | + | func logAppliedTrafficGraph( | |
| 145 | + | ctx context.Context, | |
| 146 | + | newListeners map[string]*entryPointRuntime, | |
| 147 | + | newUDPListeners map[string]*udpEntryPointRuntime, | |
| 148 | + | createdListeners []*entryPointRuntime, | |
| 149 | + | createdUDPListeners []*udpEntryPointRuntime, | |
| 150 | + | stoppedTCP int, | |
| 151 | + | stoppedUDP int, | |
| 152 | + | ) { | |
| 153 | + | logEvent := trafficInfo(ctx) | |
| 154 | + | if len(newListeners) == 0 && len(newUDPListeners) == 0 && stoppedTCP == 0 && stoppedUDP == 0 { | |
| 155 | + | logEvent = trafficDebug(ctx) | |
| 156 | + | } | |
| 157 | + | logEvent. | |
| 158 | + | Int("tcp_listeners", len(newListeners)). | |
| 159 | + | Int("udp_listeners", len(newUDPListeners)). | |
| 160 | + | Int("created_tcp_listeners", len(createdListeners)). | |
| 161 | + | Int("created_udp_listeners", len(createdUDPListeners)). | |
| 162 | + | Int("stopped_tcp_listeners", stoppedTCP). | |
| 163 | + | Int("stopped_udp_listeners", stoppedUDP). | |
| 164 | + | Msg("traffic graph applied") | |
| 165 | + | } | |
| 166 | + | ||
| 116 | 167 | func (m *Manager) Shutdown(ctx context.Context) error { | |
| 168 | + | trafficInfo(ctx).Msg("shutting down traffic manager") | |
| 117 | 169 | m.mu.Lock() | |
| 118 | 170 | defer m.mu.Unlock() | |
| 119 | 171 | ||
| @@ -136,6 +188,7 @@ func (m *Manager) Shutdown(ctx context.Context) error { | |||
| 136 | 188 | for _, runtime := range udpListeners { | |
| 137 | 189 | runtime.stop(ctx, udpDrainTimeout) | |
| 138 | 190 | } | |
| 191 | + | trafficInfo(ctx).Int("tcp_listeners", len(listeners)).Int("udp_listeners", len(udpListeners)).Msg("traffic manager shut down") | |
| 139 | 192 | return nil | |
| 140 | 193 | } | |
| 141 | 194 | ||
| @@ -165,11 +218,12 @@ func (m *Manager) Status() domain.TrafficStatus { | |||
| 165 | 218 | return status | |
| 166 | 219 | } | |
| 167 | 220 | ||
| 168 | - | func (m *Manager) recordReloadError(err error) error { | |
| 221 | + | func (m *Manager) recordReloadError(ctx context.Context, err error) error { | |
| 169 | 222 | m.mu.Lock() | |
| 170 | 223 | m.lastReloadStatus = reloadStatusError | |
| 171 | 224 | m.lastReloadError = err.Error() | |
| 172 | 225 | m.mu.Unlock() | |
| 226 | + | trafficWarn(ctx).Err(err).Msg("traffic graph rejected") | |
| 173 | 227 | return err | |
| 174 | 228 | } | |
| 175 | 229 | ||
| @@ -211,7 +265,8 @@ func (m *Manager) bindTCPEntryPoint(ctx context.Context, entryPoint domain.Entry | |||
| 211 | 265 | if err != nil { | |
| 212 | 266 | return nil, fmt.Errorf("bind tcp entrypoint %q on %s: %w", entryPoint.Name, entryPoint.Address, err) | |
| 213 | 267 | } | |
| 214 | - | runtime := newEntryPointRuntime(m, entryPoint, listener) | |
| 268 | + | trafficInfo(ctx).Str("entrypoint", entryPoint.Name).Str("address", entryPoint.Address).Str("protocol", string(entryPoint.Protocol)).Msg("bound tcp traffic entrypoint") | |
| 269 | + | runtime := newEntryPointRuntime(ctx, m, entryPoint, listener) | |
| 215 | 270 | return runtime, nil | |
| 216 | 271 | } | |
| 217 | 272 | ||
| @@ -249,7 +304,8 @@ func (m *Manager) bindUDPEntryPoint(ctx context.Context, entryPoint domain.Entry | |||
| 249 | 304 | if err != nil { | |
| 250 | 305 | return nil, fmt.Errorf("bind udp entrypoint %q on %s: %w", entryPoint.Name, entryPoint.Address, err) | |
| 251 | 306 | } | |
| 252 | - | return newUDPEntryPointRuntime(m, entryPoint, packetConn), nil | |
| 307 | + | trafficInfo(ctx).Str("entrypoint", entryPoint.Name).Str("address", entryPoint.Address).Str("protocol", string(entryPoint.Protocol)).Msg("bound udp traffic entrypoint") | |
| 308 | + | return newUDPEntryPointRuntime(ctx, m, entryPoint, packetConn), nil | |
| 253 | 309 | } | |
| 254 | 310 | ||
| 255 | 311 | func entryPointStatuses(entries []domain.EntryPoint, listeners map[string]*entryPointRuntime, udpListeners map[string]*udpEntryPointRuntime) []domain.EntryPointStatus { | |
| @@ -12,6 +12,7 @@ import ( | |||||
| 12 | "time" | 12 | "time" | ||
| 13 | 13 | ||||
| 14 | "github.com/bnema/zerowrap" | 14 | "github.com/bnema/zerowrap" | ||
| 15 | + | "github.com/rs/zerolog" | |||
| 15 | 16 | ||||
| 16 | "github.com/bnema/gordon/internal/domain" | 17 | "github.com/bnema/gordon/internal/domain" | ||
| 17 | ) | 18 | ) | ||
| @@ -21,10 +22,30 @@ const ( | |||||
| 21 | reloadStatusError = "error" | 22 | reloadStatusError = "error" | ||
| 22 | ) | 23 | ) | ||
| 23 | 24 | ||||
| 25 | + | func trafficLog(ctx context.Context) zerowrap.Logger { | |||
| 26 | + | return zerowrap.FromCtxWithFields(ctx, map[string]any{ | |||
| 27 | + | zerowrap.FieldLayer: "adapter", | |||
| 28 | + | zerowrap.FieldAdapter: "traffic", | |||
| 29 | + | }) | |||
| 30 | + | } | |||
| 31 | + | ||||
| 32 | + | func trafficInfo(ctx context.Context) *zerolog.Event { | |||
| 33 | + | log := trafficLog(ctx) | |||
| 34 | + | return log.Info() | |||
| 35 | + | } | |||
| 36 | + | ||||
| 37 | + | func trafficDebug(ctx context.Context) *zerolog.Event { | |||
| 38 | + | log := trafficLog(ctx) | |||
| 39 | + | return log.Debug() | |||
| 40 | + | } | |||
| 41 | + | ||||
| 42 | + | func trafficWarn(ctx context.Context) *zerolog.Event { | |||
| 43 | + | log := trafficLog(ctx) | |||
| 44 | + | return log.Warn() | |||
| 45 | + | } | |||
| 46 | + | ||||
| 24 | // Manager owns runtime entrypoints for the traffic plane. | 47 | // Manager owns runtime entrypoints for the traffic plane. | ||
| 25 | type Manager struct { | 48 | type Manager struct { | ||
| 26 | - | log zerowrap.Logger | |||
| 27 | - | ||||
| 28 | mu sync.Mutex | 49 | mu sync.Mutex | ||
| 29 | snapshot atomic.Pointer[domain.TrafficGraph] | 50 | snapshot atomic.Pointer[domain.TrafficGraph] | ||
| 30 | listeners map[string]*entryPointRuntime | 51 | listeners map[string]*entryPointRuntime | ||
| @@ -36,9 +57,8 @@ type Manager struct { | |||||
| 36 | } | 57 | } | ||
| 37 | 58 | ||||
| 38 | // NewManager creates a traffic manager. | 59 | // NewManager creates a traffic manager. | ||
| 39 | - | func NewManager(log zerowrap.Logger) *Manager { | 60 | + | func NewManager() *Manager { |
| 40 | manager := &Manager{ | 61 | manager := &Manager{ | ||
| 41 | - | log: log, | |||
| 42 | listeners: map[string]*entryPointRuntime{}, | 62 | listeners: map[string]*entryPointRuntime{}, | ||
| 43 | udpListeners: map[string]*udpEntryPointRuntime{}, | 63 | udpListeners: map[string]*udpEntryPointRuntime{}, | ||
| 44 | lastReloadStatus: reloadStatusOK, | 64 | lastReloadStatus: reloadStatusOK, | ||
| @@ -50,11 +70,12 @@ func NewManager(log zerowrap.Logger) *Manager { | |||||
| 50 | // Apply validates and applies a new traffic graph snapshot. | 70 | // Apply validates and applies a new traffic graph snapshot. | ||
| 51 | func (m *Manager) Apply(ctx context.Context, graph *domain.TrafficGraph) error { | 71 | func (m *Manager) Apply(ctx context.Context, graph *domain.TrafficGraph) error { | ||
| 52 | if graph == nil { | 72 | if graph == nil { | ||
| 53 | - | return m.recordReloadError(errors.New("traffic graph is required")) | 73 | + | return m.recordReloadError(ctx, errors.New("traffic graph is required")) |
| 54 | } | 74 | } | ||
| 55 | if err := graph.Validate(); err != nil { | 75 | if err := graph.Validate(); err != nil { | ||
| 56 | - | return m.recordReloadError(err) | 76 | + | return m.recordReloadError(ctx, err) |
| 57 | } | 77 | } | ||
| 78 | + | trafficDebug(ctx).Int("entrypoints", len(graph.EntryPoints)).Int("routers", len(graph.Routers)).Int("services", len(graph.Services)).Msg("applying traffic graph") | |||
| 58 | 79 | ||||
| 59 | nextGraph := cloneTrafficGraph(*graph) | 80 | nextGraph := cloneTrafficGraph(*graph) | ||
| 60 | m.mu.Lock() | 81 | m.mu.Lock() | ||
| @@ -64,6 +85,7 @@ func (m *Manager) Apply(ctx context.Context, graph *domain.TrafficGraph) error { | |||||
| 64 | if err != nil { | 85 | if err != nil { | ||
| 65 | m.lastReloadStatus = reloadStatusError | 86 | m.lastReloadStatus = reloadStatusError | ||
| 66 | m.lastReloadError = err.Error() | 87 | m.lastReloadError = err.Error() | ||
| 88 | + | trafficWarn(ctx).Err(err).Msg("failed to prepare tcp traffic listeners") | |||
| 67 | return err | 89 | return err | ||
| 68 | } | 90 | } | ||
| 69 | newUDPListeners, createdUDPListeners, err := m.prepareUDPListeners(ctx, &nextGraph) | 91 | newUDPListeners, createdUDPListeners, err := m.prepareUDPListeners(ctx, &nextGraph) | ||
| @@ -73,6 +95,7 @@ func (m *Manager) Apply(ctx context.Context, graph *domain.TrafficGraph) error { | |||||
| 73 | } | 95 | } | ||
| 74 | m.lastReloadStatus = reloadStatusError | 96 | m.lastReloadStatus = reloadStatusError | ||
| 75 | m.lastReloadError = err.Error() | 97 | m.lastReloadError = err.Error() | ||
| 98 | + | trafficWarn(ctx).Err(err).Msg("failed to prepare udp traffic listeners") | |||
| 76 | return err | 99 | return err | ||
| 77 | } | 100 | } | ||
| 78 | 101 | ||||
| @@ -91,15 +114,18 @@ func (m *Manager) Apply(ctx context.Context, graph *domain.TrafficGraph) error { | |||||
| 91 | m.lastReloadError = "" | 114 | m.lastReloadError = "" | ||
| 92 | 115 | ||||
| 93 | tcpDrainTimeout := effectiveTCPOptions(snapshotTCPOptions(&nextGraph)).DrainTimeout | 116 | tcpDrainTimeout := effectiveTCPOptions(snapshotTCPOptions(&nextGraph)).DrainTimeout | ||
| 117 | + | stoppedTCP := 0 | |||
| 94 | for name, runtime := range oldListeners { | 118 | for name, runtime := range oldListeners { | ||
| 95 | if newListeners[name] == runtime { | 119 | if newListeners[name] == runtime { | ||
| 96 | continue | 120 | continue | ||
| 97 | } | 121 | } | ||
| 98 | if runtime.shouldStopWith(newListeners[name]) { | 122 | if runtime.shouldStopWith(newListeners[name]) { | ||
| 123 | + | stoppedTCP++ | |||
| 99 | runtime.stop(ctx, tcpDrainTimeout) | 124 | runtime.stop(ctx, tcpDrainTimeout) | ||
| 100 | } | 125 | } | ||
| 101 | } | 126 | } | ||
| 102 | udpDrainTimeout := effectiveUDPOptions(snapshotUDPOptions(&nextGraph)).DrainTimeout | 127 | udpDrainTimeout := effectiveUDPOptions(snapshotUDPOptions(&nextGraph)).DrainTimeout | ||
| 128 | + | stoppedUDP := 0 | |||
| 103 | for name, runtime := range oldUDPListeners { | 129 | for name, runtime := range oldUDPListeners { | ||
| 104 | if newUDPListeners[name] == runtime { | 130 | if newUDPListeners[name] == runtime { | ||
| 105 | if _, ok := runtime.resolveUDPBackend(); !ok { | 131 | if _, ok := runtime.resolveUDPBackend(); !ok { | ||
| @@ -107,13 +133,39 @@ func (m *Manager) Apply(ctx context.Context, graph *domain.TrafficGraph) error { | |||||
| 107 | } | 133 | } | ||
| 108 | continue | 134 | continue | ||
| 109 | } | 135 | } | ||
| 136 | + | stoppedUDP++ | |||
| 110 | runtime.stop(ctx, udpDrainTimeout) | 137 | runtime.stop(ctx, udpDrainTimeout) | ||
| 111 | } | 138 | } | ||
| 139 | + | logAppliedTrafficGraph(ctx, newListeners, newUDPListeners, createdListeners, createdUDPListeners, stoppedTCP, stoppedUDP) | |||
| 112 | return nil | 140 | return nil | ||
| 113 | } | 141 | } | ||
| 114 | 142 | ||||
| 115 | // Shutdown stops all listeners and active TCP streams. | 143 | // Shutdown stops all listeners and active TCP streams. | ||
| 144 | + | func logAppliedTrafficGraph( | |||
| 145 | + | ctx context.Context, | |||
| 146 | + | newListeners map[string]*entryPointRuntime, | |||
| 147 | + | newUDPListeners map[string]*udpEntryPointRuntime, | |||
| 148 | + | createdListeners []*entryPointRuntime, | |||
| 149 | + | createdUDPListeners []*udpEntryPointRuntime, | |||
| 150 | + | stoppedTCP int, | |||
| 151 | + | stoppedUDP int, | |||
| 152 | + | ) { | |||
| 153 | + | logEvent := trafficInfo(ctx) | |||
| 154 | + | if len(newListeners) == 0 && len(newUDPListeners) == 0 && stoppedTCP == 0 && stoppedUDP == 0 { | |||
| 155 | + | logEvent = trafficDebug(ctx) | |||
| 156 | + | } | |||
| 157 | + | logEvent. | |||
| 158 | + | Int("tcp_listeners", len(newListeners)). | |||
| 159 | + | Int("udp_listeners", len(newUDPListeners)). | |||
| 160 | + | Int("created_tcp_listeners", len(createdListeners)). | |||
| 161 | + | Int("created_udp_listeners", len(createdUDPListeners)). | |||
| 162 | + | Int("stopped_tcp_listeners", stoppedTCP). | |||
| 163 | + | Int("stopped_udp_listeners", stoppedUDP). | |||
| 164 | + | Msg("traffic graph applied") | |||
| 165 | + | } | |||
| 166 | + | ||||
| 116 | func (m *Manager) Shutdown(ctx context.Context) error { | 167 | func (m *Manager) Shutdown(ctx context.Context) error { | ||
| 168 | + | trafficInfo(ctx).Msg("shutting down traffic manager") | |||
| 117 | m.mu.Lock() | 169 | m.mu.Lock() | ||
| 118 | defer m.mu.Unlock() | 170 | defer m.mu.Unlock() | ||
| 119 | 171 | ||||
| @@ -136,6 +188,7 @@ func (m *Manager) Shutdown(ctx context.Context) error { | |||||
| 136 | for _, runtime := range udpListeners { | 188 | for _, runtime := range udpListeners { | ||
| 137 | runtime.stop(ctx, udpDrainTimeout) | 189 | runtime.stop(ctx, udpDrainTimeout) | ||
| 138 | } | 190 | } | ||
| 191 | + | trafficInfo(ctx).Int("tcp_listeners", len(listeners)).Int("udp_listeners", len(udpListeners)).Msg("traffic manager shut down") | |||
| 139 | return nil | 192 | return nil | ||
| 140 | } | 193 | } | ||
| 141 | 194 | ||||
| @@ -165,11 +218,12 @@ func (m *Manager) Status() domain.TrafficStatus { | |||||
| 165 | return status | 218 | return status | ||
| 166 | } | 219 | } | ||
| 167 | 220 | ||||
| 168 | - | func (m *Manager) recordReloadError(err error) error { | 221 | + | func (m *Manager) recordReloadError(ctx context.Context, err error) error { |
| 169 | m.mu.Lock() | 222 | m.mu.Lock() | ||
| 170 | m.lastReloadStatus = reloadStatusError | 223 | m.lastReloadStatus = reloadStatusError | ||
| 171 | m.lastReloadError = err.Error() | 224 | m.lastReloadError = err.Error() | ||
| 172 | m.mu.Unlock() | 225 | m.mu.Unlock() | ||
| 226 | + | trafficWarn(ctx).Err(err).Msg("traffic graph rejected") | |||
| 173 | return err | 227 | return err | ||
| 174 | } | 228 | } | ||
| 175 | 229 | ||||
| @@ -211,7 +265,8 @@ func (m *Manager) bindTCPEntryPoint(ctx context.Context, entryPoint domain.Entry | |||||
| 211 | if err != nil { | 265 | if err != nil { | ||
| 212 | return nil, fmt.Errorf("bind tcp entrypoint %q on %s: %w", entryPoint.Name, entryPoint.Address, err) | 266 | return nil, fmt.Errorf("bind tcp entrypoint %q on %s: %w", entryPoint.Name, entryPoint.Address, err) | ||
| 213 | } | 267 | } | ||
| 214 | - | runtime := newEntryPointRuntime(m, entryPoint, listener) | 268 | + | trafficInfo(ctx).Str("entrypoint", entryPoint.Name).Str("address", entryPoint.Address).Str("protocol", string(entryPoint.Protocol)).Msg("bound tcp traffic entrypoint") |
| 269 | + | runtime := newEntryPointRuntime(ctx, m, entryPoint, listener) | |||
| 215 | return runtime, nil | 270 | return runtime, nil | ||
| 216 | } | 271 | } | ||
| 217 | 272 | ||||
| @@ -249,7 +304,8 @@ func (m *Manager) bindUDPEntryPoint(ctx context.Context, entryPoint domain.Entry | |||||
| 249 | if err != nil { | 304 | if err != nil { | ||
| 250 | return nil, fmt.Errorf("bind udp entrypoint %q on %s: %w", entryPoint.Name, entryPoint.Address, err) | 305 | return nil, fmt.Errorf("bind udp entrypoint %q on %s: %w", entryPoint.Name, entryPoint.Address, err) | ||
| 251 | } | 306 | } | ||
| 252 | - | return newUDPEntryPointRuntime(m, entryPoint, packetConn), nil | 307 | + | trafficInfo(ctx).Str("entrypoint", entryPoint.Name).Str("address", entryPoint.Address).Str("protocol", string(entryPoint.Protocol)).Msg("bound udp traffic entrypoint") |
| 308 | + | return newUDPEntryPointRuntime(ctx, m, entryPoint, packetConn), nil | |||
| 253 | } | 309 | } | ||
| 254 | 310 | ||||
| 255 | func entryPointStatuses(entries []domain.EntryPoint, listeners map[string]*entryPointRuntime, udpListeners map[string]*udpEntryPointRuntime) []domain.EntryPointStatus { | 311 | func entryPointStatuses(entries []domain.EntryPoint, listeners map[string]*entryPointRuntime, udpListeners map[string]*udpEntryPointRuntime) []domain.EntryPointStatus { | ||
M internal/adapters/in/traffic/tcp.go +8 -2
| @@ -39,8 +39,8 @@ type trackedTCPConn struct { | |||
| 39 | 39 | backend net.Conn | |
| 40 | 40 | } | |
| 41 | 41 | ||
| 42 | - | func newEntryPointRuntime(manager *Manager, entryPoint domain.EntryPoint, listener net.Listener) *entryPointRuntime { | |
| 43 | - | ctx, cancel := context.WithCancel(context.Background()) | |
| 42 | + | func newEntryPointRuntime(parentCtx context.Context, manager *Manager, entryPoint domain.EntryPoint, listener net.Listener) *entryPointRuntime { | |
| 43 | + | ctx, cancel := context.WithCancel(context.WithoutCancel(parentCtx)) | |
| 44 | 44 | return &entryPointRuntime{ | |
| 45 | 45 | manager: manager, | |
| 46 | 46 | entryPoint: entryPoint, | |
| @@ -56,6 +56,7 @@ func (r *entryPointRuntime) start() { | |||
| 56 | 56 | if !r.started.CompareAndSwap(false, true) { | |
| 57 | 57 | return | |
| 58 | 58 | } | |
| 59 | + | trafficInfo(r.ctx).Str("entrypoint", r.entryPoint.Name).Str("address", r.entryPoint.Address).Str("protocol", string(r.entryPoint.Protocol)).Msg("started tcp traffic entrypoint") | |
| 59 | 60 | go r.acceptLoop() | |
| 60 | 61 | } | |
| 61 | 62 | ||
| @@ -71,6 +72,7 @@ func (r *entryPointRuntime) acceptLoop() { | |||
| 71 | 72 | if temporary, ok := err.(interface{ Temporary() bool }); ok && temporary.Temporary() { | |
| 72 | 73 | continue | |
| 73 | 74 | } | |
| 75 | + | trafficWarn(r.ctx).Err(err).Str("entrypoint", r.entryPoint.Name).Msg("tcp traffic accept loop stopped") | |
| 74 | 76 | return | |
| 75 | 77 | } | |
| 76 | 78 | r.activeWG.Add(1) | |
| @@ -346,6 +348,7 @@ func (r *entryPointRuntime) untrack(conn *trackedTCPConn) { | |||
| 346 | 348 | ||
| 347 | 349 | func (r *entryPointRuntime) stop(ctx context.Context, drainTimeout time.Duration) { | |
| 348 | 350 | if r.closed.CompareAndSwap(false, true) { | |
| 351 | + | trafficInfo(ctx).Str("entrypoint", r.entryPoint.Name).Str("address", r.entryPoint.Address).Msg("stopping tcp traffic entrypoint") | |
| 349 | 352 | r.cancel() | |
| 350 | 353 | _ = r.listener.Close() | |
| 351 | 354 | if !r.started.Load() { | |
| @@ -358,11 +361,14 @@ func (r *entryPointRuntime) stop(ctx context.Context, drainTimeout time.Duration | |||
| 358 | 361 | return | |
| 359 | 362 | } | |
| 360 | 363 | if r.waitActive(ctx, drainTimeout) { | |
| 364 | + | trafficInfo(ctx).Str("entrypoint", r.entryPoint.Name).Msg("stopped tcp traffic entrypoint") | |
| 361 | 365 | return | |
| 362 | 366 | } | |
| 367 | + | trafficDebug(ctx).Str("entrypoint", r.entryPoint.Name).Dur("drain_timeout", drainTimeout).Msg("forcing tcp traffic entrypoint drain") | |
| 363 | 368 | r.closeActiveConns() | |
| 364 | 369 | select { | |
| 365 | 370 | case <-r.activeDone(): | |
| 371 | + | trafficInfo(ctx).Str("entrypoint", r.entryPoint.Name).Msg("stopped tcp traffic entrypoint") | |
| 366 | 372 | case <-ctx.Done(): | |
| 367 | 373 | } | |
| 368 | 374 | } | |
| @@ -39,8 +39,8 @@ type trackedTCPConn struct { | |||||
| 39 | backend net.Conn | 39 | backend net.Conn | ||
| 40 | } | 40 | } | ||
| 41 | 41 | ||||
| 42 | - | func newEntryPointRuntime(manager *Manager, entryPoint domain.EntryPoint, listener net.Listener) *entryPointRuntime { | 42 | + | func newEntryPointRuntime(parentCtx context.Context, manager *Manager, entryPoint domain.EntryPoint, listener net.Listener) *entryPointRuntime { |
| 43 | - | ctx, cancel := context.WithCancel(context.Background()) | 43 | + | ctx, cancel := context.WithCancel(context.WithoutCancel(parentCtx)) |
| 44 | return &entryPointRuntime{ | 44 | return &entryPointRuntime{ | ||
| 45 | manager: manager, | 45 | manager: manager, | ||
| 46 | entryPoint: entryPoint, | 46 | entryPoint: entryPoint, | ||
| @@ -56,6 +56,7 @@ func (r *entryPointRuntime) start() { | |||||
| 56 | if !r.started.CompareAndSwap(false, true) { | 56 | if !r.started.CompareAndSwap(false, true) { | ||
| 57 | return | 57 | return | ||
| 58 | } | 58 | } | ||
| 59 | + | trafficInfo(r.ctx).Str("entrypoint", r.entryPoint.Name).Str("address", r.entryPoint.Address).Str("protocol", string(r.entryPoint.Protocol)).Msg("started tcp traffic entrypoint") | |||
| 59 | go r.acceptLoop() | 60 | go r.acceptLoop() | ||
| 60 | } | 61 | } | ||
| 61 | 62 | ||||
| @@ -71,6 +72,7 @@ func (r *entryPointRuntime) acceptLoop() { | |||||
| 71 | if temporary, ok := err.(interface{ Temporary() bool }); ok && temporary.Temporary() { | 72 | if temporary, ok := err.(interface{ Temporary() bool }); ok && temporary.Temporary() { | ||
| 72 | continue | 73 | continue | ||
| 73 | } | 74 | } | ||
| 75 | + | trafficWarn(r.ctx).Err(err).Str("entrypoint", r.entryPoint.Name).Msg("tcp traffic accept loop stopped") | |||
| 74 | return | 76 | return | ||
| 75 | } | 77 | } | ||
| 76 | r.activeWG.Add(1) | 78 | r.activeWG.Add(1) | ||
| @@ -346,6 +348,7 @@ func (r *entryPointRuntime) untrack(conn *trackedTCPConn) { | |||||
| 346 | 348 | ||||
| 347 | func (r *entryPointRuntime) stop(ctx context.Context, drainTimeout time.Duration) { | 349 | func (r *entryPointRuntime) stop(ctx context.Context, drainTimeout time.Duration) { | ||
| 348 | if r.closed.CompareAndSwap(false, true) { | 350 | if r.closed.CompareAndSwap(false, true) { | ||
| 351 | + | trafficInfo(ctx).Str("entrypoint", r.entryPoint.Name).Str("address", r.entryPoint.Address).Msg("stopping tcp traffic entrypoint") | |||
| 349 | r.cancel() | 352 | r.cancel() | ||
| 350 | _ = r.listener.Close() | 353 | _ = r.listener.Close() | ||
| 351 | if !r.started.Load() { | 354 | if !r.started.Load() { | ||
| @@ -358,11 +361,14 @@ func (r *entryPointRuntime) stop(ctx context.Context, drainTimeout time.Duration | |||||
| 358 | return | 361 | return | ||
| 359 | } | 362 | } | ||
| 360 | if r.waitActive(ctx, drainTimeout) { | 363 | if r.waitActive(ctx, drainTimeout) { | ||
| 364 | + | trafficInfo(ctx).Str("entrypoint", r.entryPoint.Name).Msg("stopped tcp traffic entrypoint") | |||
| 361 | return | 365 | return | ||
| 362 | } | 366 | } | ||
| 367 | + | trafficDebug(ctx).Str("entrypoint", r.entryPoint.Name).Dur("drain_timeout", drainTimeout).Msg("forcing tcp traffic entrypoint drain") | |||
| 363 | r.closeActiveConns() | 368 | r.closeActiveConns() | ||
| 364 | select { | 369 | select { | ||
| 365 | case <-r.activeDone(): | 370 | case <-r.activeDone(): | ||
| 371 | + | trafficInfo(ctx).Str("entrypoint", r.entryPoint.Name).Msg("stopped tcp traffic entrypoint") | |||
| 366 | case <-ctx.Done(): | 372 | case <-ctx.Done(): | ||
| 367 | } | 373 | } | ||
| 368 | } | 374 | } | ||
M internal/adapters/in/traffic/tcp_test.go +10 -11
| @@ -8,7 +8,6 @@ import ( | |||
| 8 | 8 | "testing" | |
| 9 | 9 | "time" | |
| 10 | 10 | ||
| 11 | - | "github.com/bnema/zerowrap" | |
| 12 | 11 | "github.com/stretchr/testify/assert" | |
| 13 | 12 | "github.com/stretchr/testify/require" | |
| 14 | 13 | ||
| @@ -18,7 +17,7 @@ import ( | |||
| 18 | 17 | func TestTCPManagerLifecycle(t *testing.T) { | |
| 19 | 18 | backend := startTCPEchoServer(t, 0) | |
| 20 | 19 | graph := tcpGraph(t, freeTCPAddress(t), backend.address) | |
| 21 | - | manager := NewManager(zerowrap.Default()) | |
| 20 | + | manager := NewManager() | |
| 22 | 21 | ||
| 23 | 22 | require.NoError(t, manager.Apply(context.Background(), &graph)) | |
| 24 | 23 | defer shutdownManager(t, manager) | |
| @@ -39,7 +38,7 @@ func TestTCPManagerLifecycle(t *testing.T) { | |||
| 39 | 38 | func TestTCPManagerRejectsInvalidGraphWithoutReplacingSnapshot(t *testing.T) { | |
| 40 | 39 | backend := startTCPEchoServer(t, 0) | |
| 41 | 40 | graph := tcpGraph(t, freeTCPAddress(t), backend.address) | |
| 42 | - | manager := NewManager(zerowrap.Default()) | |
| 41 | + | manager := NewManager() | |
| 43 | 42 | require.NoError(t, manager.Apply(context.Background(), &graph)) | |
| 44 | 43 | defer shutdownManager(t, manager) | |
| 45 | 44 | ||
| @@ -60,7 +59,7 @@ func TestTCPManagerRejectsInvalidGraphWithoutReplacingSnapshot(t *testing.T) { | |||
| 60 | 59 | func TestTCPPassthroughEcho(t *testing.T) { | |
| 61 | 60 | backend := startTCPEchoServer(t, 0) | |
| 62 | 61 | graph := tcpGraph(t, freeTCPAddress(t), backend.address) | |
| 63 | - | manager := NewManager(zerowrap.Default()) | |
| 62 | + | manager := NewManager() | |
| 64 | 63 | require.NoError(t, manager.Apply(context.Background(), &graph)) | |
| 65 | 64 | defer shutdownManager(t, manager) | |
| 66 | 65 | ||
| @@ -73,7 +72,7 @@ func TestTCPPassthroughUnknownRouterCloses(t *testing.T) { | |||
| 73 | 72 | graph := tcpGraph(t, freeTCPAddress(t), freeTCPAddress(t)) | |
| 74 | 73 | graph.Routers = nil | |
| 75 | 74 | graph.Services = nil | |
| 76 | - | manager := NewManager(zerowrap.Default()) | |
| 75 | + | manager := NewManager() | |
| 77 | 76 | require.NoError(t, manager.Apply(context.Background(), &graph)) | |
| 78 | 77 | defer shutdownManager(t, manager) | |
| 79 | 78 | ||
| @@ -89,7 +88,7 @@ func TestTCPPassthroughUnknownRouterCloses(t *testing.T) { | |||
| 89 | 88 | func TestTCPPassthroughBackendDialFailure(t *testing.T) { | |
| 90 | 89 | graph := tcpGraph(t, freeTCPAddress(t), freeTCPAddress(t)) | |
| 91 | 90 | graph.Options.TCP.DialTimeout = 50 * time.Millisecond | |
| 92 | - | manager := NewManager(zerowrap.Default()) | |
| 91 | + | manager := NewManager() | |
| 93 | 92 | require.NoError(t, manager.Apply(context.Background(), &graph)) | |
| 94 | 93 | defer shutdownManager(t, manager) | |
| 95 | 94 | ||
| @@ -105,7 +104,7 @@ func TestTCPPassthroughBackendDialFailure(t *testing.T) { | |||
| 105 | 104 | func TestTCPPassthroughShutdownStopsAccepting(t *testing.T) { | |
| 106 | 105 | backend := startTCPEchoServer(t, 0) | |
| 107 | 106 | graph := tcpGraph(t, freeTCPAddress(t), backend.address) | |
| 108 | - | manager := NewManager(zerowrap.Default()) | |
| 107 | + | manager := NewManager() | |
| 109 | 108 | require.NoError(t, manager.Apply(context.Background(), &graph)) | |
| 110 | 109 | ||
| 111 | 110 | require.NoError(t, manager.Shutdown(context.Background())) | |
| @@ -117,7 +116,7 @@ func TestTCPPassthroughIdleTimeoutClosesConnection(t *testing.T) { | |||
| 117 | 116 | backend := startTCPEchoServer(t, 0) | |
| 118 | 117 | graph := tcpGraph(t, freeTCPAddress(t), backend.address) | |
| 119 | 118 | graph.Options.TCP.IdleTimeout = 50 * time.Millisecond | |
| 120 | - | manager := NewManager(zerowrap.Default()) | |
| 119 | + | manager := NewManager() | |
| 121 | 120 | require.NoError(t, manager.Apply(context.Background(), &graph)) | |
| 122 | 121 | defer shutdownManager(t, manager) | |
| 123 | 122 | ||
| @@ -132,7 +131,7 @@ func TestTCPPassthroughMaxConnectionsRejectsOverflow(t *testing.T) { | |||
| 132 | 131 | backend := startTCPEchoServer(t, 0) | |
| 133 | 132 | graph := tcpGraph(t, freeTCPAddress(t), backend.address) | |
| 134 | 133 | graph.Options.TCP.MaxConnections = 1 | |
| 135 | - | manager := NewManager(zerowrap.Default()) | |
| 134 | + | manager := NewManager() | |
| 136 | 135 | require.NoError(t, manager.Apply(context.Background(), &graph)) | |
| 137 | 136 | defer shutdownManager(t, manager) | |
| 138 | 137 | ||
| @@ -154,7 +153,7 @@ func TestTCPPassthroughDrainWaitsForActiveConnectionThenTimesOut(t *testing.T) { | |||
| 154 | 153 | backend := startTCPEchoServer(t, 0) | |
| 155 | 154 | graph := tcpGraph(t, freeTCPAddress(t), backend.address) | |
| 156 | 155 | graph.Options.TCP.DrainTimeout = 50 * time.Millisecond | |
| 157 | - | manager := NewManager(zerowrap.Default()) | |
| 156 | + | manager := NewManager() | |
| 158 | 157 | require.NoError(t, manager.Apply(context.Background(), &graph)) | |
| 159 | 158 | defer shutdownManager(t, manager) | |
| 160 | 159 | ||
| @@ -179,7 +178,7 @@ func TestTCPPassthroughStatusCountersTrackAcceptedRefusedErrorsAndBytes(t *testi | |||
| 179 | 178 | backend := startTCPEchoServer(t, 0) | |
| 180 | 179 | graph := tcpGraph(t, freeTCPAddress(t), backend.address) | |
| 181 | 180 | graph.Options.TCP.MaxConnections = 1 | |
| 182 | - | manager := NewManager(zerowrap.Default()) | |
| 181 | + | manager := NewManager() | |
| 183 | 182 | require.NoError(t, manager.Apply(context.Background(), &graph)) | |
| 184 | 183 | defer shutdownManager(t, manager) | |
| 185 | 184 | ||
| @@ -8,7 +8,6 @@ import ( | |||||
| 8 | "testing" | 8 | "testing" | ||
| 9 | "time" | 9 | "time" | ||
| 10 | 10 | ||||
| 11 | - | "github.com/bnema/zerowrap" | |||
| 12 | "github.com/stretchr/testify/assert" | 11 | "github.com/stretchr/testify/assert" | ||
| 13 | "github.com/stretchr/testify/require" | 12 | "github.com/stretchr/testify/require" | ||
| 14 | 13 | ||||
| @@ -18,7 +17,7 @@ import ( | |||||
| 18 | func TestTCPManagerLifecycle(t *testing.T) { | 17 | func TestTCPManagerLifecycle(t *testing.T) { | ||
| 19 | backend := startTCPEchoServer(t, 0) | 18 | backend := startTCPEchoServer(t, 0) | ||
| 20 | graph := tcpGraph(t, freeTCPAddress(t), backend.address) | 19 | graph := tcpGraph(t, freeTCPAddress(t), backend.address) | ||
| 21 | - | manager := NewManager(zerowrap.Default()) | 20 | + | manager := NewManager() |
| 22 | 21 | ||||
| 23 | require.NoError(t, manager.Apply(context.Background(), &graph)) | 22 | require.NoError(t, manager.Apply(context.Background(), &graph)) | ||
| 24 | defer shutdownManager(t, manager) | 23 | defer shutdownManager(t, manager) | ||
| @@ -39,7 +38,7 @@ func TestTCPManagerLifecycle(t *testing.T) { | |||||
| 39 | func TestTCPManagerRejectsInvalidGraphWithoutReplacingSnapshot(t *testing.T) { | 38 | func TestTCPManagerRejectsInvalidGraphWithoutReplacingSnapshot(t *testing.T) { | ||
| 40 | backend := startTCPEchoServer(t, 0) | 39 | backend := startTCPEchoServer(t, 0) | ||
| 41 | graph := tcpGraph(t, freeTCPAddress(t), backend.address) | 40 | graph := tcpGraph(t, freeTCPAddress(t), backend.address) | ||
| 42 | - | manager := NewManager(zerowrap.Default()) | 41 | + | manager := NewManager() |
| 43 | require.NoError(t, manager.Apply(context.Background(), &graph)) | 42 | require.NoError(t, manager.Apply(context.Background(), &graph)) | ||
| 44 | defer shutdownManager(t, manager) | 43 | defer shutdownManager(t, manager) | ||
| 45 | 44 | ||||
| @@ -60,7 +59,7 @@ func TestTCPManagerRejectsInvalidGraphWithoutReplacingSnapshot(t *testing.T) { | |||||
| 60 | func TestTCPPassthroughEcho(t *testing.T) { | 59 | func TestTCPPassthroughEcho(t *testing.T) { | ||
| 61 | backend := startTCPEchoServer(t, 0) | 60 | backend := startTCPEchoServer(t, 0) | ||
| 62 | graph := tcpGraph(t, freeTCPAddress(t), backend.address) | 61 | graph := tcpGraph(t, freeTCPAddress(t), backend.address) | ||
| 63 | - | manager := NewManager(zerowrap.Default()) | 62 | + | manager := NewManager() |
| 64 | require.NoError(t, manager.Apply(context.Background(), &graph)) | 63 | require.NoError(t, manager.Apply(context.Background(), &graph)) | ||
| 65 | defer shutdownManager(t, manager) | 64 | defer shutdownManager(t, manager) | ||
| 66 | 65 | ||||
| @@ -73,7 +72,7 @@ func TestTCPPassthroughUnknownRouterCloses(t *testing.T) { | |||||
| 73 | graph := tcpGraph(t, freeTCPAddress(t), freeTCPAddress(t)) | 72 | graph := tcpGraph(t, freeTCPAddress(t), freeTCPAddress(t)) | ||
| 74 | graph.Routers = nil | 73 | graph.Routers = nil | ||
| 75 | graph.Services = nil | 74 | graph.Services = nil | ||
| 76 | - | manager := NewManager(zerowrap.Default()) | 75 | + | manager := NewManager() |
| 77 | require.NoError(t, manager.Apply(context.Background(), &graph)) | 76 | require.NoError(t, manager.Apply(context.Background(), &graph)) | ||
| 78 | defer shutdownManager(t, manager) | 77 | defer shutdownManager(t, manager) | ||
| 79 | 78 | ||||
| @@ -89,7 +88,7 @@ func TestTCPPassthroughUnknownRouterCloses(t *testing.T) { | |||||
| 89 | func TestTCPPassthroughBackendDialFailure(t *testing.T) { | 88 | func TestTCPPassthroughBackendDialFailure(t *testing.T) { | ||
| 90 | graph := tcpGraph(t, freeTCPAddress(t), freeTCPAddress(t)) | 89 | graph := tcpGraph(t, freeTCPAddress(t), freeTCPAddress(t)) | ||
| 91 | graph.Options.TCP.DialTimeout = 50 * time.Millisecond | 90 | graph.Options.TCP.DialTimeout = 50 * time.Millisecond | ||
| 92 | - | manager := NewManager(zerowrap.Default()) | 91 | + | manager := NewManager() |
| 93 | require.NoError(t, manager.Apply(context.Background(), &graph)) | 92 | require.NoError(t, manager.Apply(context.Background(), &graph)) | ||
| 94 | defer shutdownManager(t, manager) | 93 | defer shutdownManager(t, manager) | ||
| 95 | 94 | ||||
| @@ -105,7 +104,7 @@ func TestTCPPassthroughBackendDialFailure(t *testing.T) { | |||||
| 105 | func TestTCPPassthroughShutdownStopsAccepting(t *testing.T) { | 104 | func TestTCPPassthroughShutdownStopsAccepting(t *testing.T) { | ||
| 106 | backend := startTCPEchoServer(t, 0) | 105 | backend := startTCPEchoServer(t, 0) | ||
| 107 | graph := tcpGraph(t, freeTCPAddress(t), backend.address) | 106 | graph := tcpGraph(t, freeTCPAddress(t), backend.address) | ||
| 108 | - | manager := NewManager(zerowrap.Default()) | 107 | + | manager := NewManager() |
| 109 | require.NoError(t, manager.Apply(context.Background(), &graph)) | 108 | require.NoError(t, manager.Apply(context.Background(), &graph)) | ||
| 110 | 109 | ||||
| 111 | require.NoError(t, manager.Shutdown(context.Background())) | 110 | require.NoError(t, manager.Shutdown(context.Background())) | ||
| @@ -117,7 +116,7 @@ func TestTCPPassthroughIdleTimeoutClosesConnection(t *testing.T) { | |||||
| 117 | backend := startTCPEchoServer(t, 0) | 116 | backend := startTCPEchoServer(t, 0) | ||
| 118 | graph := tcpGraph(t, freeTCPAddress(t), backend.address) | 117 | graph := tcpGraph(t, freeTCPAddress(t), backend.address) | ||
| 119 | graph.Options.TCP.IdleTimeout = 50 * time.Millisecond | 118 | graph.Options.TCP.IdleTimeout = 50 * time.Millisecond | ||
| 120 | - | manager := NewManager(zerowrap.Default()) | 119 | + | manager := NewManager() |
| 121 | require.NoError(t, manager.Apply(context.Background(), &graph)) | 120 | require.NoError(t, manager.Apply(context.Background(), &graph)) | ||
| 122 | defer shutdownManager(t, manager) | 121 | defer shutdownManager(t, manager) | ||
| 123 | 122 | ||||
| @@ -132,7 +131,7 @@ func TestTCPPassthroughMaxConnectionsRejectsOverflow(t *testing.T) { | |||||
| 132 | backend := startTCPEchoServer(t, 0) | 131 | backend := startTCPEchoServer(t, 0) | ||
| 133 | graph := tcpGraph(t, freeTCPAddress(t), backend.address) | 132 | graph := tcpGraph(t, freeTCPAddress(t), backend.address) | ||
| 134 | graph.Options.TCP.MaxConnections = 1 | 133 | graph.Options.TCP.MaxConnections = 1 | ||
| 135 | - | manager := NewManager(zerowrap.Default()) | 134 | + | manager := NewManager() |
| 136 | require.NoError(t, manager.Apply(context.Background(), &graph)) | 135 | require.NoError(t, manager.Apply(context.Background(), &graph)) | ||
| 137 | defer shutdownManager(t, manager) | 136 | defer shutdownManager(t, manager) | ||
| 138 | 137 | ||||
| @@ -154,7 +153,7 @@ func TestTCPPassthroughDrainWaitsForActiveConnectionThenTimesOut(t *testing.T) { | |||||
| 154 | backend := startTCPEchoServer(t, 0) | 153 | backend := startTCPEchoServer(t, 0) | ||
| 155 | graph := tcpGraph(t, freeTCPAddress(t), backend.address) | 154 | graph := tcpGraph(t, freeTCPAddress(t), backend.address) | ||
| 156 | graph.Options.TCP.DrainTimeout = 50 * time.Millisecond | 155 | graph.Options.TCP.DrainTimeout = 50 * time.Millisecond | ||
| 157 | - | manager := NewManager(zerowrap.Default()) | 156 | + | manager := NewManager() |
| 158 | require.NoError(t, manager.Apply(context.Background(), &graph)) | 157 | require.NoError(t, manager.Apply(context.Background(), &graph)) | ||
| 159 | defer shutdownManager(t, manager) | 158 | defer shutdownManager(t, manager) | ||
| 160 | 159 | ||||
| @@ -179,7 +178,7 @@ func TestTCPPassthroughStatusCountersTrackAcceptedRefusedErrorsAndBytes(t *testi | |||||
| 179 | backend := startTCPEchoServer(t, 0) | 178 | backend := startTCPEchoServer(t, 0) | ||
| 180 | graph := tcpGraph(t, freeTCPAddress(t), backend.address) | 179 | graph := tcpGraph(t, freeTCPAddress(t), backend.address) | ||
| 181 | graph.Options.TCP.MaxConnections = 1 | 180 | graph.Options.TCP.MaxConnections = 1 | ||
| 182 | - | manager := NewManager(zerowrap.Default()) | 181 | + | manager := NewManager() |
| 183 | require.NoError(t, manager.Apply(context.Background(), &graph)) | 182 | require.NoError(t, manager.Apply(context.Background(), &graph)) | ||
| 184 | defer shutdownManager(t, manager) | 183 | defer shutdownManager(t, manager) | ||
| 185 | 184 | ||||
M internal/adapters/in/traffic/tlsmux_test.go +4 -5
| @@ -18,7 +18,6 @@ import ( | |||
| 18 | 18 | "testing" | |
| 19 | 19 | "time" | |
| 20 | 20 | ||
| 21 | - | "github.com/bnema/zerowrap" | |
| 22 | 21 | "github.com/stretchr/testify/assert" | |
| 23 | 22 | "github.com/stretchr/testify/require" | |
| 24 | 23 | ||
| @@ -80,7 +79,7 @@ func TestTLSPassthrough(t *testing.T) { | |||
| 80 | 79 | {name: "wild", sni: "*.example.com", service: "wild", backend: backendB.address}, | |
| 81 | 80 | {name: "exact", sni: "api.example.com", service: "exact", backend: backendExact.address}, | |
| 82 | 81 | }) | |
| 83 | - | manager := NewManager(zerowrap.Default()) | |
| 82 | + | manager := NewManager() | |
| 84 | 83 | require.NoError(t, manager.Apply(context.Background(), &graph)) | |
| 85 | 84 | defer shutdownManager(t, manager) | |
| 86 | 85 | ||
| @@ -92,7 +91,7 @@ func TestTLSPassthrough(t *testing.T) { | |||
| 92 | 91 | func TestTLSPassthroughUnknownCloses(t *testing.T) { | |
| 93 | 92 | backend := startTLSGreetingServer(t, "backend", "backend\n") | |
| 94 | 93 | graph := tlsGraph(t, freeTCPAddress(t), []tlsRoute{{name: "raw", sni: "raw.example.com", service: "raw", backend: backend.address}}) | |
| 95 | - | manager := NewManager(zerowrap.Default()) | |
| 94 | + | manager := NewManager() | |
| 96 | 95 | require.NoError(t, manager.Apply(context.Background(), &graph)) | |
| 97 | 96 | defer shutdownManager(t, manager) | |
| 98 | 97 | ||
| @@ -106,7 +105,7 @@ func TestTLSMuxIncompleteClientHelloDoesNotHangShutdown(t *testing.T) { | |||
| 106 | 105 | Options: domain.TrafficOptions{TCP: domain.TCPOptions{DialTimeout: time.Second, IdleTimeout: time.Minute, DrainTimeout: 50 * time.Millisecond}}, | |
| 107 | 106 | EntryPoints: []domain.EntryPoint{{Name: "websecure", Address: freeTCPAddress(t), Protocol: domain.EntryPointProtocolTLSMux}}, | |
| 108 | 107 | } | |
| 109 | - | manager := NewManager(zerowrap.Default()) | |
| 108 | + | manager := NewManager() | |
| 110 | 109 | require.NoError(t, manager.Apply(context.Background(), &graph)) | |
| 111 | 110 | ||
| 112 | 111 | conn := dialTCP(t, graph.EntryPoints[0].Address) | |
| @@ -124,7 +123,7 @@ func TestTLSMuxIncompleteClientHelloDoesNotHangShutdown(t *testing.T) { | |||
| 124 | 123 | func TestTLSMuxHTTPSFallback(t *testing.T) { | |
| 125 | 124 | backend := startTLSGreetingServer(t, "raw-backend", "raw\n") | |
| 126 | 125 | graph := tlsGraph(t, freeTCPAddress(t), []tlsRoute{{name: "raw", sni: "raw.example.com", service: "raw", backend: backend.address}}) | |
| 127 | - | manager := NewManager(zerowrap.Default()) | |
| 126 | + | manager := NewManager() | |
| 128 | 127 | fallbackCert := testTLSConfig(t, "fallback.example.com") | |
| 129 | 128 | manager.SetTLSFallback("websecure", func(ctx context.Context, conn net.Conn) { | |
| 130 | 129 | _ = ServeHTTPSFallback(ctx, conn, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { | |
| @@ -18,7 +18,6 @@ import ( | |||||
| 18 | "testing" | 18 | "testing" | ||
| 19 | "time" | 19 | "time" | ||
| 20 | 20 | ||||
| 21 | - | "github.com/bnema/zerowrap" | |||
| 22 | "github.com/stretchr/testify/assert" | 21 | "github.com/stretchr/testify/assert" | ||
| 23 | "github.com/stretchr/testify/require" | 22 | "github.com/stretchr/testify/require" | ||
| 24 | 23 | ||||
| @@ -80,7 +79,7 @@ func TestTLSPassthrough(t *testing.T) { | |||||
| 80 | {name: "wild", sni: "*.example.com", service: "wild", backend: backendB.address}, | 79 | {name: "wild", sni: "*.example.com", service: "wild", backend: backendB.address}, | ||
| 81 | {name: "exact", sni: "api.example.com", service: "exact", backend: backendExact.address}, | 80 | {name: "exact", sni: "api.example.com", service: "exact", backend: backendExact.address}, | ||
| 82 | }) | 81 | }) | ||
| 83 | - | manager := NewManager(zerowrap.Default()) | 82 | + | manager := NewManager() |
| 84 | require.NoError(t, manager.Apply(context.Background(), &graph)) | 83 | require.NoError(t, manager.Apply(context.Background(), &graph)) | ||
| 85 | defer shutdownManager(t, manager) | 84 | defer shutdownManager(t, manager) | ||
| 86 | 85 | ||||
| @@ -92,7 +91,7 @@ func TestTLSPassthrough(t *testing.T) { | |||||
| 92 | func TestTLSPassthroughUnknownCloses(t *testing.T) { | 91 | func TestTLSPassthroughUnknownCloses(t *testing.T) { | ||
| 93 | backend := startTLSGreetingServer(t, "backend", "backend\n") | 92 | backend := startTLSGreetingServer(t, "backend", "backend\n") | ||
| 94 | graph := tlsGraph(t, freeTCPAddress(t), []tlsRoute{{name: "raw", sni: "raw.example.com", service: "raw", backend: backend.address}}) | 93 | graph := tlsGraph(t, freeTCPAddress(t), []tlsRoute{{name: "raw", sni: "raw.example.com", service: "raw", backend: backend.address}}) | ||
| 95 | - | manager := NewManager(zerowrap.Default()) | 94 | + | manager := NewManager() |
| 96 | require.NoError(t, manager.Apply(context.Background(), &graph)) | 95 | require.NoError(t, manager.Apply(context.Background(), &graph)) | ||
| 97 | defer shutdownManager(t, manager) | 96 | defer shutdownManager(t, manager) | ||
| 98 | 97 | ||||
| @@ -106,7 +105,7 @@ func TestTLSMuxIncompleteClientHelloDoesNotHangShutdown(t *testing.T) { | |||||
| 106 | Options: domain.TrafficOptions{TCP: domain.TCPOptions{DialTimeout: time.Second, IdleTimeout: time.Minute, DrainTimeout: 50 * time.Millisecond}}, | 105 | Options: domain.TrafficOptions{TCP: domain.TCPOptions{DialTimeout: time.Second, IdleTimeout: time.Minute, DrainTimeout: 50 * time.Millisecond}}, | ||
| 107 | EntryPoints: []domain.EntryPoint{{Name: "websecure", Address: freeTCPAddress(t), Protocol: domain.EntryPointProtocolTLSMux}}, | 106 | EntryPoints: []domain.EntryPoint{{Name: "websecure", Address: freeTCPAddress(t), Protocol: domain.EntryPointProtocolTLSMux}}, | ||
| 108 | } | 107 | } | ||
| 109 | - | manager := NewManager(zerowrap.Default()) | 108 | + | manager := NewManager() |
| 110 | require.NoError(t, manager.Apply(context.Background(), &graph)) | 109 | require.NoError(t, manager.Apply(context.Background(), &graph)) | ||
| 111 | 110 | ||||
| 112 | conn := dialTCP(t, graph.EntryPoints[0].Address) | 111 | conn := dialTCP(t, graph.EntryPoints[0].Address) | ||
| @@ -124,7 +123,7 @@ func TestTLSMuxIncompleteClientHelloDoesNotHangShutdown(t *testing.T) { | |||||
| 124 | func TestTLSMuxHTTPSFallback(t *testing.T) { | 123 | func TestTLSMuxHTTPSFallback(t *testing.T) { | ||
| 125 | backend := startTLSGreetingServer(t, "raw-backend", "raw\n") | 124 | backend := startTLSGreetingServer(t, "raw-backend", "raw\n") | ||
| 126 | graph := tlsGraph(t, freeTCPAddress(t), []tlsRoute{{name: "raw", sni: "raw.example.com", service: "raw", backend: backend.address}}) | 125 | graph := tlsGraph(t, freeTCPAddress(t), []tlsRoute{{name: "raw", sni: "raw.example.com", service: "raw", backend: backend.address}}) | ||
| 127 | - | manager := NewManager(zerowrap.Default()) | 126 | + | manager := NewManager() |
| 128 | fallbackCert := testTLSConfig(t, "fallback.example.com") | 127 | fallbackCert := testTLSConfig(t, "fallback.example.com") | ||
| 129 | manager.SetTLSFallback("websecure", func(ctx context.Context, conn net.Conn) { | 128 | manager.SetTLSFallback("websecure", func(ctx context.Context, conn net.Conn) { | ||
| 130 | _ = ServeHTTPSFallback(ctx, conn, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { | 129 | _ = ServeHTTPSFallback(ctx, conn, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { | ||
M internal/adapters/in/traffic/udp.go +8 -2
| @@ -40,8 +40,8 @@ type udpSession struct { | |||
| 40 | 40 | once sync.Once | |
| 41 | 41 | } | |
| 42 | 42 | ||
| 43 | - | func newUDPEntryPointRuntime(manager *Manager, entryPoint domain.EntryPoint, packetConn net.PacketConn) *udpEntryPointRuntime { | |
| 44 | - | ctx, cancel := context.WithCancel(context.Background()) | |
| 43 | + | func newUDPEntryPointRuntime(parentCtx context.Context, manager *Manager, entryPoint domain.EntryPoint, packetConn net.PacketConn) *udpEntryPointRuntime { | |
| 44 | + | ctx, cancel := context.WithCancel(context.WithoutCancel(parentCtx)) | |
| 45 | 45 | return &udpEntryPointRuntime{ | |
| 46 | 46 | manager: manager, | |
| 47 | 47 | entryPoint: entryPoint, | |
| @@ -57,6 +57,7 @@ func (r *udpEntryPointRuntime) start() { | |||
| 57 | 57 | if !r.started.CompareAndSwap(false, true) { | |
| 58 | 58 | return | |
| 59 | 59 | } | |
| 60 | + | trafficInfo(r.ctx).Str("entrypoint", r.entryPoint.Name).Str("address", r.entryPoint.Address).Str("protocol", string(r.entryPoint.Protocol)).Msg("started udp traffic entrypoint") | |
| 60 | 61 | go r.readLoop() | |
| 61 | 62 | go r.expireLoop() | |
| 62 | 63 | } | |
| @@ -234,6 +235,7 @@ func (r *udpEntryPointRuntime) resolveUDPBackend() (domain.TrafficBackend, bool) | |||
| 234 | 235 | ||
| 235 | 236 | func (r *udpEntryPointRuntime) stop(ctx context.Context, drainTimeout time.Duration) { | |
| 236 | 237 | if r.closed.CompareAndSwap(false, true) { | |
| 238 | + | trafficInfo(ctx).Str("entrypoint", r.entryPoint.Name).Str("address", r.entryPoint.Address).Msg("stopping udp traffic entrypoint") | |
| 237 | 239 | r.cancel() | |
| 238 | 240 | _ = r.packetConn.Close() | |
| 239 | 241 | if !r.started.Load() { | |
| @@ -246,16 +248,20 @@ func (r *udpEntryPointRuntime) stop(ctx context.Context, drainTimeout time.Durat | |||
| 246 | 248 | return | |
| 247 | 249 | } | |
| 248 | 250 | if r.waitSessions(ctx, drainTimeout) { | |
| 251 | + | trafficInfo(ctx).Str("entrypoint", r.entryPoint.Name).Msg("stopped udp traffic entrypoint") | |
| 249 | 252 | return | |
| 250 | 253 | } | |
| 254 | + | trafficDebug(ctx).Str("entrypoint", r.entryPoint.Name).Dur("drain_timeout", drainTimeout).Msg("forcing udp traffic entrypoint drain") | |
| 251 | 255 | r.closeSessions() | |
| 252 | 256 | select { | |
| 253 | 257 | case <-r.sessionsDone(): | |
| 258 | + | trafficInfo(ctx).Str("entrypoint", r.entryPoint.Name).Msg("stopped udp traffic entrypoint") | |
| 254 | 259 | case <-ctx.Done(): | |
| 255 | 260 | } | |
| 256 | 261 | } | |
| 257 | 262 | ||
| 258 | 263 | func (r *udpEntryPointRuntime) drainSessionsAfter(drainTimeout time.Duration) { | |
| 264 | + | trafficDebug(r.ctx).Str("entrypoint", r.entryPoint.Name).Dur("drain_timeout", drainTimeout).Msg("scheduled udp session drain after router removal") | |
| 259 | 265 | go func() { | |
| 260 | 266 | if drainTimeout <= 0 { | |
| 261 | 267 | drainTimeout = defaultUDPOptions().DrainTimeout | |
| @@ -40,8 +40,8 @@ type udpSession struct { | |||||
| 40 | once sync.Once | 40 | once sync.Once | ||
| 41 | } | 41 | } | ||
| 42 | 42 | ||||
| 43 | - | func newUDPEntryPointRuntime(manager *Manager, entryPoint domain.EntryPoint, packetConn net.PacketConn) *udpEntryPointRuntime { | 43 | + | func newUDPEntryPointRuntime(parentCtx context.Context, manager *Manager, entryPoint domain.EntryPoint, packetConn net.PacketConn) *udpEntryPointRuntime { |
| 44 | - | ctx, cancel := context.WithCancel(context.Background()) | 44 | + | ctx, cancel := context.WithCancel(context.WithoutCancel(parentCtx)) |
| 45 | return &udpEntryPointRuntime{ | 45 | return &udpEntryPointRuntime{ | ||
| 46 | manager: manager, | 46 | manager: manager, | ||
| 47 | entryPoint: entryPoint, | 47 | entryPoint: entryPoint, | ||
| @@ -57,6 +57,7 @@ func (r *udpEntryPointRuntime) start() { | |||||
| 57 | if !r.started.CompareAndSwap(false, true) { | 57 | if !r.started.CompareAndSwap(false, true) { | ||
| 58 | return | 58 | return | ||
| 59 | } | 59 | } | ||
| 60 | + | trafficInfo(r.ctx).Str("entrypoint", r.entryPoint.Name).Str("address", r.entryPoint.Address).Str("protocol", string(r.entryPoint.Protocol)).Msg("started udp traffic entrypoint") | |||
| 60 | go r.readLoop() | 61 | go r.readLoop() | ||
| 61 | go r.expireLoop() | 62 | go r.expireLoop() | ||
| 62 | } | 63 | } | ||
| @@ -234,6 +235,7 @@ func (r *udpEntryPointRuntime) resolveUDPBackend() (domain.TrafficBackend, bool) | |||||
| 234 | 235 | ||||
| 235 | func (r *udpEntryPointRuntime) stop(ctx context.Context, drainTimeout time.Duration) { | 236 | func (r *udpEntryPointRuntime) stop(ctx context.Context, drainTimeout time.Duration) { | ||
| 236 | if r.closed.CompareAndSwap(false, true) { | 237 | if r.closed.CompareAndSwap(false, true) { | ||
| 238 | + | trafficInfo(ctx).Str("entrypoint", r.entryPoint.Name).Str("address", r.entryPoint.Address).Msg("stopping udp traffic entrypoint") | |||
| 237 | r.cancel() | 239 | r.cancel() | ||
| 238 | _ = r.packetConn.Close() | 240 | _ = r.packetConn.Close() | ||
| 239 | if !r.started.Load() { | 241 | if !r.started.Load() { | ||
| @@ -246,16 +248,20 @@ func (r *udpEntryPointRuntime) stop(ctx context.Context, drainTimeout time.Durat | |||||
| 246 | return | 248 | return | ||
| 247 | } | 249 | } | ||
| 248 | if r.waitSessions(ctx, drainTimeout) { | 250 | if r.waitSessions(ctx, drainTimeout) { | ||
| 251 | + | trafficInfo(ctx).Str("entrypoint", r.entryPoint.Name).Msg("stopped udp traffic entrypoint") | |||
| 249 | return | 252 | return | ||
| 250 | } | 253 | } | ||
| 254 | + | trafficDebug(ctx).Str("entrypoint", r.entryPoint.Name).Dur("drain_timeout", drainTimeout).Msg("forcing udp traffic entrypoint drain") | |||
| 251 | r.closeSessions() | 255 | r.closeSessions() | ||
| 252 | select { | 256 | select { | ||
| 253 | case <-r.sessionsDone(): | 257 | case <-r.sessionsDone(): | ||
| 258 | + | trafficInfo(ctx).Str("entrypoint", r.entryPoint.Name).Msg("stopped udp traffic entrypoint") | |||
| 254 | case <-ctx.Done(): | 259 | case <-ctx.Done(): | ||
| 255 | } | 260 | } | ||
| 256 | } | 261 | } | ||
| 257 | 262 | ||||
| 258 | func (r *udpEntryPointRuntime) drainSessionsAfter(drainTimeout time.Duration) { | 263 | func (r *udpEntryPointRuntime) drainSessionsAfter(drainTimeout time.Duration) { | ||
| 264 | + | trafficDebug(r.ctx).Str("entrypoint", r.entryPoint.Name).Dur("drain_timeout", drainTimeout).Msg("scheduled udp session drain after router removal") | |||
| 259 | go func() { | 265 | go func() { | ||
| 260 | if drainTimeout <= 0 { | 266 | if drainTimeout <= 0 { | ||
| 261 | drainTimeout = defaultUDPOptions().DrainTimeout | 267 | drainTimeout = defaultUDPOptions().DrainTimeout | ||
M internal/adapters/in/traffic/udp_test.go +8 -9
| @@ -6,7 +6,6 @@ import ( | |||
| 6 | 6 | "testing" | |
| 7 | 7 | "time" | |
| 8 | 8 | ||
| 9 | - | "github.com/bnema/zerowrap" | |
| 10 | 9 | "github.com/stretchr/testify/assert" | |
| 11 | 10 | "github.com/stretchr/testify/require" | |
| 12 | 11 | ||
| @@ -16,7 +15,7 @@ import ( | |||
| 16 | 15 | func TestUDPPassthroughEcho(t *testing.T) { | |
| 17 | 16 | backend := startUDPEchoServer(t) | |
| 18 | 17 | graph := udpGraph(t, freeUDPAddress(t), backend.address) | |
| 19 | - | manager := NewManager(zerowrap.Default()) | |
| 18 | + | manager := NewManager() | |
| 20 | 19 | require.NoError(t, manager.Apply(context.Background(), &graph)) | |
| 21 | 20 | defer shutdownManager(t, manager) | |
| 22 | 21 | ||
| @@ -28,7 +27,7 @@ func TestUDPPassthroughEcho(t *testing.T) { | |||
| 28 | 27 | func TestUDPTwoClientsGetIsolatedSessions(t *testing.T) { | |
| 29 | 28 | backend := startUDPEchoServer(t) | |
| 30 | 29 | graph := udpGraph(t, freeUDPAddress(t), backend.address) | |
| 31 | - | manager := NewManager(zerowrap.Default()) | |
| 30 | + | manager := NewManager() | |
| 32 | 31 | require.NoError(t, manager.Apply(context.Background(), &graph)) | |
| 33 | 32 | defer shutdownManager(t, manager) | |
| 34 | 33 | ||
| @@ -45,7 +44,7 @@ func TestUDPIdleSessionExpires(t *testing.T) { | |||
| 45 | 44 | backend := startUDPEchoServer(t) | |
| 46 | 45 | graph := udpGraph(t, freeUDPAddress(t), backend.address) | |
| 47 | 46 | graph.Options.UDP.IdleTimeout = 40 * time.Millisecond | |
| 48 | - | manager := NewManager(zerowrap.Default()) | |
| 47 | + | manager := NewManager() | |
| 49 | 48 | require.NoError(t, manager.Apply(context.Background(), &graph)) | |
| 50 | 49 | defer shutdownManager(t, manager) | |
| 51 | 50 | ||
| @@ -59,7 +58,7 @@ func TestUDPMaxSessionsRejectsOverflow(t *testing.T) { | |||
| 59 | 58 | backend := startUDPEchoServer(t) | |
| 60 | 59 | graph := udpGraph(t, freeUDPAddress(t), backend.address) | |
| 61 | 60 | graph.Options.UDP.MaxSessions = 1 | |
| 62 | - | manager := NewManager(zerowrap.Default()) | |
| 61 | + | manager := NewManager() | |
| 63 | 62 | require.NoError(t, manager.Apply(context.Background(), &graph)) | |
| 64 | 63 | defer shutdownManager(t, manager) | |
| 65 | 64 | ||
| @@ -82,7 +81,7 @@ func TestUDPRemovedRouterWithRetainedEntryPointDrainsSession(t *testing.T) { | |||
| 82 | 81 | backend := startUDPEchoServer(t) | |
| 83 | 82 | graph := udpGraph(t, freeUDPAddress(t), backend.address) | |
| 84 | 83 | graph.Options.UDP.DrainTimeout = 50 * time.Millisecond | |
| 85 | - | manager := NewManager(zerowrap.Default()) | |
| 84 | + | manager := NewManager() | |
| 86 | 85 | require.NoError(t, manager.Apply(context.Background(), &graph)) | |
| 87 | 86 | defer shutdownManager(t, manager) | |
| 88 | 87 | ||
| @@ -108,7 +107,7 @@ func TestUDPRemovedRouterDrainsThenClosesSessions(t *testing.T) { | |||
| 108 | 107 | backend := startUDPEchoServer(t) | |
| 109 | 108 | graph := udpGraph(t, freeUDPAddress(t), backend.address) | |
| 110 | 109 | graph.Options.UDP.DrainTimeout = 50 * time.Millisecond | |
| 111 | - | manager := NewManager(zerowrap.Default()) | |
| 110 | + | manager := NewManager() | |
| 112 | 111 | require.NoError(t, manager.Apply(context.Background(), &graph)) | |
| 113 | 112 | defer shutdownManager(t, manager) | |
| 114 | 113 | ||
| @@ -127,7 +126,7 @@ func TestUDPRemovedRouterDrainsThenClosesSessions(t *testing.T) { | |||
| 127 | 126 | func TestUDPShutdownClosesSocket(t *testing.T) { | |
| 128 | 127 | backend := startUDPEchoServer(t) | |
| 129 | 128 | graph := udpGraph(t, freeUDPAddress(t), backend.address) | |
| 130 | - | manager := NewManager(zerowrap.Default()) | |
| 129 | + | manager := NewManager() | |
| 131 | 130 | require.NoError(t, manager.Apply(context.Background(), &graph)) | |
| 132 | 131 | require.NoError(t, manager.Shutdown(context.Background())) | |
| 133 | 132 | ||
| @@ -144,7 +143,7 @@ func TestUDPShutdownClosesSocket(t *testing.T) { | |||
| 144 | 143 | func TestUDPStatusCountersTrackDatagrams(t *testing.T) { | |
| 145 | 144 | backend := startUDPEchoServer(t) | |
| 146 | 145 | graph := udpGraph(t, freeUDPAddress(t), backend.address) | |
| 147 | - | manager := NewManager(zerowrap.Default()) | |
| 146 | + | manager := NewManager() | |
| 148 | 147 | require.NoError(t, manager.Apply(context.Background(), &graph)) | |
| 149 | 148 | defer shutdownManager(t, manager) | |
| 150 | 149 | ||
| @@ -6,7 +6,6 @@ import ( | |||||
| 6 | "testing" | 6 | "testing" | ||
| 7 | "time" | 7 | "time" | ||
| 8 | 8 | ||||
| 9 | - | "github.com/bnema/zerowrap" | |||
| 10 | "github.com/stretchr/testify/assert" | 9 | "github.com/stretchr/testify/assert" | ||
| 11 | "github.com/stretchr/testify/require" | 10 | "github.com/stretchr/testify/require" | ||
| 12 | 11 | ||||
| @@ -16,7 +15,7 @@ import ( | |||||
| 16 | func TestUDPPassthroughEcho(t *testing.T) { | 15 | func TestUDPPassthroughEcho(t *testing.T) { | ||
| 17 | backend := startUDPEchoServer(t) | 16 | backend := startUDPEchoServer(t) | ||
| 18 | graph := udpGraph(t, freeUDPAddress(t), backend.address) | 17 | graph := udpGraph(t, freeUDPAddress(t), backend.address) | ||
| 19 | - | manager := NewManager(zerowrap.Default()) | 18 | + | manager := NewManager() |
| 20 | require.NoError(t, manager.Apply(context.Background(), &graph)) | 19 | require.NoError(t, manager.Apply(context.Background(), &graph)) | ||
| 21 | defer shutdownManager(t, manager) | 20 | defer shutdownManager(t, manager) | ||
| 22 | 21 | ||||
| @@ -28,7 +27,7 @@ func TestUDPPassthroughEcho(t *testing.T) { | |||||
| 28 | func TestUDPTwoClientsGetIsolatedSessions(t *testing.T) { | 27 | func TestUDPTwoClientsGetIsolatedSessions(t *testing.T) { | ||
| 29 | backend := startUDPEchoServer(t) | 28 | backend := startUDPEchoServer(t) | ||
| 30 | graph := udpGraph(t, freeUDPAddress(t), backend.address) | 29 | graph := udpGraph(t, freeUDPAddress(t), backend.address) | ||
| 31 | - | manager := NewManager(zerowrap.Default()) | 30 | + | manager := NewManager() |
| 32 | require.NoError(t, manager.Apply(context.Background(), &graph)) | 31 | require.NoError(t, manager.Apply(context.Background(), &graph)) | ||
| 33 | defer shutdownManager(t, manager) | 32 | defer shutdownManager(t, manager) | ||
| 34 | 33 | ||||
| @@ -45,7 +44,7 @@ func TestUDPIdleSessionExpires(t *testing.T) { | |||||
| 45 | backend := startUDPEchoServer(t) | 44 | backend := startUDPEchoServer(t) | ||
| 46 | graph := udpGraph(t, freeUDPAddress(t), backend.address) | 45 | graph := udpGraph(t, freeUDPAddress(t), backend.address) | ||
| 47 | graph.Options.UDP.IdleTimeout = 40 * time.Millisecond | 46 | graph.Options.UDP.IdleTimeout = 40 * time.Millisecond | ||
| 48 | - | manager := NewManager(zerowrap.Default()) | 47 | + | manager := NewManager() |
| 49 | require.NoError(t, manager.Apply(context.Background(), &graph)) | 48 | require.NoError(t, manager.Apply(context.Background(), &graph)) | ||
| 50 | defer shutdownManager(t, manager) | 49 | defer shutdownManager(t, manager) | ||
| 51 | 50 | ||||
| @@ -59,7 +58,7 @@ func TestUDPMaxSessionsRejectsOverflow(t *testing.T) { | |||||
| 59 | backend := startUDPEchoServer(t) | 58 | backend := startUDPEchoServer(t) | ||
| 60 | graph := udpGraph(t, freeUDPAddress(t), backend.address) | 59 | graph := udpGraph(t, freeUDPAddress(t), backend.address) | ||
| 61 | graph.Options.UDP.MaxSessions = 1 | 60 | graph.Options.UDP.MaxSessions = 1 | ||
| 62 | - | manager := NewManager(zerowrap.Default()) | 61 | + | manager := NewManager() |
| 63 | require.NoError(t, manager.Apply(context.Background(), &graph)) | 62 | require.NoError(t, manager.Apply(context.Background(), &graph)) | ||
| 64 | defer shutdownManager(t, manager) | 63 | defer shutdownManager(t, manager) | ||
| 65 | 64 | ||||
| @@ -82,7 +81,7 @@ func TestUDPRemovedRouterWithRetainedEntryPointDrainsSession(t *testing.T) { | |||||
| 82 | backend := startUDPEchoServer(t) | 81 | backend := startUDPEchoServer(t) | ||
| 83 | graph := udpGraph(t, freeUDPAddress(t), backend.address) | 82 | graph := udpGraph(t, freeUDPAddress(t), backend.address) | ||
| 84 | graph.Options.UDP.DrainTimeout = 50 * time.Millisecond | 83 | graph.Options.UDP.DrainTimeout = 50 * time.Millisecond | ||
| 85 | - | manager := NewManager(zerowrap.Default()) | 84 | + | manager := NewManager() |
| 86 | require.NoError(t, manager.Apply(context.Background(), &graph)) | 85 | require.NoError(t, manager.Apply(context.Background(), &graph)) | ||
| 87 | defer shutdownManager(t, manager) | 86 | defer shutdownManager(t, manager) | ||
| 88 | 87 | ||||
| @@ -108,7 +107,7 @@ func TestUDPRemovedRouterDrainsThenClosesSessions(t *testing.T) { | |||||
| 108 | backend := startUDPEchoServer(t) | 107 | backend := startUDPEchoServer(t) | ||
| 109 | graph := udpGraph(t, freeUDPAddress(t), backend.address) | 108 | graph := udpGraph(t, freeUDPAddress(t), backend.address) | ||
| 110 | graph.Options.UDP.DrainTimeout = 50 * time.Millisecond | 109 | graph.Options.UDP.DrainTimeout = 50 * time.Millisecond | ||
| 111 | - | manager := NewManager(zerowrap.Default()) | 110 | + | manager := NewManager() |
| 112 | require.NoError(t, manager.Apply(context.Background(), &graph)) | 111 | require.NoError(t, manager.Apply(context.Background(), &graph)) | ||
| 113 | defer shutdownManager(t, manager) | 112 | defer shutdownManager(t, manager) | ||
| 114 | 113 | ||||
| @@ -127,7 +126,7 @@ func TestUDPRemovedRouterDrainsThenClosesSessions(t *testing.T) { | |||||
| 127 | func TestUDPShutdownClosesSocket(t *testing.T) { | 126 | func TestUDPShutdownClosesSocket(t *testing.T) { | ||
| 128 | backend := startUDPEchoServer(t) | 127 | backend := startUDPEchoServer(t) | ||
| 129 | graph := udpGraph(t, freeUDPAddress(t), backend.address) | 128 | graph := udpGraph(t, freeUDPAddress(t), backend.address) | ||
| 130 | - | manager := NewManager(zerowrap.Default()) | 129 | + | manager := NewManager() |
| 131 | require.NoError(t, manager.Apply(context.Background(), &graph)) | 130 | require.NoError(t, manager.Apply(context.Background(), &graph)) | ||
| 132 | require.NoError(t, manager.Shutdown(context.Background())) | 131 | require.NoError(t, manager.Shutdown(context.Background())) | ||
| 133 | 132 | ||||
| @@ -144,7 +143,7 @@ func TestUDPShutdownClosesSocket(t *testing.T) { | |||||
| 144 | func TestUDPStatusCountersTrackDatagrams(t *testing.T) { | 143 | func TestUDPStatusCountersTrackDatagrams(t *testing.T) { | ||
| 145 | backend := startUDPEchoServer(t) | 144 | backend := startUDPEchoServer(t) | ||
| 146 | graph := udpGraph(t, freeUDPAddress(t), backend.address) | 145 | graph := udpGraph(t, freeUDPAddress(t), backend.address) | ||
| 147 | - | manager := NewManager(zerowrap.Default()) | 146 | + | manager := NewManager() |
| 148 | require.NoError(t, manager.Apply(context.Background(), &graph)) | 147 | require.NoError(t, manager.Apply(context.Background(), &graph)) | ||
| 149 | defer shutdownManager(t, manager) | 148 | defer shutdownManager(t, manager) | ||
| 150 | 149 | ||||
M internal/app/run.go +2 -2
| @@ -716,7 +716,7 @@ func (si *serviceInit) initRuntimeProxyAndTraffic() error { | |||
| 716 | 716 | if err := si.initRuntimeAndProxy(); err != nil { | |
| 717 | 717 | return err | |
| 718 | 718 | } | |
| 719 | - | si.svc.trafficManager = trafficadapter.NewManager(si.log) | |
| 719 | + | si.svc.trafficManager = trafficadapter.NewManager() | |
| 720 | 720 | if err := applyTrafficRuntimeConfig(si.ctx, si.svc.trafficManager, si.cfg, si.svc.configSvc); err != nil { | |
| 721 | 721 | return si.log.WrapErr(err, "failed to apply traffic configuration") | |
| 722 | 722 | } | |
| @@ -793,7 +793,7 @@ func (si *serviceInit) initHandlers() { | |||
| 793 | 793 | ||
| 794 | 794 | initPreviewService(si.ctx, si.cfg, si.svc, si.log) | |
| 795 | 795 | if si.svc.trafficManager == nil { | |
| 796 | - | si.svc.trafficManager = trafficadapter.NewManager(si.log) | |
| 796 | + | si.svc.trafficManager = trafficadapter.NewManager() | |
| 797 | 797 | } | |
| 798 | 798 | ||
| 799 | 799 | si.svc.adminHandler = admin.NewHandler(admin.HandlerDeps{ | |
| @@ -716,7 +716,7 @@ func (si *serviceInit) initRuntimeProxyAndTraffic() error { | |||||
| 716 | if err := si.initRuntimeAndProxy(); err != nil { | 716 | if err := si.initRuntimeAndProxy(); err != nil { | ||
| 717 | return err | 717 | return err | ||
| 718 | } | 718 | } | ||
| 719 | - | si.svc.trafficManager = trafficadapter.NewManager(si.log) | 719 | + | si.svc.trafficManager = trafficadapter.NewManager() |
| 720 | if err := applyTrafficRuntimeConfig(si.ctx, si.svc.trafficManager, si.cfg, si.svc.configSvc); err != nil { | 720 | if err := applyTrafficRuntimeConfig(si.ctx, si.svc.trafficManager, si.cfg, si.svc.configSvc); err != nil { | ||
| 721 | return si.log.WrapErr(err, "failed to apply traffic configuration") | 721 | return si.log.WrapErr(err, "failed to apply traffic configuration") | ||
| 722 | } | 722 | } | ||
| @@ -793,7 +793,7 @@ func (si *serviceInit) initHandlers() { | |||||
| 793 | 793 | ||||
| 794 | initPreviewService(si.ctx, si.cfg, si.svc, si.log) | 794 | initPreviewService(si.ctx, si.cfg, si.svc, si.log) | ||
| 795 | if si.svc.trafficManager == nil { | 795 | if si.svc.trafficManager == nil { | ||
| 796 | - | si.svc.trafficManager = trafficadapter.NewManager(si.log) | 796 | + | si.svc.trafficManager = trafficadapter.NewManager() |
| 797 | } | 797 | } | ||
| 798 | 798 | ||||
| 799 | si.svc.adminHandler = admin.NewHandler(admin.HandlerDeps{ | 799 | si.svc.adminHandler = admin.NewHandler(admin.HandlerDeps{ | ||
M internal/app/traffic.go +2 -4
| @@ -5,14 +5,12 @@ import ( | |||
| 5 | 5 | "fmt" | |
| 6 | 6 | ||
| 7 | 7 | trafficadapter "github.com/bnema/gordon/internal/adapters/in/traffic" | |
| 8 | + | "github.com/bnema/gordon/internal/boundaries/in" | |
| 8 | 9 | "github.com/bnema/gordon/internal/domain" | |
| 9 | 10 | trafficbuilder "github.com/bnema/gordon/internal/usecase/traffic" | |
| 10 | 11 | ) | |
| 11 | 12 | ||
| 12 | - | func applyTrafficRuntimeConfig(ctx context.Context, manager *trafficadapter.Manager, cfg Config, configSvc interface { | |
| 13 | - | GetRoutes(context.Context) []domain.Route | |
| 14 | - | GetExternalRoutes() map[string]string | |
| 15 | - | }) error { | |
| 13 | + | func applyTrafficRuntimeConfig(ctx context.Context, manager *trafficadapter.Manager, cfg Config, configSvc in.ConfigService) error { | |
| 16 | 14 | if manager == nil || configSvc == nil { | |
| 17 | 15 | return nil | |
| 18 | 16 | } | |
| @@ -5,14 +5,12 @@ import ( | |||||
| 5 | "fmt" | 5 | "fmt" | ||
| 6 | 6 | ||||
| 7 | trafficadapter "github.com/bnema/gordon/internal/adapters/in/traffic" | 7 | trafficadapter "github.com/bnema/gordon/internal/adapters/in/traffic" | ||
| 8 | + | "github.com/bnema/gordon/internal/boundaries/in" | |||
| 8 | "github.com/bnema/gordon/internal/domain" | 9 | "github.com/bnema/gordon/internal/domain" | ||
| 9 | trafficbuilder "github.com/bnema/gordon/internal/usecase/traffic" | 10 | trafficbuilder "github.com/bnema/gordon/internal/usecase/traffic" | ||
| 10 | ) | 11 | ) | ||
| 11 | 12 | ||||
| 12 | - | func applyTrafficRuntimeConfig(ctx context.Context, manager *trafficadapter.Manager, cfg Config, configSvc interface { | 13 | + | func applyTrafficRuntimeConfig(ctx context.Context, manager *trafficadapter.Manager, cfg Config, configSvc in.ConfigService) error { |
| 13 | - | GetRoutes(context.Context) []domain.Route | |||
| 14 | - | GetExternalRoutes() map[string]string | |||
| 15 | - | }) error { | |||
| 16 | if manager == nil || configSvc == nil { | 14 | if manager == nil || configSvc == nil { | ||
| 17 | return nil | 15 | return nil | ||
| 18 | } | 16 | } | ||
M internal/app/traffic_test.go +7 -8
| @@ -5,11 +5,11 @@ import ( | |||
| 5 | 5 | "net" | |
| 6 | 6 | "testing" | |
| 7 | 7 | ||
| 8 | - | "github.com/bnema/zerowrap" | |
| 9 | 8 | "github.com/stretchr/testify/assert" | |
| 10 | 9 | "github.com/stretchr/testify/require" | |
| 11 | 10 | ||
| 12 | 11 | trafficadapter "github.com/bnema/gordon/internal/adapters/in/traffic" | |
| 12 | + | inmocks "github.com/bnema/gordon/internal/boundaries/in/mocks" | |
| 13 | 13 | "github.com/bnema/gordon/internal/domain" | |
| 14 | 14 | "github.com/bnema/gordon/internal/usecase/traffic" | |
| 15 | 15 | ) | |
| @@ -53,7 +53,7 @@ func TestTrafficRuntimeGraphRejectsL4RouterOnLegacyEntrypoint(t *testing.T) { | |||
| 53 | 53 | } | |
| 54 | 54 | ||
| 55 | 55 | func TestApplyTrafficRuntimeConfigAppliesCustomL4Entrypoint(t *testing.T) { | |
| 56 | - | manager := trafficadapter.NewManager(zerowrap.Default()) | |
| 56 | + | manager := trafficadapter.NewManager() | |
| 57 | 57 | defer func() { require.NoError(t, manager.Shutdown(context.Background())) }() | |
| 58 | 58 | cfg := Config{ | |
| 59 | 59 | EntryPoints: map[string]traffic.EntryPointConfig{ | |
| @@ -66,18 +66,17 @@ func TestApplyTrafficRuntimeConfigAppliesCustomL4Entrypoint(t *testing.T) { | |||
| 66 | 66 | } | |
| 67 | 67 | cfg.Traffic.TCP.Routers = []traffic.RouterConfig{{Name: "postgres", EntryPoint: "postgres", Service: "network_service:postgres:db"}} | |
| 68 | 68 | ||
| 69 | - | require.NoError(t, applyTrafficRuntimeConfig(context.Background(), manager, cfg, fakeTrafficConfigService{})) | |
| 69 | + | configSvc := inmocks.NewMockConfigService(t) | |
| 70 | + | configSvc.EXPECT().GetRoutes(context.Background()).Return(nil) | |
| 71 | + | configSvc.EXPECT().GetExternalRoutes().Return(nil) | |
| 72 | + | ||
| 73 | + | require.NoError(t, applyTrafficRuntimeConfig(context.Background(), manager, cfg, configSvc)) | |
| 70 | 74 | status := manager.Status() | |
| 71 | 75 | require.Len(t, status.EntryPoints, 1) | |
| 72 | 76 | assert.Equal(t, "postgres", status.EntryPoints[0].Name) | |
| 73 | 77 | assert.True(t, status.EntryPoints[0].Active) | |
| 74 | 78 | } | |
| 75 | 79 | ||
| 76 | - | type fakeTrafficConfigService struct{} | |
| 77 | - | ||
| 78 | - | func (fakeTrafficConfigService) GetRoutes(context.Context) []domain.Route { return nil } | |
| 79 | - | func (fakeTrafficConfigService) GetExternalRoutes() map[string]string { return nil } | |
| 80 | - | ||
| 81 | 80 | func freeTCPAddress(t *testing.T) string { | |
| 82 | 81 | t.Helper() | |
| 83 | 82 | listener, err := net.Listen("tcp", "127.0.0.1:0") | |
| @@ -5,11 +5,11 @@ import ( | |||||
| 5 | "net" | 5 | "net" | ||
| 6 | "testing" | 6 | "testing" | ||
| 7 | 7 | ||||
| 8 | - | "github.com/bnema/zerowrap" | |||
| 9 | "github.com/stretchr/testify/assert" | 8 | "github.com/stretchr/testify/assert" | ||
| 10 | "github.com/stretchr/testify/require" | 9 | "github.com/stretchr/testify/require" | ||
| 11 | 10 | ||||
| 12 | trafficadapter "github.com/bnema/gordon/internal/adapters/in/traffic" | 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" | 13 | "github.com/bnema/gordon/internal/domain" | ||
| 14 | "github.com/bnema/gordon/internal/usecase/traffic" | 14 | "github.com/bnema/gordon/internal/usecase/traffic" | ||
| 15 | ) | 15 | ) | ||
| @@ -53,7 +53,7 @@ func TestTrafficRuntimeGraphRejectsL4RouterOnLegacyEntrypoint(t *testing.T) { | |||||
| 53 | } | 53 | } | ||
| 54 | 54 | ||||
| 55 | func TestApplyTrafficRuntimeConfigAppliesCustomL4Entrypoint(t *testing.T) { | 55 | func TestApplyTrafficRuntimeConfigAppliesCustomL4Entrypoint(t *testing.T) { | ||
| 56 | - | manager := trafficadapter.NewManager(zerowrap.Default()) | 56 | + | manager := trafficadapter.NewManager() |
| 57 | defer func() { require.NoError(t, manager.Shutdown(context.Background())) }() | 57 | defer func() { require.NoError(t, manager.Shutdown(context.Background())) }() | ||
| 58 | cfg := Config{ | 58 | cfg := Config{ | ||
| 59 | EntryPoints: map[string]traffic.EntryPointConfig{ | 59 | EntryPoints: map[string]traffic.EntryPointConfig{ | ||
| @@ -66,18 +66,17 @@ func TestApplyTrafficRuntimeConfigAppliesCustomL4Entrypoint(t *testing.T) { | |||||
| 66 | } | 66 | } | ||
| 67 | cfg.Traffic.TCP.Routers = []traffic.RouterConfig{{Name: "postgres", EntryPoint: "postgres", Service: "network_service:postgres:db"}} | 67 | cfg.Traffic.TCP.Routers = []traffic.RouterConfig{{Name: "postgres", EntryPoint: "postgres", Service: "network_service:postgres:db"}} | ||
| 68 | 68 | ||||
| 69 | - | require.NoError(t, applyTrafficRuntimeConfig(context.Background(), manager, cfg, fakeTrafficConfigService{})) | 69 | + | configSvc := inmocks.NewMockConfigService(t) |
| 70 | + | configSvc.EXPECT().GetRoutes(context.Background()).Return(nil) | |||
| 71 | + | configSvc.EXPECT().GetExternalRoutes().Return(nil) | |||
| 72 | + | ||||
| 73 | + | require.NoError(t, applyTrafficRuntimeConfig(context.Background(), manager, cfg, configSvc)) | |||
| 70 | status := manager.Status() | 74 | status := manager.Status() | ||
| 71 | require.Len(t, status.EntryPoints, 1) | 75 | require.Len(t, status.EntryPoints, 1) | ||
| 72 | assert.Equal(t, "postgres", status.EntryPoints[0].Name) | 76 | assert.Equal(t, "postgres", status.EntryPoints[0].Name) | ||
| 73 | assert.True(t, status.EntryPoints[0].Active) | 77 | assert.True(t, status.EntryPoints[0].Active) | ||
| 74 | } | 78 | } | ||
| 75 | 79 | ||||
| 76 | - | type fakeTrafficConfigService struct{} | |||
| 77 | - | ||||
| 78 | - | func (fakeTrafficConfigService) GetRoutes(context.Context) []domain.Route { return nil } | |||
| 79 | - | func (fakeTrafficConfigService) GetExternalRoutes() map[string]string { return nil } | |||
| 80 | - | ||||
| 81 | func freeTCPAddress(t *testing.T) string { | 80 | func freeTCPAddress(t *testing.T) string { | ||
| 82 | t.Helper() | 81 | t.Helper() | ||
| 83 | listener, err := net.Listen("tcp", "127.0.0.1:0") | 82 | listener, err := net.Listen("tcp", "127.0.0.1:0") | ||
A internal/boundaries/in/mocks/mock_traffic_status_service.go +81 -0
| @@ -0,0 +1,81 @@ | |||
| 1 | + | // Code generated by mockery; DO NOT EDIT. | |
| 2 | + | // github.com/vektra/mockery | |
| 3 | + | // template: testify | |
| 4 | + | ||
| 5 | + | package mocks | |
| 6 | + | ||
| 7 | + | import ( | |
| 8 | + | "github.com/bnema/gordon/internal/domain" | |
| 9 | + | mock "github.com/stretchr/testify/mock" | |
| 10 | + | ) | |
| 11 | + | ||
| 12 | + | // NewMockTrafficStatusService creates a new instance of MockTrafficStatusService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. | |
| 13 | + | // The first argument is typically a *testing.T value. | |
| 14 | + | func NewMockTrafficStatusService(t interface { | |
| 15 | + | mock.TestingT | |
| 16 | + | Cleanup(func()) | |
| 17 | + | }) *MockTrafficStatusService { | |
| 18 | + | mock := &MockTrafficStatusService{} | |
| 19 | + | mock.Mock.Test(t) | |
| 20 | + | ||
| 21 | + | t.Cleanup(func() { mock.AssertExpectations(t) }) | |
| 22 | + | ||
| 23 | + | return mock | |
| 24 | + | } | |
| 25 | + | ||
| 26 | + | // MockTrafficStatusService is an autogenerated mock type for the TrafficStatusService type | |
| 27 | + | type MockTrafficStatusService struct { | |
| 28 | + | mock.Mock | |
| 29 | + | } | |
| 30 | + | ||
| 31 | + | type MockTrafficStatusService_Expecter struct { | |
| 32 | + | mock *mock.Mock | |
| 33 | + | } | |
| 34 | + | ||
| 35 | + | func (_m *MockTrafficStatusService) EXPECT() *MockTrafficStatusService_Expecter { | |
| 36 | + | return &MockTrafficStatusService_Expecter{mock: &_m.Mock} | |
| 37 | + | } | |
| 38 | + | ||
| 39 | + | // Status provides a mock function for the type MockTrafficStatusService | |
| 40 | + | func (_mock *MockTrafficStatusService) Status() domain.TrafficStatus { | |
| 41 | + | ret := _mock.Called() | |
| 42 | + | ||
| 43 | + | if len(ret) == 0 { | |
| 44 | + | panic("no return value specified for Status") | |
| 45 | + | } | |
| 46 | + | ||
| 47 | + | var r0 domain.TrafficStatus | |
| 48 | + | if returnFunc, ok := ret.Get(0).(func() domain.TrafficStatus); ok { | |
| 49 | + | r0 = returnFunc() | |
| 50 | + | } else { | |
| 51 | + | r0 = ret.Get(0).(domain.TrafficStatus) | |
| 52 | + | } | |
| 53 | + | return r0 | |
| 54 | + | } | |
| 55 | + | ||
| 56 | + | // MockTrafficStatusService_Status_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Status' | |
| 57 | + | type MockTrafficStatusService_Status_Call struct { | |
| 58 | + | *mock.Call | |
| 59 | + | } | |
| 60 | + | ||
| 61 | + | // Status is a helper method to define mock.On call | |
| 62 | + | func (_e *MockTrafficStatusService_Expecter) Status() *MockTrafficStatusService_Status_Call { | |
| 63 | + | return &MockTrafficStatusService_Status_Call{Call: _e.mock.On("Status")} | |
| 64 | + | } | |
| 65 | + | ||
| 66 | + | func (_c *MockTrafficStatusService_Status_Call) Run(run func()) *MockTrafficStatusService_Status_Call { | |
| 67 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 68 | + | run() | |
| 69 | + | }) | |
| 70 | + | return _c | |
| 71 | + | } | |
| 72 | + | ||
| 73 | + | func (_c *MockTrafficStatusService_Status_Call) Return(trafficStatus domain.TrafficStatus) *MockTrafficStatusService_Status_Call { | |
| 74 | + | _c.Call.Return(trafficStatus) | |
| 75 | + | return _c | |
| 76 | + | } | |
| 77 | + | ||
| 78 | + | func (_c *MockTrafficStatusService_Status_Call) RunAndReturn(run func() domain.TrafficStatus) *MockTrafficStatusService_Status_Call { | |
| 79 | + | _c.Call.Return(run) | |
| 80 | + | return _c | |
| 81 | + | } | |
| @@ -0,0 +1,81 @@ | |||||
| 1 | + | // Code generated by mockery; DO NOT EDIT. | |||
| 2 | + | // github.com/vektra/mockery | |||
| 3 | + | // template: testify | |||
| 4 | + | ||||
| 5 | + | package mocks | |||
| 6 | + | ||||
| 7 | + | import ( | |||
| 8 | + | "github.com/bnema/gordon/internal/domain" | |||
| 9 | + | mock "github.com/stretchr/testify/mock" | |||
| 10 | + | ) | |||
| 11 | + | ||||
| 12 | + | // NewMockTrafficStatusService creates a new instance of MockTrafficStatusService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. | |||
| 13 | + | // The first argument is typically a *testing.T value. | |||
| 14 | + | func NewMockTrafficStatusService(t interface { | |||
| 15 | + | mock.TestingT | |||
| 16 | + | Cleanup(func()) | |||
| 17 | + | }) *MockTrafficStatusService { | |||
| 18 | + | mock := &MockTrafficStatusService{} | |||
| 19 | + | mock.Mock.Test(t) | |||
| 20 | + | ||||
| 21 | + | t.Cleanup(func() { mock.AssertExpectations(t) }) | |||
| 22 | + | ||||
| 23 | + | return mock | |||
| 24 | + | } | |||
| 25 | + | ||||
| 26 | + | // MockTrafficStatusService is an autogenerated mock type for the TrafficStatusService type | |||
| 27 | + | type MockTrafficStatusService struct { | |||
| 28 | + | mock.Mock | |||
| 29 | + | } | |||
| 30 | + | ||||
| 31 | + | type MockTrafficStatusService_Expecter struct { | |||
| 32 | + | mock *mock.Mock | |||
| 33 | + | } | |||
| 34 | + | ||||
| 35 | + | func (_m *MockTrafficStatusService) EXPECT() *MockTrafficStatusService_Expecter { | |||
| 36 | + | return &MockTrafficStatusService_Expecter{mock: &_m.Mock} | |||
| 37 | + | } | |||
| 38 | + | ||||
| 39 | + | // Status provides a mock function for the type MockTrafficStatusService | |||
| 40 | + | func (_mock *MockTrafficStatusService) Status() domain.TrafficStatus { | |||
| 41 | + | ret := _mock.Called() | |||
| 42 | + | ||||
| 43 | + | if len(ret) == 0 { | |||
| 44 | + | panic("no return value specified for Status") | |||
| 45 | + | } | |||
| 46 | + | ||||
| 47 | + | var r0 domain.TrafficStatus | |||
| 48 | + | if returnFunc, ok := ret.Get(0).(func() domain.TrafficStatus); ok { | |||
| 49 | + | r0 = returnFunc() | |||
| 50 | + | } else { | |||
| 51 | + | r0 = ret.Get(0).(domain.TrafficStatus) | |||
| 52 | + | } | |||
| 53 | + | return r0 | |||
| 54 | + | } | |||
| 55 | + | ||||
| 56 | + | // MockTrafficStatusService_Status_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Status' | |||
| 57 | + | type MockTrafficStatusService_Status_Call struct { | |||
| 58 | + | *mock.Call | |||
| 59 | + | } | |||
| 60 | + | ||||
| 61 | + | // Status is a helper method to define mock.On call | |||
| 62 | + | func (_e *MockTrafficStatusService_Expecter) Status() *MockTrafficStatusService_Status_Call { | |||
| 63 | + | return &MockTrafficStatusService_Status_Call{Call: _e.mock.On("Status")} | |||
| 64 | + | } | |||
| 65 | + | ||||
| 66 | + | func (_c *MockTrafficStatusService_Status_Call) Run(run func()) *MockTrafficStatusService_Status_Call { | |||
| 67 | + | _c.Call.Run(func(args mock.Arguments) { | |||
| 68 | + | run() | |||
| 69 | + | }) | |||
| 70 | + | return _c | |||
| 71 | + | } | |||
| 72 | + | ||||
| 73 | + | func (_c *MockTrafficStatusService_Status_Call) Return(trafficStatus domain.TrafficStatus) *MockTrafficStatusService_Status_Call { | |||
| 74 | + | _c.Call.Return(trafficStatus) | |||
| 75 | + | return _c | |||
| 76 | + | } | |||
| 77 | + | ||||
| 78 | + | func (_c *MockTrafficStatusService_Status_Call) RunAndReturn(run func() domain.TrafficStatus) *MockTrafficStatusService_Status_Call { | |||
| 79 | + | _c.Call.Return(run) | |||
| 80 | + | return _c | |||
| 81 | + | } | |||
A internal/boundaries/in/traffic.go +8 -0
| @@ -0,0 +1,8 @@ | |||
| 1 | + | package in | |
| 2 | + | ||
| 3 | + | import "github.com/bnema/gordon/internal/domain" | |
| 4 | + | ||
| 5 | + | // TrafficStatusService exposes runtime traffic-plane status. | |
| 6 | + | type TrafficStatusService interface { | |
| 7 | + | Status() domain.TrafficStatus | |
| 8 | + | } | |
| @@ -0,0 +1,8 @@ | |||||
| 1 | + | package in | |||
| 2 | + | ||||
| 3 | + | import "github.com/bnema/gordon/internal/domain" | |||
| 4 | + | ||||
| 5 | + | // TrafficStatusService exposes runtime traffic-plane status. | |||
| 6 | + | type TrafficStatusService interface { | |||
| 7 | + | Status() domain.TrafficStatus | |||
| 8 | + | } | |||