Skip to content

Commit 2bb935c

Browse files
committed
[feat gw-api]handle status for partially accepted parentRef
1 parent 50c7007 commit 2bb935c

File tree

7 files changed

+113
-84
lines changed

7 files changed

+113
-84
lines changed

controllers/gateway/route_reconciler.go

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ func (d *routeReconcilerImpl) updateRouteStatus(route client.Object, routeData r
173173
}
174174

175175
// Generate key for routeData's parentRef
176-
routeDataParentRefKey := getParentRefKeyFromRouteData(routeData.ParentRefGateway)
176+
routeDataParentRefKey := getParentRefKeyFromRouteData(routeData.ParentRef, routeData.RouteMetadata.RouteNamespace)
177177

178178
// do not allow backward generation update, Accepted and ResolvedRef always have same generation based on our implementation
179179
if (len(newRouteParentStatus.Conditions) != 0 && newRouteParentStatus.Conditions[0].ObservedGeneration <= routeData.RouteMetadata.RouteGeneration) || len(newRouteParentStatus.Conditions) == 0 {
@@ -358,32 +358,22 @@ func getRouteStatus(route client.Object) []gwv1.RouteParentStatus {
358358
return routeStatus
359359
}
360360

361-
// Helper function to generate key from RouteData's ParentRefGateway, use same format as getParentStatusKey
362-
func getParentRefKeyFromRouteData(gatewayRef routeutils.ParentRefGateway) string {
363-
364-
namespace := gatewayRef.Namespace
365-
366-
sectionName := ""
367-
if gatewayRef.SectionName != nil {
368-
sectionName = string(*gatewayRef.SectionName)
369-
}
370-
371-
port := ""
372-
if gatewayRef.Port != nil {
373-
port = strconv.Itoa(int(*gatewayRef.Port))
374-
}
375-
376-
key := fmt.Sprintf("%s/%s/%s/%s",
377-
namespace,
378-
gatewayRef.Name,
379-
sectionName,
380-
port)
381-
382-
return key
361+
// Helper function to generate key from RouteData's ParentReference, use same format as getParentStatusKey
362+
func getParentRefKeyFromRouteData(parentRef gwv1.ParentReference, routeNamespace string) string {
363+
return getParentStatusKey(parentRef, routeNamespace)
383364
}
384365

385366
// Helper function to generate a unique key for a RouteParentStatus
386367
func getParentStatusKey(ref gwv1.ParentReference, routeNamespace string) string {
368+
group := ""
369+
if ref.Group != nil {
370+
group = string(*ref.Group)
371+
}
372+
kind := ""
373+
if ref.Kind != nil {
374+
kind = string(*ref.Kind)
375+
}
376+
387377
namespace := ""
388378
if ref.Namespace != nil {
389379
namespace = string(*ref.Namespace)
@@ -401,7 +391,9 @@ func getParentStatusKey(ref gwv1.ParentReference, routeNamespace string) string
401391
port = strconv.Itoa(int(*ref.Port))
402392
}
403393

404-
key := fmt.Sprintf("%s/%s/%s/%s",
394+
key := fmt.Sprintf("%s/%s/%s/%s/%s/%s",
395+
group,
396+
kind,
405397
namespace,
406398
string(ref.Name),
407399
sectionName,

controllers/gateway/route_reconciler_test.go

Lines changed: 41 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ func Test_getParentStatusKey(t *testing.T) {
308308
Port: portPtr(80),
309309
},
310310
},
311-
want: "test-namespace/test-gateway/test-section/80",
311+
want: "networking.k8s.io/Gateway/test-namespace/test-gateway/test-section/80",
312312
},
313313
{
314314
name: "no namespace should use route namespace",
@@ -321,7 +321,7 @@ func Test_getParentStatusKey(t *testing.T) {
321321
Port: portPtr(80),
322322
},
323323
},
324-
want: "route-namespace/test-gateway/test-section/80",
324+
want: "networking.k8s.io/Gateway/route-namespace/test-gateway/test-section/80",
325325
},
326326
{
327327
name: "no section or port",
@@ -333,7 +333,7 @@ func Test_getParentStatusKey(t *testing.T) {
333333
Name: "test-gateway",
334334
},
335335
},
336-
want: "test-namespace/test-gateway//",
336+
want: "networking.k8s.io/Gateway/test-namespace/test-gateway//",
337337
},
338338
}
339339

