diff --git a/internal/mode/static/state/change_processor_test.go b/internal/mode/static/state/change_processor_test.go index 4b88aeb7de..3e1455b2ca 100644 --- a/internal/mode/static/state/change_processor_test.go +++ b/internal/mode/static/state/change_processor_test.go @@ -519,12 +519,15 @@ var _ = Describe("ChangeProcessor", func() { Source: gw1, Listeners: []*graph.Listener{ { - Name: "listener-80-1", - Source: gw1.Spec.Listeners[0], - Valid: true, - Attachable: true, - Routes: map[graph.RouteKey]*graph.L7Route{routeKey1: expRouteHR1}, - SupportedKinds: []v1.RouteGroupKind{{Kind: kinds.HTTPRoute}}, + Name: "listener-80-1", + Source: gw1.Spec.Listeners[0], + Valid: true, + Attachable: true, + Routes: map[graph.RouteKey]*graph.L7Route{routeKey1: expRouteHR1}, + SupportedKinds: []v1.RouteGroupKind{ + {Kind: v1.Kind(kinds.HTTPRoute), Group: helpers.GetPointer[v1.Group](v1.GroupName)}, + {Kind: v1.Kind(kinds.GRPCRoute), Group: helpers.GetPointer[v1.Group](v1.GroupName)}, + }, }, { Name: "listener-443-1", @@ -533,7 +536,10 @@ var _ = Describe("ChangeProcessor", func() { Attachable: true, Routes: map[graph.RouteKey]*graph.L7Route{routeKey1: expRouteHR1}, ResolvedSecret: helpers.GetPointer(client.ObjectKeyFromObject(diffNsTLSSecret)), - SupportedKinds: []v1.RouteGroupKind{{Kind: kinds.HTTPRoute}}, + SupportedKinds: []v1.RouteGroupKind{ + {Kind: v1.Kind(kinds.HTTPRoute), Group: helpers.GetPointer[v1.Group](v1.GroupName)}, + {Kind: v1.Kind(kinds.GRPCRoute), Group: helpers.GetPointer[v1.Group](v1.GroupName)}, + }, }, }, Valid: true, diff --git a/internal/mode/static/state/graph/gateway_listener.go b/internal/mode/static/state/graph/gateway_listener.go index 7c61d45231..f1f4e6bd9f 100644 --- a/internal/mode/static/state/graph/gateway_listener.go +++ b/internal/mode/static/state/graph/gateway_listener.go @@ -11,6 +11,7 @@ import ( v1 "sigs.k8s.io/gateway-api/apis/v1" "github.com/nginxinc/nginx-gateway-fabric/internal/framework/conditions" + "github.com/nginxinc/nginx-gateway-fabric/internal/framework/helpers" "github.com/nginxinc/nginx-gateway-fabric/internal/framework/kinds" staticConds "github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/conditions" ) @@ -224,22 +225,17 @@ func validateListenerHostname(listener v1.Listener) (conds []conditions.Conditio return nil, true } +// getAndValidateListenerSupportedKinds validates the route kind and returns the supported kinds for the listener. +// The supported kinds are determined based on the listener's allowedRoutes field. +// If the listener does not specify allowedRoutes, listener determines allowed routes based on its protocol. func getAndValidateListenerSupportedKinds(listener v1.Listener) ( []conditions.Condition, []v1.RouteGroupKind, ) { - if listener.AllowedRoutes == nil || listener.AllowedRoutes.Kinds == nil { - return nil, []v1.RouteGroupKind{ - { - Kind: kinds.HTTPRoute, - }, - } - } var conds []conditions.Condition + var supportedKinds []v1.RouteGroupKind - supportedKinds := make([]v1.RouteGroupKind, 0, len(listener.AllowedRoutes.Kinds)) - - validHTTPProtocolRouteKind := func(kind v1.RouteGroupKind) bool { + validRouteKind := func(kind v1.RouteGroupKind) bool { if kind.Kind != v1.Kind(kinds.HTTPRoute) && kind.Kind != v1.Kind(kinds.GRPCRoute) { return false } @@ -249,17 +245,26 @@ func getAndValidateListenerSupportedKinds(listener v1.Listener) ( return true } - switch listener.Protocol { - case v1.HTTPProtocolType, v1.HTTPSProtocolType: + if listener.AllowedRoutes != nil && listener.AllowedRoutes.Kinds != nil { + supportedKinds = make([]v1.RouteGroupKind, 0, len(listener.AllowedRoutes.Kinds)) for _, kind := range listener.AllowedRoutes.Kinds { - if !validHTTPProtocolRouteKind(kind) { + if !validRouteKind(kind) { msg := fmt.Sprintf("Unsupported route kind \"%s/%s\"", *kind.Group, kind.Kind) conds = append(conds, staticConds.NewListenerInvalidRouteKinds(msg)...) continue } supportedKinds = append(supportedKinds, kind) } + } else { + switch listener.Protocol { + case v1.HTTPProtocolType, v1.HTTPSProtocolType: + supportedKinds = []v1.RouteGroupKind{ + {Kind: v1.Kind(kinds.HTTPRoute), Group: helpers.GetPointer[v1.Group](v1.GroupName)}, + {Kind: v1.Kind(kinds.GRPCRoute), Group: helpers.GetPointer[v1.Group](v1.GroupName)}, + } + } } + return conds, supportedKinds } diff --git a/internal/mode/static/state/graph/gateway_listener_test.go b/internal/mode/static/state/graph/gateway_listener_test.go index 96aba1a66c..d60ad7321b 100644 --- a/internal/mode/static/state/graph/gateway_listener_test.go +++ b/internal/mode/static/state/graph/gateway_listener_test.go @@ -289,11 +289,13 @@ func TestValidateListenerHostname(t *testing.T) { } func TestGetAndValidateListenerSupportedKinds(t *testing.T) { - HTTPRouteGroupKind := []v1.RouteGroupKind{ - { - Kind: kinds.HTTPRoute, - Group: helpers.GetPointer[v1.Group](v1.GroupName), - }, + HTTPRouteGroupKind := v1.RouteGroupKind{ + Kind: kinds.HTTPRoute, + Group: helpers.GetPointer[v1.Group](v1.GroupName), + } + GRPCRouteGroupKind := v1.RouteGroupKind{ + Kind: kinds.GRPCRoute, + Group: helpers.GetPointer[v1.Group](v1.GroupName), } TCPRouteGroupKind := []v1.RouteGroupKind{ { @@ -312,8 +314,7 @@ func TestGetAndValidateListenerSupportedKinds(t *testing.T) { protocol: v1.TCPProtocolType, expectErr: false, name: "unsupported protocol is ignored", - kind: TCPRouteGroupKind, - expected: []v1.RouteGroupKind{}, + expected: nil, }, { protocol: v1.HTTPProtocolType, @@ -336,35 +337,30 @@ func TestGetAndValidateListenerSupportedKinds(t *testing.T) { }, { protocol: v1.HTTPProtocolType, - kind: HTTPRouteGroupKind, + kind: []v1.RouteGroupKind{HTTPRouteGroupKind}, expectErr: false, name: "valid HTTP", - expected: HTTPRouteGroupKind, + expected: []v1.RouteGroupKind{HTTPRouteGroupKind}, }, { protocol: v1.HTTPSProtocolType, - kind: HTTPRouteGroupKind, + kind: []v1.RouteGroupKind{HTTPRouteGroupKind}, expectErr: false, name: "valid HTTPS", - expected: HTTPRouteGroupKind, + expected: []v1.RouteGroupKind{HTTPRouteGroupKind}, }, { protocol: v1.HTTPSProtocolType, expectErr: false, name: "valid HTTPS no kind specified", expected: []v1.RouteGroupKind{ - { - Kind: kinds.HTTPRoute, - }, + HTTPRouteGroupKind, GRPCRouteGroupKind, }, }, { protocol: v1.HTTPProtocolType, kind: []v1.RouteGroupKind{ - { - Kind: kinds.HTTPRoute, - Group: helpers.GetPointer[v1.Group](v1.GroupName), - }, + HTTPRouteGroupKind, { Kind: "bad-kind", Group: helpers.GetPointer[v1.Group](v1.GroupName), @@ -372,7 +368,7 @@ func TestGetAndValidateListenerSupportedKinds(t *testing.T) { }, expectErr: true, name: "valid and invalid kinds", - expected: HTTPRouteGroupKind, + expected: []v1.RouteGroupKind{HTTPRouteGroupKind}, }, } diff --git a/internal/mode/static/state/graph/gateway_test.go b/internal/mode/static/state/graph/gateway_test.go index 26f3f48c88..b942c4470f 100644 --- a/internal/mode/static/state/graph/gateway_test.go +++ b/internal/mode/static/state/graph/gateway_test.go @@ -347,6 +347,11 @@ func TestBuildGateway(t *testing.T) { Valid: false, } + supportedKindsForListeners := []v1.RouteGroupKind{ + {Kind: v1.Kind(kinds.HTTPRoute), Group: helpers.GetPointer[v1.Group](v1.GroupName)}, + {Kind: v1.Kind(kinds.GRPCRoute), Group: helpers.GetPointer[v1.Group](v1.GroupName)}, + } + tests := []struct { gateway *v1.Gateway gatewayClass *GatewayClass @@ -361,24 +366,20 @@ func TestBuildGateway(t *testing.T) { Source: getLastCreatedGetaway(), Listeners: []*Listener{ { - Name: "foo-80-1", - Source: foo80Listener1, - Valid: true, - Attachable: true, - Routes: map[RouteKey]*L7Route{}, - SupportedKinds: []v1.RouteGroupKind{ - {Kind: kinds.HTTPRoute}, - }, + Name: "foo-80-1", + Source: foo80Listener1, + Valid: true, + Attachable: true, + Routes: map[RouteKey]*L7Route{}, + SupportedKinds: supportedKindsForListeners, }, { - Name: "foo-8080", - Source: foo8080Listener, - Valid: true, - Attachable: true, - Routes: map[RouteKey]*L7Route{}, - SupportedKinds: []v1.RouteGroupKind{ - {Kind: kinds.HTTPRoute}, - }, + Name: "foo-8080", + Source: foo8080Listener, + Valid: true, + Attachable: true, + Routes: map[RouteKey]*L7Route{}, + SupportedKinds: supportedKindsForListeners, }, }, Valid: true, @@ -400,9 +401,7 @@ func TestBuildGateway(t *testing.T) { Attachable: true, Routes: map[RouteKey]*L7Route{}, ResolvedSecret: helpers.GetPointer(client.ObjectKeyFromObject(secretSameNs)), - SupportedKinds: []v1.RouteGroupKind{ - {Kind: kinds.HTTPRoute}, - }, + SupportedKinds: supportedKindsForListeners, }, { Name: "foo-8443-https", @@ -411,9 +410,7 @@ func TestBuildGateway(t *testing.T) { Attachable: true, Routes: map[RouteKey]*L7Route{}, ResolvedSecret: helpers.GetPointer(client.ObjectKeyFromObject(secretSameNs)), - SupportedKinds: []v1.RouteGroupKind{ - {Kind: kinds.HTTPRoute}, - }, + SupportedKinds: supportedKindsForListeners, }, }, Valid: true, @@ -479,9 +476,7 @@ func TestBuildGateway(t *testing.T) { Attachable: true, Routes: map[RouteKey]*L7Route{}, ResolvedSecret: helpers.GetPointer(client.ObjectKeyFromObject(secretDiffNamespace)), - SupportedKinds: []v1.RouteGroupKind{ - {Kind: kinds.HTTPRoute}, - }, + SupportedKinds: supportedKindsForListeners, }, }, Valid: true, @@ -502,10 +497,8 @@ func TestBuildGateway(t *testing.T) { Conditions: staticConds.NewListenerRefNotPermitted( `Certificate ref to secret diff-ns/secret not permitted by any ReferenceGrant`, ), - Routes: map[RouteKey]*L7Route{}, - SupportedKinds: []v1.RouteGroupKind{ - {Kind: kinds.HTTPRoute}, - }, + Routes: map[RouteKey]*L7Route{}, + SupportedKinds: supportedKindsForListeners, }, }, Valid: true, @@ -551,9 +544,6 @@ func TestBuildGateway(t *testing.T) { `protocol: Unsupported value: "TCP": supported values: "HTTP", "HTTPS"`, ), Routes: map[RouteKey]*L7Route{}, - SupportedKinds: []v1.RouteGroupKind{ - {Kind: kinds.HTTPRoute}, - }, }, }, Valid: true, @@ -582,10 +572,8 @@ func TestBuildGateway(t *testing.T) { Conditions: staticConds.NewListenerUnsupportedValue( `port: Invalid value: 0: port must be between 1-65535`, ), - Routes: map[RouteKey]*L7Route{}, - SupportedKinds: []v1.RouteGroupKind{ - {Kind: kinds.HTTPRoute}, - }, + Routes: map[RouteKey]*L7Route{}, + SupportedKinds: supportedKindsForListeners, }, { Name: "invalid-https-port", @@ -595,10 +583,8 @@ func TestBuildGateway(t *testing.T) { Conditions: staticConds.NewListenerUnsupportedValue( `port: Invalid value: 65536: port must be between 1-65535`, ), - Routes: map[RouteKey]*L7Route{}, - SupportedKinds: []v1.RouteGroupKind{ - {Kind: kinds.HTTPRoute}, - }, + Routes: map[RouteKey]*L7Route{}, + SupportedKinds: supportedKindsForListeners, }, { Name: "invalid-protected-port", @@ -608,10 +594,8 @@ func TestBuildGateway(t *testing.T) { Conditions: staticConds.NewListenerUnsupportedValue( `port: Invalid value: 9113: port is already in use as MetricsPort`, ), - Routes: map[RouteKey]*L7Route{}, - SupportedKinds: []v1.RouteGroupKind{ - {Kind: kinds.HTTPRoute}, - }, + Routes: map[RouteKey]*L7Route{}, + SupportedKinds: supportedKindsForListeners, }, }, Valid: true, @@ -627,24 +611,20 @@ func TestBuildGateway(t *testing.T) { Source: getLastCreatedGetaway(), Listeners: []*Listener{ { - Name: "invalid-hostname", - Source: invalidHostnameListener, - Valid: false, - Conditions: staticConds.NewListenerUnsupportedValue(invalidHostnameMsg), - Routes: map[RouteKey]*L7Route{}, - SupportedKinds: []v1.RouteGroupKind{ - {Kind: kinds.HTTPRoute}, - }, + Name: "invalid-hostname", + Source: invalidHostnameListener, + Valid: false, + Conditions: staticConds.NewListenerUnsupportedValue(invalidHostnameMsg), + Routes: map[RouteKey]*L7Route{}, + SupportedKinds: supportedKindsForListeners, }, { - Name: "invalid-https-hostname", - Source: invalidHTTPSHostnameListener, - Valid: false, - Conditions: staticConds.NewListenerUnsupportedValue(invalidHostnameMsg), - Routes: map[RouteKey]*L7Route{}, - SupportedKinds: []v1.RouteGroupKind{ - {Kind: kinds.HTTPRoute}, - }, + Name: "invalid-https-hostname", + Source: invalidHTTPSHostnameListener, + Valid: false, + Conditions: staticConds.NewListenerUnsupportedValue(invalidHostnameMsg), + Routes: map[RouteKey]*L7Route{}, + SupportedKinds: supportedKindsForListeners, }, }, Valid: true, @@ -666,9 +646,7 @@ func TestBuildGateway(t *testing.T) { Conditions: staticConds.NewListenerInvalidCertificateRef( `tls.certificateRefs[0]: Invalid value: test/does-not-exist: secret does not exist`, ), - SupportedKinds: []v1.RouteGroupKind{ - {Kind: kinds.HTTPRoute}, - }, + SupportedKinds: supportedKindsForListeners, }, }, Valid: true, @@ -695,34 +673,28 @@ func TestBuildGateway(t *testing.T) { Source: getLastCreatedGetaway(), Listeners: []*Listener{ { - Name: "foo-80-1", - Source: foo80Listener1, - Valid: true, - Attachable: true, - Routes: map[RouteKey]*L7Route{}, - SupportedKinds: []v1.RouteGroupKind{ - {Kind: kinds.HTTPRoute}, - }, + Name: "foo-80-1", + Source: foo80Listener1, + Valid: true, + Attachable: true, + Routes: map[RouteKey]*L7Route{}, + SupportedKinds: supportedKindsForListeners, }, { - Name: "foo-8080", - Source: foo8080Listener, - Valid: true, - Attachable: true, - Routes: map[RouteKey]*L7Route{}, - SupportedKinds: []v1.RouteGroupKind{ - {Kind: kinds.HTTPRoute}, - }, + Name: "foo-8080", + Source: foo8080Listener, + Valid: true, + Attachable: true, + Routes: map[RouteKey]*L7Route{}, + SupportedKinds: supportedKindsForListeners, }, { - Name: "foo-8081", - Source: foo8081Listener, - Valid: true, - Attachable: true, - Routes: map[RouteKey]*L7Route{}, - SupportedKinds: []v1.RouteGroupKind{ - {Kind: kinds.HTTPRoute}, - }, + Name: "foo-8081", + Source: foo8081Listener, + Valid: true, + Attachable: true, + Routes: map[RouteKey]*L7Route{}, + SupportedKinds: supportedKindsForListeners, }, { Name: "foo-443-https-1", @@ -731,9 +703,7 @@ func TestBuildGateway(t *testing.T) { Attachable: true, Routes: map[RouteKey]*L7Route{}, ResolvedSecret: helpers.GetPointer(client.ObjectKeyFromObject(secretSameNs)), - SupportedKinds: []v1.RouteGroupKind{ - {Kind: kinds.HTTPRoute}, - }, + SupportedKinds: supportedKindsForListeners, }, { Name: "foo-8443-https", @@ -742,19 +712,15 @@ func TestBuildGateway(t *testing.T) { Attachable: true, Routes: map[RouteKey]*L7Route{}, ResolvedSecret: helpers.GetPointer(client.ObjectKeyFromObject(secretSameNs)), - SupportedKinds: []v1.RouteGroupKind{ - {Kind: kinds.HTTPRoute}, - }, + SupportedKinds: supportedKindsForListeners, }, { - Name: "bar-80", - Source: bar80Listener, - Valid: true, - Attachable: true, - Routes: map[RouteKey]*L7Route{}, - SupportedKinds: []v1.RouteGroupKind{ - {Kind: kinds.HTTPRoute}, - }, + Name: "bar-80", + Source: bar80Listener, + Valid: true, + Attachable: true, + Routes: map[RouteKey]*L7Route{}, + SupportedKinds: supportedKindsForListeners, }, { Name: "bar-443-https", @@ -763,9 +729,7 @@ func TestBuildGateway(t *testing.T) { Attachable: true, Routes: map[RouteKey]*L7Route{}, ResolvedSecret: helpers.GetPointer(client.ObjectKeyFromObject(secretSameNs)), - SupportedKinds: []v1.RouteGroupKind{ - {Kind: kinds.HTTPRoute}, - }, + SupportedKinds: supportedKindsForListeners, }, { Name: "bar-8443-https", @@ -774,9 +738,7 @@ func TestBuildGateway(t *testing.T) { Attachable: true, Routes: map[RouteKey]*L7Route{}, ResolvedSecret: helpers.GetPointer(client.ObjectKeyFromObject(secretSameNs)), - SupportedKinds: []v1.RouteGroupKind{ - {Kind: kinds.HTTPRoute}, - }, + SupportedKinds: supportedKindsForListeners, }, }, Valid: true, @@ -801,37 +763,31 @@ func TestBuildGateway(t *testing.T) { Source: getLastCreatedGetaway(), Listeners: []*Listener{ { - Name: "foo-80-1", - Source: foo80Listener1, - Valid: false, - Attachable: true, - Routes: map[RouteKey]*L7Route{}, - Conditions: staticConds.NewListenerProtocolConflict(conflict80PortMsg), - SupportedKinds: []v1.RouteGroupKind{ - {Kind: kinds.HTTPRoute}, - }, + Name: "foo-80-1", + Source: foo80Listener1, + Valid: false, + Attachable: true, + Routes: map[RouteKey]*L7Route{}, + Conditions: staticConds.NewListenerProtocolConflict(conflict80PortMsg), + SupportedKinds: supportedKindsForListeners, }, { - Name: "bar-80", - Source: bar80Listener, - Valid: false, - Attachable: true, - Routes: map[RouteKey]*L7Route{}, - Conditions: staticConds.NewListenerProtocolConflict(conflict80PortMsg), - SupportedKinds: []v1.RouteGroupKind{ - {Kind: kinds.HTTPRoute}, - }, + Name: "bar-80", + Source: bar80Listener, + Valid: false, + Attachable: true, + Routes: map[RouteKey]*L7Route{}, + Conditions: staticConds.NewListenerProtocolConflict(conflict80PortMsg), + SupportedKinds: supportedKindsForListeners, }, { - Name: "foo-443", - Source: foo443Listener, - Valid: false, - Attachable: true, - Routes: map[RouteKey]*L7Route{}, - Conditions: staticConds.NewListenerProtocolConflict(conflict443PortMsg), - SupportedKinds: []v1.RouteGroupKind{ - {Kind: kinds.HTTPRoute}, - }, + Name: "foo-443", + Source: foo443Listener, + Valid: false, + Attachable: true, + Routes: map[RouteKey]*L7Route{}, + Conditions: staticConds.NewListenerProtocolConflict(conflict443PortMsg), + SupportedKinds: supportedKindsForListeners, }, { Name: "foo-80-https", @@ -841,9 +797,7 @@ func TestBuildGateway(t *testing.T) { Routes: map[RouteKey]*L7Route{}, Conditions: staticConds.NewListenerProtocolConflict(conflict80PortMsg), ResolvedSecret: helpers.GetPointer(client.ObjectKeyFromObject(secretSameNs)), - SupportedKinds: []v1.RouteGroupKind{ - {Kind: kinds.HTTPRoute}, - }, + SupportedKinds: supportedKindsForListeners, }, { Name: "foo-443-https-1", @@ -853,9 +807,7 @@ func TestBuildGateway(t *testing.T) { Routes: map[RouteKey]*L7Route{}, Conditions: staticConds.NewListenerProtocolConflict(conflict443PortMsg), ResolvedSecret: helpers.GetPointer(client.ObjectKeyFromObject(secretSameNs)), - SupportedKinds: []v1.RouteGroupKind{ - {Kind: kinds.HTTPRoute}, - }, + SupportedKinds: supportedKindsForListeners, }, { Name: "bar-443-https", @@ -865,9 +817,7 @@ func TestBuildGateway(t *testing.T) { Routes: map[RouteKey]*L7Route{}, Conditions: staticConds.NewListenerProtocolConflict(conflict443PortMsg), ResolvedSecret: helpers.GetPointer(client.ObjectKeyFromObject(secretSameNs)), - SupportedKinds: []v1.RouteGroupKind{ - {Kind: kinds.HTTPRoute}, - }, + SupportedKinds: supportedKindsForListeners, }, }, Valid: true, diff --git a/internal/mode/static/state/graph/graph_test.go b/internal/mode/static/state/graph/graph_test.go index 2081fd881c..15d1afde17 100644 --- a/internal/mode/static/state/graph/graph_test.go +++ b/internal/mode/static/state/graph/graph_test.go @@ -534,6 +534,11 @@ func TestBuildGraph(t *testing.T) { }, } + supportedKindsForListeners := []gatewayv1.RouteGroupKind{ + {Kind: gatewayv1.Kind(kinds.HTTPRoute), Group: helpers.GetPointer[gatewayv1.Group](gatewayv1.GroupName)}, + {Kind: gatewayv1.Kind(kinds.GRPCRoute), Group: helpers.GetPointer[gatewayv1.Group](gatewayv1.GroupName)}, + } + createExpectedGraphWithGatewayClass := func(gc *gatewayv1.GatewayClass) *Graph { return &Graph{ GatewayClass: &GatewayClass{ @@ -553,7 +558,7 @@ func TestBuildGraph(t *testing.T) { CreateRouteKey(hr1): routeHR1, CreateRouteKey(gr): routeGR, }, - SupportedKinds: []gatewayv1.RouteGroupKind{{Kind: kinds.HTTPRoute}}, + SupportedKinds: supportedKindsForListeners, AllowedRouteLabelSelector: labels.SelectorFromSet(map[string]string{"app": "allowed"}), }, { @@ -563,7 +568,7 @@ func TestBuildGraph(t *testing.T) { Attachable: true, Routes: map[RouteKey]*L7Route{CreateRouteKey(hr3): routeHR3}, ResolvedSecret: helpers.GetPointer(client.ObjectKeyFromObject(secret)), - SupportedKinds: []gatewayv1.RouteGroupKind{{Kind: kinds.HTTPRoute}}, + SupportedKinds: supportedKindsForListeners, }, }, Valid: true, diff --git a/internal/mode/static/state/graph/route_common.go b/internal/mode/static/state/graph/route_common.go index 8fe32a96c4..f8fedd50d0 100644 --- a/internal/mode/static/state/graph/route_common.go +++ b/internal/mode/static/state/graph/route_common.go @@ -354,7 +354,11 @@ func tryToAttachRouteToListeners( rk := CreateRouteKey(route.Source) bind := func(l *Listener) (allowed, attached bool) { - if !routeAllowedByListener(l, route.Source.GetNamespace(), gw.Source.Namespace, namespaces) { + if !isRouteNamespaceAllowedByListener(l, route.Source.GetNamespace(), gw.Source.Namespace, namespaces) { + return false, false + } + + if !isRouteTypeAllowedByListener(l, route.RouteType) { return false, false } @@ -502,13 +506,14 @@ func GetMoreSpecificHostname(hostname1, hostname2 string) string { return "" } -func routeAllowedByListener( +// isRouteNamespaceAllowedByListener checks if the route namespace is allowed by the listener. +func isRouteNamespaceAllowedByListener( listener *Listener, routeNS, gwNS string, namespaces map[types.NamespacedName]*apiv1.Namespace, ) bool { - if listener.Source.AllowedRoutes != nil { + if listener.Source.AllowedRoutes != nil && listener.Source.AllowedRoutes.Namespaces != nil { switch *listener.Source.AllowedRoutes.Namespaces.From { case v1.NamespacesFromAll: return true @@ -529,6 +534,27 @@ func routeAllowedByListener( return true } +// isRouteKindAllowedByListener checks if the route is allowed to attach to the listener. +func isRouteTypeAllowedByListener(listener *Listener, routeType RouteType) bool { + for _, kind := range listener.SupportedKinds { + if kind.Kind == convertRouteType(routeType) { + return true + } + } + return false +} + +func convertRouteType(routeType RouteType) v1.Kind { + switch routeType { + case RouteTypeHTTP: + return kinds.HTTPRoute + case RouteTypeGRPC: + return kinds.GRPCRoute + default: + panic(fmt.Sprintf("unsupported route type: %s", routeType)) + } +} + func getHostname(h *v1.Hostname) string { if h == nil { return "" diff --git a/internal/mode/static/state/graph/route_common_test.go b/internal/mode/static/state/graph/route_common_test.go index 51f7c268a2..36b32318a5 100644 --- a/internal/mode/static/state/graph/route_common_test.go +++ b/internal/mode/static/state/graph/route_common_test.go @@ -228,10 +228,15 @@ func TestBindRouteToListeners(t *testing.T) { Source: gatewayv1.Listener{ Name: gatewayv1.SectionName(name), Hostname: (*gatewayv1.Hostname)(helpers.GetPointer("foo.example.com")), + Protocol: gatewayv1.HTTPProtocolType, }, Valid: true, Attachable: true, Routes: map[RouteKey]*L7Route{}, + SupportedKinds: []gatewayv1.RouteGroupKind{ + {Kind: gatewayv1.Kind(kinds.HTTPRoute), Group: helpers.GetPointer[gatewayv1.Group](gatewayv1.GroupName)}, + {Kind: gatewayv1.Kind(kinds.GRPCRoute), Group: helpers.GetPointer[gatewayv1.Group](gatewayv1.GroupName)}, + }, } } createModifiedListener := func(name string, m func(*Listener)) *Listener { @@ -262,6 +267,9 @@ func TestBindRouteToListeners(t *testing.T) { Namespace: "test", Name: "hr", }, + TypeMeta: metav1.TypeMeta{ + Kind: "HTTPRoute", + }, Spec: gatewayv1.HTTPRouteSpec{ CommonRouteSpec: gatewayv1.CommonRouteSpec{ ParentRefs: []gatewayv1.ParentReference{ @@ -291,9 +299,9 @@ func TestBindRouteToListeners(t *testing.T) { nil, ) - var normalRoute *L7Route - createNormalRoute := func(gateway *gatewayv1.Gateway) *L7Route { - normalRoute = &L7Route{ + var normalHTTPRoute *L7Route + createNormalHTTPRoute := func(gateway *gatewayv1.Gateway) *L7Route { + normalHTTPRoute = &L7Route{ RouteType: RouteTypeHTTP, Source: hr, Spec: L7RouteSpec{ @@ -309,10 +317,11 @@ func TestBindRouteToListeners(t *testing.T) { }, }, } - return normalRoute + return normalHTTPRoute } - getLastNormalRoute := func() *L7Route { - return normalRoute + + getLastNormalHTTPRoute := func() *L7Route { + return normalHTTPRoute } invalidAttachableRoute1 := &L7Route{ @@ -430,6 +439,62 @@ func TestBindRouteToListeners(t *testing.T) { l.Source.Hostname = helpers.GetPointer[gatewayv1.Hostname]("bar.example.com") }) + createGRPCRouteWithSectionNameAndPort := func( + sectionName *gatewayv1.SectionName, + port *gatewayv1.PortNumber, + ) *gatewayv1.GRPCRoute { + return &gatewayv1.GRPCRoute{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "hr", + }, + TypeMeta: metav1.TypeMeta{ + Kind: "GRPCRoute", + }, + Spec: gatewayv1.GRPCRouteSpec{ + CommonRouteSpec: gatewayv1.CommonRouteSpec{ + ParentRefs: []gatewayv1.ParentReference{ + { + Name: gatewayv1.ObjectName(gw.Name), + SectionName: sectionName, + Port: port, + }, + }, + }, + Hostnames: []gatewayv1.Hostname{ + "foo.example.com", + }, + }, + } + } + + gr := createGRPCRouteWithSectionNameAndPort(helpers.GetPointer[gatewayv1.SectionName]("listener-80-1"), nil) + + var normalGRPCRoute *L7Route + createNormalGRPCRoute := func(gateway *gatewayv1.Gateway) *L7Route { + normalGRPCRoute = &L7Route{ + RouteType: RouteTypeGRPC, + Source: gr, + Spec: L7RouteSpec{ + Hostnames: gr.Spec.Hostnames, + }, + Valid: true, + Attachable: true, + ParentRefs: []ParentRef{ + { + Idx: 0, + Gateway: client.ObjectKeyFromObject(gateway), + SectionName: gr.Spec.ParentRefs[0].SectionName, + }, + }, + } + return normalGRPCRoute + } + + getLastNormalGRPCRoute := func() *L7Route { + return normalGRPCRoute + } + tests := []struct { route *L7Route gateway *Gateway @@ -439,7 +504,7 @@ func TestBindRouteToListeners(t *testing.T) { expectedConditions []conditions.Condition }{ { - route: createNormalRoute(gw), + route: createNormalHTTPRoute(gw), gateway: &Gateway{ Source: gw, Valid: true, @@ -463,7 +528,7 @@ func TestBindRouteToListeners(t *testing.T) { expectedGatewayListeners: []*Listener{ createModifiedListener("listener-80-1", func(l *Listener) { l.Routes = map[RouteKey]*L7Route{ - CreateRouteKey(hr): getLastNormalRoute(), + CreateRouteKey(hr): getLastNormalHTTPRoute(), } }), }, @@ -620,7 +685,7 @@ func TestBindRouteToListeners(t *testing.T) { name: "listener doesn't exist", }, { - route: createNormalRoute(gw), + route: createNormalHTTPRoute(gw), gateway: &Gateway{ Source: gw, Valid: true, @@ -646,7 +711,7 @@ func TestBindRouteToListeners(t *testing.T) { name: "listener isn't valid and attachable", }, { - route: createNormalRoute(gw), + route: createNormalHTTPRoute(gw), gateway: &Gateway{ Source: gw, Valid: true, @@ -720,7 +785,7 @@ func TestBindRouteToListeners(t *testing.T) { name: "route isn't valid", }, { - route: createNormalRoute(gw), + route: createNormalHTTPRoute(gw), gateway: &Gateway{ Source: gw, Valid: false, @@ -746,7 +811,7 @@ func TestBindRouteToListeners(t *testing.T) { name: "invalid gateway", }, { - route: createNormalRoute(gw), + route: createNormalHTTPRoute(gw), gateway: &Gateway{ Source: gw, Valid: true, @@ -773,7 +838,7 @@ func TestBindRouteToListeners(t *testing.T) { createModifiedListener("listener-80-1", func(l *Listener) { l.Valid = false l.Routes = map[RouteKey]*L7Route{ - CreateRouteKey(hr): getLastNormalRoute(), + CreateRouteKey(hr): getLastNormalHTTPRoute(), } }), }, @@ -847,7 +912,7 @@ func TestBindRouteToListeners(t *testing.T) { name: "invalid attachable listener with invalid attachable route", }, { - route: createNormalRoute(gw), + route: createNormalHTTPRoute(gw), gateway: &Gateway{ Source: gw, Valid: true, @@ -889,7 +954,7 @@ func TestBindRouteToListeners(t *testing.T) { name: "route not allowed via labels", }, { - route: createNormalRoute(gw), + route: createNormalHTTPRoute(gw), gateway: &Gateway{ Source: gw, Valid: true, @@ -928,14 +993,14 @@ func TestBindRouteToListeners(t *testing.T) { }, } l.Routes = map[RouteKey]*L7Route{ - CreateRouteKey(hr): getLastNormalRoute(), + CreateRouteKey(hr): getLastNormalHTTPRoute(), } }), }, name: "route allowed via labels", }, { - route: createNormalRoute(gwDiffNamespace), + route: createNormalHTTPRoute(gwDiffNamespace), gateway: &Gateway{ Source: gwDiffNamespace, Valid: true, @@ -973,7 +1038,7 @@ func TestBindRouteToListeners(t *testing.T) { name: "route not allowed via same namespace", }, { - route: createNormalRoute(gw), + route: createNormalHTTPRoute(gw), gateway: &Gateway{ Source: gw, Valid: true, @@ -1008,14 +1073,14 @@ func TestBindRouteToListeners(t *testing.T) { }, } l.Routes = map[RouteKey]*L7Route{ - CreateRouteKey(hr): getLastNormalRoute(), + CreateRouteKey(hr): getLastNormalHTTPRoute(), } }), }, name: "route allowed via same namespace", }, { - route: createNormalRoute(gwDiffNamespace), + route: createNormalHTTPRoute(gwDiffNamespace), gateway: &Gateway{ Source: gwDiffNamespace, Valid: true, @@ -1050,12 +1115,97 @@ func TestBindRouteToListeners(t *testing.T) { }, } l.Routes = map[RouteKey]*L7Route{ - CreateRouteKey(hr): getLastNormalRoute(), + CreateRouteKey(hr): getLastNormalHTTPRoute(), } }), }, name: "route allowed via all namespaces", }, + { + route: createNormalGRPCRoute(gw), + gateway: &Gateway{ + Source: gw, + Valid: true, + Listeners: []*Listener{ + createModifiedListener("listener-80-1", func(l *Listener) { + l.SupportedKinds = []gatewayv1.RouteGroupKind{ + {Kind: gatewayv1.Kind(kinds.HTTPRoute), Group: helpers.GetPointer[gatewayv1.Group](gatewayv1.GroupName)}, + } + l.Routes = map[RouteKey]*L7Route{ + CreateRouteKey(gr): getLastNormalGRPCRoute(), + } + }), + }, + }, + expectedSectionNameRefs: []ParentRef{ + { + Idx: 0, + Gateway: client.ObjectKeyFromObject(gw), + SectionName: gr.Spec.ParentRefs[0].SectionName, + Attachment: &ParentRefAttachmentStatus{ + Attached: false, + FailedCondition: staticConds.NewRouteNotAllowedByListeners(), + AcceptedHostnames: map[string][]string{}, + }, + }, + }, + expectedGatewayListeners: []*Listener{ + createModifiedListener("listener-80-1", func(l *Listener) { + l.SupportedKinds = []gatewayv1.RouteGroupKind{ + {Kind: gatewayv1.Kind(kinds.HTTPRoute), Group: helpers.GetPointer[gatewayv1.Group](gatewayv1.GroupName)}, + } + l.Routes = map[RouteKey]*L7Route{ + CreateRouteKey(gr): getLastNormalGRPCRoute(), + } + }), + }, + name: "grpc route not allowed when listener kind is HTTPRoute", + }, + { + route: createNormalHTTPRoute(gw), + gateway: &Gateway{ + Source: gw, + Valid: true, + Listeners: []*Listener{ + createModifiedListener("listener-80-1", func(l *Listener) { + l.Source.AllowedRoutes = &gatewayv1.AllowedRoutes{ + Kinds: []gatewayv1.RouteGroupKind{ + {Kind: "HTTPRoute"}, + }, + } + l.Routes = map[RouteKey]*L7Route{ + CreateRouteKey(hr): getLastNormalHTTPRoute(), + } + }), + }, + }, + expectedSectionNameRefs: []ParentRef{ + { + Idx: 0, + Gateway: client.ObjectKeyFromObject(gw), + SectionName: hr.Spec.ParentRefs[0].SectionName, + Attachment: &ParentRefAttachmentStatus{ + Attached: true, + AcceptedHostnames: map[string][]string{ + "listener-80-1": {"foo.example.com"}, + }, + }, + }, + }, + expectedGatewayListeners: []*Listener{ + createModifiedListener("listener-80-1", func(l *Listener) { + l.Source.AllowedRoutes = &gatewayv1.AllowedRoutes{ + Kinds: []gatewayv1.RouteGroupKind{ + {Kind: "HTTPRoute"}, + }, + } + l.Routes = map[RouteKey]*L7Route{ + CreateRouteKey(hr): getLastNormalHTTPRoute(), + } + }), + }, + name: "http route allowed when listener kind is HTTPRoute", + }, } namespaces := map[types.NamespacedName]*v1.Namespace{ @@ -1584,3 +1734,61 @@ func TestRouteKeyForKind(t *testing.T) { g.Expect(rk).To(Panic()) } + +func TestAllowedRouteType(t *testing.T) { + test := []struct { + listener *Listener + name string + routeType RouteType + expResult bool + }{ + { + name: "grpcRoute is allowed when listener supports grpcRoute kind", + routeType: RouteTypeGRPC, + listener: &Listener{ + SupportedKinds: []gatewayv1.RouteGroupKind{ + {Kind: kinds.GRPCRoute}, + }, + }, + expResult: true, + }, + { + name: "grpcRoute is allowed when listener supports grpcRoute and httpRoute kind", + routeType: RouteTypeGRPC, + listener: &Listener{ + SupportedKinds: []gatewayv1.RouteGroupKind{ + {Kind: kinds.HTTPRoute}, + {Kind: kinds.GRPCRoute}, + }, + }, + expResult: true, + }, + { + name: "grpcRoute is allowed when listener supports httpRoute kind", + routeType: RouteTypeGRPC, + listener: &Listener{ + SupportedKinds: []gatewayv1.RouteGroupKind{ + {Kind: kinds.HTTPRoute}, + }, + }, + expResult: false, + }, + { + name: "httpRoute not allowed when listener supports grpcRoute kind", + routeType: RouteTypeHTTP, + listener: &Listener{ + SupportedKinds: []gatewayv1.RouteGroupKind{ + {Kind: kinds.GRPCRoute}, + }, + }, + expResult: false, + }, + } + + for _, test := range test { + t.Run(test.name, func(t *testing.T) { + g := NewWithT(t) + g.Expect(isRouteTypeAllowedByListener(test.listener, test.routeType)).To(Equal(test.expResult)) + }) + } +}