Repository: nginx/nginx-gateway-fabric

Issue:
Handle duplicate kinds in listener allowed kinds
It's possible for a user to configure the following listener:

```
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: gateway
spec:
  gatewayClassName: nginx
  listeners:
  - name: http
    port: 80
    protocol: HTTP
    hostname: "*.example.com"
    allowedRoutes:
      kinds:
        - kind: HTTPRoute
        - kind: HTTPRoute
```

which has duplicate kinds in the allowedRoutes field. NGF then writes those duplicate kinds back to the status in the supported kinds field:

```
Supported Kinds:
  Group:  gateway.networking.k8s.io
  Kind:   HTTPRoute
  Group:  gateway.networking.k8s.io
  Kind:   HTTPRoute
```

A/C:
- Remove any duplicate kinds from SupportedKinds

Discussion:

Solution patch:
diff --git a/internal/controller/state/graph/gateway_listener.go b/internal/controller/state/graph/gateway_listener.go
index 13b6f4d8..3f6e6958 100644
--- a/internal/controller/state/graph/gateway_listener.go
+++ b/internal/controller/state/graph/gateway_listener.go
@@ -287,7 +287,8 @@ func getAndValidateListenerSupportedKinds(listener v1.Listener) (
 	}
 
 	if listener.AllowedRoutes != nil && listener.AllowedRoutes.Kinds != nil {
-		supportedKinds = make([]v1.RouteGroupKind, 0, len(listener.AllowedRoutes.Kinds))
+		unique := make(map[string]struct{})
+		supportedKinds = make([]v1.RouteGroupKind, 0)
 		for _, kind := range listener.AllowedRoutes.Kinds {
 			if !validProtocolRouteKind(kind) {
 				group := v1.GroupName
@@ -298,7 +299,15 @@ func getAndValidateListenerSupportedKinds(listener v1.Listener) (
 				conds = append(conds, conditions.NewListenerInvalidRouteKinds(msg)...)
 				continue
 			}
-			supportedKinds = append(supportedKinds, kind)
+			// Use group/kind as key for uniqueness
+			key := string(kind.Kind)
+			if kind.Group != nil {
+				key = string(*kind.Group) + "/" + key
+			}
+			if _, exists := unique[key]; !exists {
+				unique[key] = struct{}{}
+				supportedKinds = append(supportedKinds, kind)
+			}
 		}
 		return conds, supportedKinds
 	}



Test patch:
diff --git a/internal/controller/state/graph/gateway_listener_test.go b/internal/controller/state/graph/gateway_listener_test.go
index 5dd468c6..0055bbe5 100644
--- a/internal/controller/state/graph/gateway_listener_test.go
+++ b/internal/controller/state/graph/gateway_listener_test.go
@@ -382,6 +382,20 @@ func TestGetAndValidateListenerSupportedKinds(t *testing.T) {
 			name:      "valid and invalid kinds",
 			expected:  []v1.RouteGroupKind{HTTPRouteGroupKind},
 		},
+		{
+			protocol: v1.HTTPProtocolType,
+			kind: []v1.RouteGroupKind{
+				HTTPRouteGroupKind,
+				GRPCRouteGroupKind,
+				GRPCRouteGroupKind,
+			},
+			expectErr: false,
+			name:      "handle duplicate kinds",
+			expected: []v1.RouteGroupKind{
+				HTTPRouteGroupKind,
+				GRPCRouteGroupKind,
+			},
+		},
 		{
 			protocol: v1.TLSProtocolType,
 			kind: []v1.RouteGroupKind{