diff --git a/internal/gatewayapi/contexts.go b/internal/gatewayapi/contexts.go index 83eb153a94d1..1edf13a72fad 100644 --- a/internal/gatewayapi/contexts.go +++ b/internal/gatewayapi/contexts.go @@ -417,3 +417,41 @@ func (r *RouteParentContext) HasCondition(route RouteContext, condType gwapiv1.R } return false } + +// BackendRefContext represents a generic BackendRef object that can reference Gateway objects. +type BackendRefContext struct { + HTTPBackendRef *gwapiv1.HTTPBackendRef + GRPCBackendRef *v1alpha2.GRPCBackendRef + BackendRef *gwapiv1.BackendRef +} + +// GetBackendRef returns the BackendRef object from BackendRefContext matching route Kind. +func (b *BackendRefContext) GetBackendRef(routeKind gwapiv1.Kind) *gwapiv1.BackendRef { + switch routeKind { + case KindHTTPRoute: + if b.HTTPBackendRef != nil { + return &b.HTTPBackendRef.BackendRef + } + case KindGRPCRoute: + if b.GRPCBackendRef != nil { + return &b.GRPCBackendRef.BackendRef + } + default: + return b.BackendRef + } + return nil +} + +func (b *BackendRefContext) GetHTTPFilters() []gwapiv1.HTTPRouteFilter { + if b.HTTPBackendRef != nil { + return b.HTTPBackendRef.Filters + } + return nil +} + +func (b *BackendRefContext) GetGRPCFilters() []v1alpha2.GRPCRouteFilter { + if b.GRPCBackendRef != nil { + return b.GRPCBackendRef.Filters + } + return nil +} diff --git a/internal/gatewayapi/filters.go b/internal/gatewayapi/filters.go index 73dcf54ca7ff..8958bb78af39 100644 --- a/internal/gatewayapi/filters.go +++ b/internal/gatewayapi/filters.go @@ -709,15 +709,20 @@ func (t *Translator) processRequestMirrorFilter( Weight: &weight, } + backendRefContext := BackendRefContext{ + HTTPBackendRef: &gwapiv1.HTTPBackendRef{ + BackendRef: mirrorBackendRef, + }, + } // This sets the status on the HTTPRoute, should the usage be changed so that the status message reflects that the backendRef is from the filter? filterNs := filterContext.Route.GetNamespace() serviceNamespace := NamespaceDerefOr(mirrorBackend.Namespace, filterNs) - if !t.validateBackendRef(&mirrorBackendRef, filterContext.ParentRef, filterContext.Route, + if !t.validateBackendRef(backendRefContext, filterContext.ParentRef, filterContext.Route, resources, serviceNamespace, KindHTTPRoute) { return } - ds, _ := t.processDestination(mirrorBackendRef, filterContext.ParentRef, filterContext.Route, resources) + ds, _ := t.processDestination(backendRefContext, filterContext.ParentRef, filterContext.Route, resources) newMirror := &ir.RouteDestination{ Name: fmt.Sprintf("%s-mirror-%d", irRouteDestinationName(filterContext.Route, filterContext.RuleIdx), filterIdx), diff --git a/internal/gatewayapi/route.go b/internal/gatewayapi/route.go index e676b73c570b..f2b99db78f62 100644 --- a/internal/gatewayapi/route.go +++ b/internal/gatewayapi/route.go @@ -173,17 +173,11 @@ func (t *Translator) processHTTPRouteRules(httpRoute *HTTPRouteContext, parentRe dstAddrTypeMap := make(map[ir.DestinationAddressType]int) for _, backendRef := range rule.BackendRefs { - if len(backendRef.Filters) > 0 { - parentRef.SetCondition(httpRoute, - gwapiv1.RouteConditionResolvedRefs, - metav1.ConditionFalse, - "UnsupportedRefValue", - "The filters field within BackendRef is not supported", - ) - continue + backendRefContext := BackendRefContext{ + HTTPBackendRef: &backendRef, } - ds, backendWeight := t.processDestination(backendRef.BackendRef, parentRef, httpRoute, resources) + ds, backendWeight := t.processDestination(backendRefContext, parentRef, httpRoute, resources) if !t.EndpointRoutingDisabled && ds != nil && len(ds.Endpoints) > 0 && ds.AddressType != nil { dstAddrTypeMap[*ds.AddressType]++ } @@ -477,16 +471,11 @@ func (t *Translator) processGRPCRouteRules(grpcRoute *GRPCRouteContext, parentRe } for _, backendRef := range rule.BackendRefs { - if len(backendRef.Filters) > 0 { - parentRef.SetCondition(grpcRoute, - gwapiv1.RouteConditionResolvedRefs, - metav1.ConditionFalse, - "UnsupportedRefValue", - "The filters field within BackendRef is not supported", - ) - continue + backendRefContext := BackendRefContext{ + GRPCBackendRef: &backendRef, } - ds, backendWeight := t.processDestination(backendRef.BackendRef, parentRef, grpcRoute, resources) + + ds, backendWeight := t.processDestination(backendRefContext, parentRef, grpcRoute, resources) for _, route := range ruleRoutes { // If the route already has a direct response or redirect configured, then it was from a filter so skip // processing any destinations for this route. @@ -738,7 +727,10 @@ func (t *Translator) processTLSRouteParentRefs(tlsRoute *TLSRouteContext, resour for _, rule := range tlsRoute.Spec.Rules { for _, backendRef := range rule.BackendRefs { backendRef := backendRef - ds, _ := t.processDestination(backendRef, parentRef, tlsRoute, resources) + backendRefContext := BackendRefContext{ + BackendRef: &backendRef, + } + ds, _ := t.processDestination(backendRefContext, parentRef, tlsRoute, resources) if ds != nil { destSettings = append(destSettings, ds) } @@ -876,7 +868,10 @@ func (t *Translator) processUDPRouteParentRefs(udpRoute *UDPRouteContext, resour } backendRef := udpRoute.Spec.Rules[0].BackendRefs[0] - ds, _ := t.processDestination(backendRef, parentRef, udpRoute, resources) + backendRefContext := BackendRefContext{ + BackendRef: &backendRef, + } + ds, _ := t.processDestination(backendRefContext, parentRef, udpRoute, resources) // Skip further processing if route destination is not valid if ds == nil || len(ds.Endpoints) == 0 { continue @@ -1008,7 +1003,10 @@ func (t *Translator) processTCPRouteParentRefs(tcpRoute *TCPRouteContext, resour } backendRef := tcpRoute.Spec.Rules[0].BackendRefs[0] - ds, _ := t.processDestination(backendRef, parentRef, tcpRoute, resources) + backendRefContext := BackendRefContext{ + BackendRef: &backendRef, + } + ds, _ := t.processDestination(backendRefContext, parentRef, tcpRoute, resources) // Skip further processing if route destination is not valid if ds == nil || len(ds.Endpoints) == 0 { continue @@ -1084,20 +1082,19 @@ func (t *Translator) processTCPRouteParentRefs(tcpRoute *TCPRouteContext, resour // processDestination takes a backendRef and translates it into destination setting or sets error statuses and // returns the weight for the backend so that 500 error responses can be returned for invalid backends in // the same proportion as the backend would have otherwise received -func (t *Translator) processDestination(backendRef gwapiv1.BackendRef, +func (t *Translator) processDestination(backendRefContext BackendRefContext, parentRef *RouteParentContext, route RouteContext, resources *Resources) (ds *ir.DestinationSetting, backendWeight uint32) { - + routeType := GetRouteType(route) weight := uint32(1) + backendRef := backendRefContext.GetBackendRef(routeType) if backendRef.Weight != nil { weight = uint32(*backendRef.Weight) } backendNamespace := NamespaceDerefOr(backendRef.Namespace, route.GetNamespace()) - - routeType := GetRouteType(route) - if !t.validateBackendRef(&backendRef, parentRef, route, resources, backendNamespace, routeType) { + if !t.validateBackendRef(backendRefContext, parentRef, route, resources, backendNamespace, routeType) { return nil, weight } diff --git a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-unsupported-filter.out.yaml b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-unsupported-filter.out.yaml index 8f4cd5d718ed..ca8a4298faea 100755 --- a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-unsupported-filter.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-unsupported-filter.out.yaml @@ -114,8 +114,10 @@ xdsIR: port: 10080 routes: - backendWeights: - invalid: 0 + invalid: 1 valid: 0 + directResponse: + statusCode: 500 hostname: '*' name: httproute/default/httproute-1/rule/0/match/0/* pathMatch: diff --git a/internal/gatewayapi/validate.go b/internal/gatewayapi/validate.go index 5c5c50098e10..dc76ceeb68bb 100644 --- a/internal/gatewayapi/validate.go +++ b/internal/gatewayapi/validate.go @@ -22,8 +22,13 @@ import ( egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" ) -func (t *Translator) validateBackendRef(backendRef *gwapiv1a2.BackendRef, parentRef *RouteParentContext, route RouteContext, +func (t *Translator) validateBackendRef(backendRefContext BackendRefContext, parentRef *RouteParentContext, route RouteContext, resources *Resources, backendNamespace string, routeKind gwapiv1.Kind) bool { + if !t.validateBackendRefFilters(backendRefContext, parentRef, route, routeKind) { + return false + } + backendRef := backendRefContext.GetBackendRef(routeKind) + if !t.validateBackendRefGroup(backendRef, parentRef, route) { return false } @@ -80,6 +85,30 @@ func (t *Translator) validateBackendRefKind(backendRef *gwapiv1a2.BackendRef, pa return true } +func (t *Translator) validateBackendRefFilters(backendRef BackendRefContext, parentRef *RouteParentContext, route RouteContext, routeKind gwapiv1.Kind) bool { + var filtersLen int + switch routeKind { + case KindHTTPRoute: + filtersLen = len(backendRef.GetHTTPFilters()) + case KindGRPCRoute: + filtersLen = len(backendRef.GetGRPCFilters()) + default: + return true + } + + if filtersLen > 0 { + parentRef.SetCondition(route, + gwapiv1.RouteConditionResolvedRefs, + metav1.ConditionFalse, + "UnsupportedRefValue", + "The filters field within BackendRef is not supported", + ) + return false + } + + return true +} + func (t *Translator) validateBackendNamespace(backendRef *gwapiv1a2.BackendRef, parentRef *RouteParentContext, route RouteContext, resources *Resources, routeKind gwapiv1.Kind) bool { if backendRef.Namespace != nil && string(*backendRef.Namespace) != "" && string(*backendRef.Namespace) != route.GetNamespace() {