Skip to content

Commit ec4b505

Browse files
committed
fix(grpcserver): preserve projected backend refs by port
1 parent 10c8305 commit ec4b505

2 files changed

Lines changed: 138 additions & 0 deletions

File tree

internal/grpcserver/snapshot_projection.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package grpcserver
22

33
import (
44
"log/slog"
5+
"strconv"
56

67
"github.com/nantian-gw/gateway/internal/ir"
78
controlv1 "github.com/nantian-gw/proto/gateway/control/v1"
@@ -182,6 +183,12 @@ func filterBackendRefs(routeNamespace string, refs []ir.BackendRef, survivingBac
182183
}
183184
if _, ok := survivingBackends[backendProjectionKey(namespace, ref.Name)]; ok {
184185
out = append(out, ref)
186+
continue
187+
}
188+
if ref.Port != 0 {
189+
if _, ok := survivingBackends[backendProjectionKey(namespace, portQualifiedBackendName(ref.Name, ref.Port))]; ok {
190+
out = append(out, ref)
191+
}
185192
}
186193
}
187194
return out
@@ -282,6 +289,10 @@ func backendProjectionKey(namespace, name string) string {
282289
return namespace + "/" + name
283290
}
284291

292+
func portQualifiedBackendName(name string, port uint32) string {
293+
return name + ":" + strconv.Itoa(int(port))
294+
}
295+
285296
func supportsFeature(supported map[string]struct{}, feature string) bool {
286297
_, ok := supported[feature]
287298
return ok

internal/grpcserver/snapshot_projection_test.go

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,133 @@ func TestProjectedSnapshotPreservesListenerWhenAllAttachedRoutesArePruned(t *tes
173173
}
174174
}
175175

176+
func TestProjectedSnapshotKeepsHTTPRouteWithPortQualifiedBackendName(t *testing.T) {
177+
t.Parallel()
178+
179+
projected := buildProjectedProtoSnapshot(
180+
&ir.Snapshot{
181+
HTTPRoutes: []ir.HTTPRoute{{
182+
Name: "echo-route",
183+
Namespace: "default",
184+
Rules: []ir.HTTPRule{{
185+
Name: "echo",
186+
BackendRefs: []ir.BackendRef{{
187+
Name: "echo",
188+
Namespace: "default",
189+
Port: 80,
190+
}},
191+
}},
192+
}},
193+
Backends: []ir.BackendCluster{{
194+
Name: "echo:80",
195+
Namespace: "default",
196+
Protocol: "HTTP",
197+
ConnectTimeout: 5 * time.Second,
198+
Endpoints: []ir.BackendEndpoint{{
199+
Address: "10.0.0.10",
200+
Port: 80,
201+
Healthy: true,
202+
}},
203+
}},
204+
},
205+
effectiveProjectionProfile([]string{featureCoreV1}),
206+
slog.New(slog.NewTextHandler(io.Discard, nil)),
207+
)
208+
209+
route := findProjectedHTTPRoute(t, projected, "echo-route")
210+
if got := len(route.GetRules()); got != 1 {
211+
t.Fatalf("http route rule count = %d, want 1", got)
212+
}
213+
if got := len(route.GetRules()[0].GetBackendRefs()); got != 1 {
214+
t.Fatalf("http route backend ref count = %d, want 1", got)
215+
}
216+
}
217+
218+
func TestProjectedSnapshotKeepsGRPCRouteWithPortQualifiedBackendName(t *testing.T) {
219+
t.Parallel()
220+
221+
projected := buildProjectedProtoSnapshot(
222+
&ir.Snapshot{
223+
GRPCRoutes: []ir.GRPCRoute{{
224+
Name: "echo-grpc",
225+
Namespace: "default",
226+
Rules: []ir.GRPCRule{{
227+
Name: "echo",
228+
BackendRefs: []ir.BackendRef{{
229+
Name: "echo",
230+
Namespace: "default",
231+
Port: 9000,
232+
}},
233+
}},
234+
}},
235+
Backends: []ir.BackendCluster{{
236+
Name: "echo:9000",
237+
Namespace: "default",
238+
Protocol: "GRPC",
239+
ConnectTimeout: 5 * time.Second,
240+
Endpoints: []ir.BackendEndpoint{{
241+
Address: "10.0.0.11",
242+
Port: 9000,
243+
Healthy: true,
244+
}},
245+
}},
246+
},
247+
effectiveProjectionProfile([]string{featureCoreV1}),
248+
slog.New(slog.NewTextHandler(io.Discard, nil)),
249+
)
250+
251+
route := findProjectedGRPCRoute(t, projected, "echo-grpc")
252+
if got := len(route.GetRules()); got != 1 {
253+
t.Fatalf("grpc route rule count = %d, want 1", got)
254+
}
255+
if got := len(route.GetRules()[0].GetBackendRefs()); got != 1 {
256+
t.Fatalf("grpc route backend ref count = %d, want 1", got)
257+
}
258+
}
259+
260+
func TestProjectedSnapshotKeepsStreamRouteWithPortQualifiedBackendName(t *testing.T) {
261+
t.Parallel()
262+
263+
projected := buildProjectedProtoSnapshot(
264+
&ir.Snapshot{
265+
StreamRoutes: []ir.StreamRoute{{
266+
Name: "echo-stream",
267+
Namespace: "default",
268+
Kind: "TCP",
269+
Rules: []ir.StreamRule{{
270+
Name: "echo",
271+
BackendRefs: []ir.BackendRef{{
272+
Name: "echo",
273+
Namespace: "default",
274+
Port: 7000,
275+
}},
276+
}},
277+
}},
278+
Backends: []ir.BackendCluster{{
279+
Name: "echo:7000",
280+
Namespace: "default",
281+
Protocol: "TCP",
282+
ConnectTimeout: 5 * time.Second,
283+
Endpoints: []ir.BackendEndpoint{{
284+
Address: "10.0.0.12",
285+
Port: 7000,
286+
Healthy: true,
287+
}},
288+
}},
289+
},
290+
effectiveProjectionProfile([]string{featureCoreV1}),
291+
slog.New(slog.NewTextHandler(io.Discard, nil)),
292+
)
293+
294+
route := findProjectedStreamRoute(t, projected, "echo-stream")
295+
if got := len(route.GetRules()); got != 1 {
296+
t.Fatalf("stream route rule count = %d, want 1", got)
297+
}
298+
if got := len(route.GetRules()[0].GetBackendRefs()); got != 1 {
299+
t.Fatalf("stream route backend ref count = %d, want 1", got)
300+
}
301+
}
302+
176303
func projectionTestSnapshot() *ir.Snapshot {
177304
return &ir.Snapshot{
178305
ID: "projection-snapshot",

0 commit comments

Comments
 (0)