@@ -353,7 +353,7 @@ func TestEnqueue(t *testing.T) {
353353
routeData routeutils.RouteData
354354
routeStatusInfo routeutils.RouteStatusInfo
355355
routeMetadataDescriptor routeutils.RouteMetadata
356-
parentRefGateway routeutils.ParentRefGateway
356+
parentRefGateway gwv1.ParentReference
357357

358358
validateEnqueued func(t *testing.T, enqueued []routeutils.EnqueuedType) // Use the type here
359359
}{
@@ -369,7 +369,7 @@ func TestEnqueue(t *testing.T) {
369369
RouteNamespace: "test-namespace",
370370
RouteKind: "test-kind",
371371
},
372-
ParentRefGateway: routeutils.ParentRefGateway{},
372+
ParentRef: gwv1.ParentReference{},
373373
},
374374
validateEnqueued: func(t *testing.T, enqueued []routeutils.EnqueuedType) {
375375
assert.Len(t, enqueued, 1)
@@ -392,7 +392,7 @@ func TestEnqueue(t *testing.T) {
392392
RouteNamespace: "test-namespace",
393393
RouteKind: "test-kind",
394394
},
395-
ParentRefGateway: routeutils.ParentRefGateway{},
395+
ParentRef: gwv1.ParentReference{},
396396
},
397397
validateEnqueued: func(t *testing.T, enqueued []routeutils.EnqueuedType) {
398398
assert.Len(t, enqueued, 1)
@@ -414,7 +414,7 @@ func TestEnqueue(t *testing.T) {
414414
RouteNamespace: "test-namespace",
415415
RouteKind: "test-kind",
416416
},
417-
ParentRefGateway: routeutils.ParentRefGateway{},
417+
ParentRef: gwv1.ParentReference{},
418418
},
419419
validateEnqueued: func(t *testing.T, enqueued []routeutils.EnqueuedType) {
420420
assert.Len(t, enqueued, 1)
@@ -439,6 +439,7 @@ func TestEnqueue(t *testing.T) {
439439
}
440440

441441
func Test_updateRouteStatus(t *testing.T) {
442+
testNamespace := gwv1.Namespace("test-namespace")
442443
tests := []struct {
443444
name string
444445
route client.Object
@@ -463,9 +464,9 @@ func Test_updateRouteStatus(t *testing.T) {
463464
Reason: string(gwv1.RouteConditionAccepted),
464465
Message: "route accepted",
465466
},
466-
ParentRefGateway: routeutils.ParentRefGateway{
467+
ParentRef: gwv1.ParentReference{
467468
Name: "test-gateway",
468-
Namespace: "test-namespace",
469+
Namespace: &testNamespace,
469470
},
470471
},
471472
validateResult: func(t *testing.T, route client.Object) {
@@ -734,52 +735,69 @@ func findCondition(conditions []metav1.Condition, conditionType string) *metav1.
734735
}
735736

736737
func Test_getParentRefKeyFromRouteData(t *testing.T) {
738+
testNamespace := gwv1.Namespace("test-namespace")
739+
testGroup := gwv1.Group("test-group")
740+
testKind := gwv1.Kind("test-kind")
741+
routeNamespace := "route-namespace"
737742
tests := []struct {
738743
name string
739-
gatewayRef routeutils.ParentRefGateway
744+
gatewayRef gwv1.ParentReference
740745
want string
741746
}{
742747
{
743748
name: "all fields provided",
744-
gatewayRef: routeutils.ParentRefGateway{
745-
Namespace: "test-namespace",
749+
gatewayRef: gwv1.ParentReference{
750+
Group: &testGroup,
751+
Kind: &testKind,
752+
Namespace: &testNamespace,
753+
Name: "test-gateway",
754+
SectionName: ptr.To(gwv1.SectionName("test-section")),
755+
Port: ptr.To(gwv1.PortNumber(80)),
756+
},
757+
want: "test-group/test-kind/test-namespace/test-gateway/test-section/80",
758+
},
759+
{
760+
name: "no namespace provided",
761+
gatewayRef: gwv1.ParentReference{
762+
Group: &testGroup,
763+
Kind: &testKind,
746764
Name: "test-gateway",
747765
SectionName: ptr.To(gwv1.SectionName("test-section")),
748766
Port: ptr.To(gwv1.PortNumber(80)),
749767
},
750-
want: "test-namespace/test-gateway/test-section/80",
768+
want: "test-group/test-kind/route-namespace/test-gateway/test-section/80",
751769
},
752770
{
753771
name: "no section or port",
754-
gatewayRef: routeutils.ParentRefGateway{
755-
Namespace: "test-namespace",
772+
gatewayRef: gwv1.ParentReference{
773+
Namespace: &testNamespace,
756774
Name: "test-gateway",
757775
},
758-
want: "test-namespace/test-gateway//",
776+
want: "//test-namespace/test-gateway//",
759777
},
760778
{
761779
name: "with port only",
762-
gatewayRef: routeutils.ParentRefGateway{
763-
Namespace: "test-namespace",
780+
gatewayRef: gwv1.ParentReference{
781+
Namespace: &testNamespace,
764782
Name: "test-gateway",
765783
Port: ptr.To(gwv1.PortNumber(443)),
766784
},
767-
want: "test-namespace/test-gateway//443",
785+
want: "//test-namespace/test-gateway//443",
768786
},
769787
{
770788
name: "with section only",
771-
gatewayRef: routeutils.ParentRefGateway{
772-
Namespace: "test-namespace",
789+
gatewayRef: gwv1.ParentReference{
790+
Namespace: &testNamespace,
773791
Name: "test-gateway",
774792
SectionName: ptr.To(gwv1.SectionName("https")),
775793
},
776-
want: "test-namespace/test-gateway/https/",
794+
want: "//test-namespace/test-gateway/https/",
777795
},
778796
}
779797

780798
for _, tt := range tests {
781799
t.Run(tt.name, func(t *testing.T) {
782-
got := getParentRefKeyFromRouteData(tt.gatewayRef)
800+
got := getParentRefKeyFromRouteData(tt.gatewayRef, routeNamespace)
783801
assert.Equal(t, tt.want, got)
784802
})
785803
}

pkg/gateway/routeutils/listener_attachment_helper_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,8 @@ func Test_listenerAllowsAttachment(t *testing.T) {
103103
assert.Nil(t, statusUpdate)
104104
} else {
105105
assert.NotNil(t, statusUpdate)
106-
assert.Equal(t, gw.Name, statusUpdate.ParentRefGateway.Name)
107-
assert.Equal(t, gw.Namespace, statusUpdate.ParentRefGateway.Namespace)
106+
assert.Equal(t, gwv1.ObjectName(gw.Name), statusUpdate.ParentRef.Name)
107+
assert.Equal(t, gwv1.Namespace(gw.Namespace), *statusUpdate.ParentRef.Namespace)
108108
assert.Equal(t, route.GetRouteNamespacedName().Name, statusUpdate.RouteMetadata.RouteName)
109109
assert.Equal(t, route.GetRouteNamespacedName().Namespace, statusUpdate.RouteMetadata.RouteNamespace)
110110
assert.Equal(t, tc.expectedStatusUpdate.message, statusUpdate.RouteStatusInfo.Message)

pkg/gateway/routeutils/loader.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -242,14 +242,18 @@ func (l *loaderImpl) loadChildResources(ctx context.Context, preloadedRoutes map
242242

243243
func generateRouteDataCacheKey(rd RouteData) string {
244244
port := ""
245-
if rd.ParentRefGateway.Port != nil {
246-
port = fmt.Sprintf("%d", *rd.ParentRefGateway.Port)
245+
if rd.ParentRef.Port != nil {
246+
port = fmt.Sprintf("%d", *rd.ParentRef.Port)
247247
}
248248
sectionName := ""
249-
if rd.ParentRefGateway.SectionName != nil {
250-
sectionName = string(*rd.ParentRefGateway.SectionName)
249+
if rd.ParentRef.SectionName != nil {
250+
sectionName = string(*rd.ParentRef.SectionName)
251251
}
252-
return fmt.Sprintf("%s-%s-%s-%s-%s-%s-%s", rd.RouteMetadata.RouteName, rd.RouteMetadata.RouteNamespace, rd.RouteMetadata.RouteKind, rd.ParentRefGateway.Name, rd.ParentRefGateway.Namespace, port, sectionName)
252+
namespace := ""
253+
if rd.ParentRef.Namespace != nil {
254+
namespace = string(*rd.ParentRef.Namespace)
255+
}
256+
return fmt.Sprintf("%s-%s-%s-%s-%s-%s-%s", rd.RouteMetadata.RouteName, rd.RouteMetadata.RouteNamespace, rd.RouteMetadata.RouteKind, rd.ParentRef.Name, namespace, port, sectionName)
253257
}
254258

255259
func buildAttachedRouteMap(gw gwv1.Gateway, mappedRoutes map[int][]preLoadRouteDescriptor) map[gwv1.SectionName]int32 {

pkg/gateway/routeutils/loader_test.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ func (m *mockRoute) GetRouteIdentifier() string {
105105
}
106106

107107
func Test_LoadRoutesForGateway(t *testing.T) {
108+
testNamespace := gwv1.Namespace("gw-ns")
109+
108110
preLoadHTTPRoutes := []preLoadRouteDescriptor{
109111
&mockRoute{
110112
namespacedName: types.NamespacedName{
@@ -293,9 +295,9 @@ func Test_LoadRoutesForGateway(t *testing.T) {
293295
RouteKind: string(TCPRouteKind),
294296
RouteGeneration: 0,
295297
},
296-
ParentRefGateway: ParentRefGateway{
298+
ParentRef: gwv1.ParentReference{
297299
Name: "gw",
298-
Namespace: "gw-ns",
300+
Namespace: &testNamespace,
299301
},
300302
},
301303
},
@@ -326,9 +328,9 @@ func Test_LoadRoutesForGateway(t *testing.T) {
326328
RouteKind: string(HTTPRouteKind),
327329
RouteGeneration: 0,
328330
},
329-
ParentRefGateway: ParentRefGateway{
331+
ParentRef: gwv1.ParentReference{
330332
Name: "gw",
331-
Namespace: "gw-ns",
333+
Namespace: &testNamespace,
332334
},
333335
},
334336
{
@@ -341,9 +343,9 @@ func Test_LoadRoutesForGateway(t *testing.T) {
341343
RouteKind: string(HTTPRouteKind),
342344
RouteGeneration: 0,
343345
},
344-
ParentRefGateway: ParentRefGateway{
346+
ParentRef: gwv1.ParentReference{
345347
Name: "gw",
346-
Namespace: "gw-ns",
348+
Namespace: &testNamespace,
347349
},
348350
},
349351
},

pkg/gateway/routeutils/route_listener_mapper.go

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,9 +151,26 @@ func (ltr *listenerToRouteMapperImpl) mapGatewayAndRoutes(ctx context.Context, g
151151
}
152152
}
153153

154+
// Per Gateway API spec: "If 1 of 2 Gateway listeners accept attachment from the referencing Route,
155+
// the Route MUST be considered successfully attached."
156+
// For specific parentRefs (with sectionName/port), report individual status.
157+
// For generic parentRefs (no sectionName/port), skip failures if any listener accepted.
154158
failedRoutes := make([]RouteData, 0)
155-
for _, routeDataList := range failedRoutesMap {
156-
failedRoutes = append(failedRoutes, routeDataList...)
159+
for routeKey, routeDataList := range failedRoutesMap {
160+
for _, routeData := range routeDataList {
161+
parentRefKey := getParentRefKey(routeData.ParentRef, gw)
162+
// Check if this specific parentRef succeeded
163+
if _, succeeded := matchedParentRefs[routeKey][parentRefKey]; succeeded {
164+
continue
165+
}
166+
// For generic parentRefs (no sectionName/port), skip if route attached anywhere
167+
if routeData.ParentRef.SectionName == nil && routeData.ParentRef.Port == nil {
168+
if _, hasMatches := matchedParentRefs[routeKey]; hasMatches {
169+
continue
170+
}
171+
}
172+
failedRoutes = append(failedRoutes, routeData)
173+
}
157174
}
158175

159176
// Convert matchedParentRefs to return format

0 commit comments

Comments
 (0)