diff --git a/internal/gatewayapi/backendtrafficpolicy.go b/internal/gatewayapi/backendtrafficpolicy.go index 494ef2d3d4..e8545d5a4d 100644 --- a/internal/gatewayapi/backendtrafficpolicy.go +++ b/internal/gatewayapi/backendtrafficpolicy.go @@ -48,7 +48,7 @@ func (t *Translator) ProcessBackendTrafficPolicies(resources *resource.Resources routeMap := map[policyTargetRouteKey]*policyRouteTargetContext{} for _, route := range routes { key := policyTargetRouteKey{ - Kind: string(GetRouteType(route)), + Kind: string(route.GetRouteType()), Name: route.GetName(), Namespace: route.GetNamespace(), } diff --git a/internal/gatewayapi/contexts.go b/internal/gatewayapi/contexts.go index f09c4e4d3b..f9e8fb7d96 100644 --- a/internal/gatewayapi/contexts.go +++ b/internal/gatewayapi/contexts.go @@ -148,140 +148,315 @@ func (l *ListenerContext) SetTLSSecrets(tlsSecrets []*corev1.Secret) { // that can reference Gateway objects. type RouteContext interface { client.Object + GetRouteType() gwapiv1.Kind + HasRuleNames(sectionName gwapiv1.SectionName) bool + GetHostnames() []string + GetParentReferences() []gwapiv1.ParentReference + GetRouteStatus() *gwapiv1.RouteStatus + GetRouteParentContext(forParentRef gwapiv1.ParentReference) *RouteParentContext + SetRouteParentContext(forParentRef gwapiv1.ParentReference, ctx *RouteParentContext) } // HTTPRouteContext wraps an HTTPRoute and provides helper methods for // accessing the route's parents. type HTTPRouteContext struct { - // GatewayControllerName is the name of the Gateway API controller. - GatewayControllerName string - *gwapiv1.HTTPRoute ParentRefs map[gwapiv1.ParentReference]*RouteParentContext } +func (r *HTTPRouteContext) GetRouteType() gwapiv1.Kind { + return resource.KindHTTPRoute +} + +func (r *HTTPRouteContext) HasRuleNames(sectionName gwapiv1.SectionName) bool { + rs := r.Spec.Rules + for _, rule := range rs { + if rule.Name != nil { + if *rule.Name == sectionName { + return true + } + } + } + return false +} + +func (r *HTTPRouteContext) GetHostnames() []string { + hs := r.Spec.Hostnames + hostnames := make([]string, len(hs)) + for i := range hs { + hostnames[i] = string(hs[i]) + } + return hostnames +} + +func (r *HTTPRouteContext) GetParentReferences() []gwapiv1.ParentReference { + return r.Spec.ParentRefs +} + +func (r *HTTPRouteContext) GetRouteStatus() *gwapiv1.RouteStatus { + return &r.Status.RouteStatus +} + +func (r *HTTPRouteContext) GetRouteParentContext(forParentRef gwapiv1.ParentReference) *RouteParentContext { + if r.ParentRefs == nil { + r.ParentRefs = make(map[gwapiv1.ParentReference]*RouteParentContext) + } + return r.ParentRefs[forParentRef] +} + +func (r *HTTPRouteContext) SetRouteParentContext(forParentRef gwapiv1.ParentReference, ctx *RouteParentContext) { + if r.ParentRefs == nil { + r.ParentRefs = make(map[gwapiv1.ParentReference]*RouteParentContext) + } + r.ParentRefs[forParentRef] = ctx +} + // GRPCRouteContext wraps a GRPCRoute and provides helper methods for // accessing the route's parents. type GRPCRouteContext struct { - // GatewayControllerName is the name of the Gateway API controller. - GatewayControllerName string - *gwapiv1.GRPCRoute ParentRefs map[gwapiv1.ParentReference]*RouteParentContext } +func (r *GRPCRouteContext) GetRouteType() gwapiv1.Kind { + return resource.KindGRPCRoute +} + +func (r *GRPCRouteContext) HasRuleNames(sectionName gwapiv1.SectionName) bool { + rs := r.Spec.Rules + for _, rule := range rs { + if rule.Name != nil { + if *rule.Name == sectionName { + return true + } + } + } + return false +} + +func (r *GRPCRouteContext) GetHostnames() []string { + hs := r.Spec.Hostnames + hostnames := make([]string, len(hs)) + for i := range hs { + hostnames[i] = string(hs[i]) + } + return hostnames +} + +func (r *GRPCRouteContext) GetParentReferences() []gwapiv1.ParentReference { + return r.Spec.ParentRefs +} + +func (r *GRPCRouteContext) GetRouteStatus() *gwapiv1.RouteStatus { + return &r.Status.RouteStatus +} + +func (r *GRPCRouteContext) GetRouteParentContext(forParentRef gwapiv1.ParentReference) *RouteParentContext { + if r.ParentRefs == nil { + r.ParentRefs = make(map[gwapiv1.ParentReference]*RouteParentContext) + } + return r.ParentRefs[forParentRef] +} + +func (r *GRPCRouteContext) SetRouteParentContext(forParentRef gwapiv1.ParentReference, ctx *RouteParentContext) { + if r.ParentRefs == nil { + r.ParentRefs = make(map[gwapiv1.ParentReference]*RouteParentContext) + } + r.ParentRefs[forParentRef] = ctx +} + // TLSRouteContext wraps a TLSRoute and provides helper methods for // accessing the route's parents. type TLSRouteContext struct { - // GatewayControllerName is the name of the Gateway API controller. - GatewayControllerName string - *gwapiv1a2.TLSRoute ParentRefs map[gwapiv1.ParentReference]*RouteParentContext } +func (r *TLSRouteContext) GetRouteType() gwapiv1.Kind { + return resource.KindTLSRoute +} + +func (r *TLSRouteContext) HasRuleNames(sectionName gwapiv1.SectionName) bool { + rs := r.Spec.Rules + for _, rule := range rs { + if rule.Name != nil { + if *rule.Name == sectionName { + return true + } + } + } + return false +} + +func (r *TLSRouteContext) GetHostnames() []string { + hs := r.Spec.Hostnames + hostnames := make([]string, len(hs)) + for i := range hs { + hostnames[i] = string(hs[i]) + } + return hostnames +} + +func (r *TLSRouteContext) GetParentReferences() []gwapiv1.ParentReference { + return r.Spec.ParentRefs +} + +func (r *TLSRouteContext) GetRouteStatus() *gwapiv1.RouteStatus { + return &r.Status.RouteStatus +} + +func (r *TLSRouteContext) GetRouteParentContext(forParentRef gwapiv1.ParentReference) *RouteParentContext { + if r.ParentRefs == nil { + r.ParentRefs = make(map[gwapiv1.ParentReference]*RouteParentContext) + } + return r.ParentRefs[forParentRef] +} + +func (r *TLSRouteContext) SetRouteParentContext(forParentRef gwapiv1.ParentReference, ctx *RouteParentContext) { + if r.ParentRefs == nil { + r.ParentRefs = make(map[gwapiv1.ParentReference]*RouteParentContext) + } + r.ParentRefs[forParentRef] = ctx +} + // UDPRouteContext wraps a UDPRoute and provides helper methods for // accessing the route's parents. type UDPRouteContext struct { - // GatewayControllerName is the name of the Gateway API controller. - GatewayControllerName string - *gwapiv1a2.UDPRoute ParentRefs map[gwapiv1.ParentReference]*RouteParentContext } +func (r *UDPRouteContext) GetRouteType() gwapiv1.Kind { + return resource.KindUDPRoute +} + +func (r *UDPRouteContext) HasRuleNames(sectionName gwapiv1.SectionName) bool { + rs := r.Spec.Rules + for _, rule := range rs { + if rule.Name != nil { + if *rule.Name == sectionName { + return true + } + } + } + return false +} + +func (r *UDPRouteContext) GetHostnames() []string { + // UDPRoute doesn't have hostnames, return empty slice + return []string{} +} + +func (r *UDPRouteContext) GetParentReferences() []gwapiv1.ParentReference { + return r.Spec.ParentRefs +} + +func (r *UDPRouteContext) GetRouteStatus() *gwapiv1.RouteStatus { + return &r.Status.RouteStatus +} + +func (r *UDPRouteContext) GetRouteParentContext(forParentRef gwapiv1.ParentReference) *RouteParentContext { + if r.ParentRefs == nil { + r.ParentRefs = make(map[gwapiv1.ParentReference]*RouteParentContext) + } + return r.ParentRefs[forParentRef] +} + +func (r *UDPRouteContext) SetRouteParentContext(forParentRef gwapiv1.ParentReference, ctx *RouteParentContext) { + if r.ParentRefs == nil { + r.ParentRefs = make(map[gwapiv1.ParentReference]*RouteParentContext) + } + r.ParentRefs[forParentRef] = ctx +} + +func (r *UDPRouteContext) GetParentRefs() map[gwapiv1.ParentReference]*RouteParentContext { + return r.ParentRefs +} + // TCPRouteContext wraps a TCPRoute and provides helper methods for // accessing the route's parents. type TCPRouteContext struct { - // GatewayControllerName is the name of the Gateway API controller. - GatewayControllerName string - *gwapiv1a2.TCPRoute ParentRefs map[gwapiv1.ParentReference]*RouteParentContext } -// GetRouteType returns the Kind of the Route object, HTTPRoute, -// TLSRoute, TCPRoute, UDPRoute etc. -func GetRouteType(route RouteContext) gwapiv1.Kind { - rv := reflect.ValueOf(route).Elem() - return gwapiv1.Kind(rv.FieldByName("Kind").String()) +func (r *TCPRouteContext) GetRouteType() gwapiv1.Kind { + return resource.KindTCPRoute } -// GetRuleNames returns the rule names targeted by the Route object. -func GetRuleNames(route RouteContext) []gwapiv1.SectionName { - rv := reflect.ValueOf(route).Elem() - - rs := rv.FieldByName("Spec").FieldByName("Rules") - ruleNames := make([]gwapiv1.SectionName, 0, rs.Len()) - for i := 0; i < rs.Len(); i++ { - nameField := rs.Index(i).FieldByName("Name") - if !nameField.IsNil() { - ruleNames = append(ruleNames, nameField.Elem().Interface().(gwapiv1.SectionName)) +func (r *TCPRouteContext) HasRuleNames(sectionName gwapiv1.SectionName) bool { + rs := r.Spec.Rules + for _, rule := range rs { + if rule.Name != nil { + if *rule.Name == sectionName { + return true + } } } - return ruleNames + return false } -// GetHostnames returns the hosts targeted by the Route object. -func GetHostnames(route RouteContext) []string { - rv := reflect.ValueOf(route).Elem() - kind := rv.FieldByName("Kind").String() - if kind == resource.KindTCPRoute || kind == resource.KindUDPRoute { - return nil +func (r *TCPRouteContext) GetHostnames() []string { + // TCPRoute doesn't have hostnames, return empty slice + return []string{} +} + +func (r *TCPRouteContext) GetParentReferences() []gwapiv1.ParentReference { + return r.Spec.ParentRefs +} + +func (r *TCPRouteContext) GetRouteStatus() *gwapiv1.RouteStatus { + return &r.Status.RouteStatus +} + +func (r *TCPRouteContext) GetRouteParentContext(forParentRef gwapiv1.ParentReference) *RouteParentContext { + if r.ParentRefs == nil { + r.ParentRefs = make(map[gwapiv1.ParentReference]*RouteParentContext) } + return r.ParentRefs[forParentRef] +} - hs := rv.FieldByName("Spec").FieldByName("Hostnames") - hostnames := make([]string, hs.Len()) - for i := 0; i < len(hostnames); i++ { - hostnames[i] = hs.Index(i).String() +func (r *TCPRouteContext) SetRouteParentContext(forParentRef gwapiv1.ParentReference, ctx *RouteParentContext) { + if r.ParentRefs == nil { + r.ParentRefs = make(map[gwapiv1.ParentReference]*RouteParentContext) } - return hostnames + r.ParentRefs[forParentRef] = ctx +} + +// GetHostnames returns the hosts targeted by the Route object. +func GetHostnames(route RouteContext) []string { + return route.GetHostnames() } // GetParentReferences returns the ParentReference of the Route object. func GetParentReferences(route RouteContext) []gwapiv1.ParentReference { - rv := reflect.ValueOf(route).Elem() - pr := rv.FieldByName("Spec").FieldByName("ParentRefs") - return pr.Interface().([]gwapiv1.ParentReference) + return route.GetParentReferences() } // GetRouteStatus returns the RouteStatus object associated with the Route. func GetRouteStatus(route RouteContext) *gwapiv1.RouteStatus { - rv := reflect.ValueOf(route).Elem() - rs := rv.FieldByName("Status").FieldByName("RouteStatus").Interface().(gwapiv1.RouteStatus) - return &rs + return route.GetRouteStatus() } // GetRouteParentContext returns RouteParentContext by using the Route objects' ParentReference. // It creates a new RouteParentContext and add a new RouteParentStatus to the Route's Status if the ParentReference is not found. -func GetRouteParentContext(route RouteContext, forParentRef gwapiv1.ParentReference) *RouteParentContext { - rv := reflect.ValueOf(route).Elem() - pr := rv.FieldByName("ParentRefs") - - // If the ParentRefs field is nil, initialize it. - if pr.IsNil() { - mm := reflect.MakeMap(reflect.TypeOf(map[gwapiv1.ParentReference]*RouteParentContext{})) - pr.Set(mm) - } - +func GetRouteParentContext(route RouteContext, forParentRef gwapiv1.ParentReference, controllerName string) *RouteParentContext { // If the RouteParentContext is already in the RouteContext, return it. - if p := pr.MapIndex(reflect.ValueOf(forParentRef)); p.IsValid() && !p.IsZero() { - ctx := p.Interface().(*RouteParentContext) - return ctx + if existingCtx := route.GetRouteParentContext(forParentRef); existingCtx != nil { + return existingCtx } // Verify that the ParentReference is present in the Route.Spec.ParentRefs. // This is just a sanity check, the parentRef should always be present, otherwise it's a programming error. var parentRef *gwapiv1.ParentReference - specParentRefs := rv.FieldByName("Spec").FieldByName("ParentRefs") - for i := 0; i < specParentRefs.Len(); i++ { - p := specParentRefs.Index(i).Interface().(gwapiv1.ParentReference) - if reflect.DeepEqual(p, forParentRef) { + specParentRefs := route.GetParentReferences() + for _, p := range specParentRefs { + if isParentRefEqual(p, forParentRef, route.GetNamespace()) { parentRef = &p break } @@ -292,11 +467,10 @@ func GetRouteParentContext(route RouteContext, forParentRef gwapiv1.ParentRefere // Find the parent in the Route's Status. routeParentStatusIdx := -1 - statusParents := rv.FieldByName("Status").FieldByName("Parents") + routeStatus := route.GetRouteStatus() - for i := 0; i < statusParents.Len(); i++ { - p := statusParents.Index(i).FieldByName("ParentRef").Interface().(gwapiv1.ParentReference) - if isParentRefEqual(p, *parentRef, route.GetNamespace()) { + for i, parent := range routeStatus.Parents { + if isParentRefEqual(parent.ParentRef, *parentRef, route.GetNamespace()) { routeParentStatusIdx = i break } @@ -305,11 +479,11 @@ func GetRouteParentContext(route RouteContext, forParentRef gwapiv1.ParentRefere // If the parent is not found in the Route's Status, create a new RouteParentStatus and add it to the Route's Status. if routeParentStatusIdx == -1 { rParentStatus := gwapiv1a2.RouteParentStatus{ - ControllerName: gwapiv1a2.GatewayController(rv.FieldByName("GatewayControllerName").String()), + ControllerName: gwapiv1a2.GatewayController(controllerName), ParentRef: forParentRef, } - statusParents.Set(reflect.Append(statusParents, reflect.ValueOf(rParentStatus))) - routeParentStatusIdx = statusParents.Len() - 1 + routeStatus.Parents = append(routeStatus.Parents, rParentStatus) + routeParentStatusIdx = len(routeStatus.Parents) - 1 } // Also add the RouteParentContext to the RouteContext. @@ -317,9 +491,22 @@ func GetRouteParentContext(route RouteContext, forParentRef gwapiv1.ParentRefere ParentReference: parentRef, routeParentStatusIdx: routeParentStatusIdx, } - rctx := reflect.ValueOf(ctx) - rctx.Elem().FieldByName(string(GetRouteType(route))).Set(rv.Field(1)) - pr.SetMapIndex(reflect.ValueOf(forParentRef), rctx) + + // Set the appropriate route field based on the route type + switch route.GetRouteType() { + case resource.KindHTTPRoute: + ctx.HTTPRoute = route.(*HTTPRouteContext).HTTPRoute + case resource.KindGRPCRoute: + ctx.GRPCRoute = route.(*GRPCRouteContext).GRPCRoute + case resource.KindTLSRoute: + ctx.TLSRoute = route.(*TLSRouteContext).TLSRoute + case resource.KindTCPRoute: + ctx.TCPRoute = route.(*TCPRouteContext).TCPRoute + case resource.KindUDPRoute: + ctx.UDPRoute = route.(*UDPRouteContext).UDPRoute + } + + route.SetRouteParentContext(forParentRef, ctx) return ctx } diff --git a/internal/gatewayapi/envoyextensionpolicy.go b/internal/gatewayapi/envoyextensionpolicy.go index e942ae6999..a0895aecb4 100644 --- a/internal/gatewayapi/envoyextensionpolicy.go +++ b/internal/gatewayapi/envoyextensionpolicy.go @@ -47,7 +47,7 @@ func (t *Translator) ProcessEnvoyExtensionPolicies(envoyExtensionPolicies []*egv routeMap := map[policyTargetRouteKey]*policyRouteTargetContext{} for _, route := range routes { key := policyTargetRouteKey{ - Kind: string(GetRouteType(route)), + Kind: string(route.GetRouteType()), Name: route.GetName(), Namespace: route.GetNamespace(), } @@ -466,7 +466,7 @@ func (t *Translator) translateEnvoyExtensionPolicyForRoute( prefix := irRoutePrefix(route) parentRefs := GetParentReferences(route) for _, p := range parentRefs { - parentRefCtx := GetRouteParentContext(route, p) + parentRefCtx := GetRouteParentContext(route, p, t.GatewayControllerName) gtwCtx := parentRefCtx.GetGateway() if gtwCtx == nil { continue diff --git a/internal/gatewayapi/helpers.go b/internal/gatewayapi/helpers.go index 782db32810..cc215108f3 100644 --- a/internal/gatewayapi/helpers.go +++ b/internal/gatewayapi/helpers.go @@ -386,7 +386,7 @@ func irListenerPortName(proto ir.ProtocolType, port int32) string { func irRoutePrefix(route RouteContext) string { // add a "/" at the end of the prefix to prevent mismatching routes with the // same prefix. For example, route prefix "/foo/" should not match a route "/foobar". - return fmt.Sprintf("%s/%s/%s/", strings.ToLower(string(GetRouteType(route))), route.GetNamespace(), route.GetName()) + return fmt.Sprintf("%s/%s/%s/", strings.ToLower(string(route.GetRouteType())), route.GetNamespace(), route.GetName()) } func irRouteName(route RouteContext, ruleIdx, matchIdx int) string { @@ -394,7 +394,7 @@ func irRouteName(route RouteContext, ruleIdx, matchIdx int) string { } func irTCPRouteName(route RouteContext) string { - return fmt.Sprintf("%s/%s/%s", strings.ToLower(string(GetRouteType(route))), route.GetNamespace(), route.GetName()) + return fmt.Sprintf("%s/%s/%s", strings.ToLower(string(route.GetRouteType())), route.GetNamespace(), route.GetName()) } func irUDPRouteName(route RouteContext) string { diff --git a/internal/gatewayapi/route.go b/internal/gatewayapi/route.go index 031764753a..62fa944c0f 100644 --- a/internal/gatewayapi/route.go +++ b/internal/gatewayapi/route.go @@ -60,10 +60,7 @@ func (t *Translator) ProcessHTTPRoutes(httpRoutes []*gwapiv1.HTTPRoute, gateways if h == nil { panic("received nil httproute") } - httpRoute := &HTTPRouteContext{ - GatewayControllerName: t.GatewayControllerName, - HTTPRoute: h.DeepCopy(), - } + httpRoute := &HTTPRouteContext{HTTPRoute: h.DeepCopy()} // Find out if this route attaches to one of our Gateway's listeners, // and if so, get the list of listeners that allow it to attach for each @@ -90,10 +87,7 @@ func (t *Translator) ProcessGRPCRoutes(grpcRoutes []*gwapiv1.GRPCRoute, gateways if g == nil { panic("received nil grpcroute") } - grpcRoute := &GRPCRouteContext{ - GatewayControllerName: t.GatewayControllerName, - GRPCRoute: g.DeepCopy(), - } + grpcRoute := &GRPCRouteContext{GRPCRoute: g.DeepCopy()} // Find out if this route attaches to one of our Gateway's listeners, // and if so, get the list of listeners that allow it to attach for each @@ -184,7 +178,7 @@ func (t *Translator) processHTTPRouteRules(httpRoute *HTTPRouteContext, parentRe irRoutes []*ir.HTTPRoute errs = &status.MultiStatusError{} ) - pattern := getStatPattern(httpRoute, parentRef) + pattern := getStatPattern(httpRoute, parentRef, t.GatewayControllerName) // process each HTTPRouteRule, generate a unique Xds IR HTTPRoute per match of the rule for ruleIdx, rule := range httpRoute.Spec.Rules { @@ -638,7 +632,7 @@ func (t *Translator) processGRPCRouteRules(grpcRoute *GRPCRouteContext, parentRe irRoutes []*ir.HTTPRoute errs = &status.MultiStatusError{} ) - pattern := getStatPattern(grpcRoute, parentRef) + pattern := getStatPattern(grpcRoute, parentRef, t.GatewayControllerName) // compute matches, filters, backends for ruleIdx, rule := range grpcRoute.Spec.Rules { @@ -878,7 +872,7 @@ func (t *Translator) processHTTPRouteParentRefListener(route RouteContext, route irListener := xdsIR[irKey].GetHTTPListener(irListenerName(listener)) if irListener != nil { - if GetRouteType(route) == resource.KindGRPCRoute { + if route.GetRouteType() == resource.KindGRPCRoute { irListener.IsHTTP2 = true } irListener.Routes = append(irListener.Routes, perHostRoutes...) @@ -922,10 +916,7 @@ func (t *Translator) ProcessTLSRoutes(tlsRoutes []*gwapiv1a2.TLSRoute, gateways if tls == nil { panic("received nil tlsroute") } - tlsRoute := &TLSRouteContext{ - GatewayControllerName: t.GatewayControllerName, - TLSRoute: tls.DeepCopy(), - } + tlsRoute := &TLSRouteContext{TLSRoute: tls.DeepCopy()} // Find out if this route attaches to one of our Gateway's listeners, // and if so, get the list of listeners that allow it to attach for each @@ -1069,10 +1060,7 @@ func (t *Translator) ProcessUDPRoutes(udpRoutes []*gwapiv1a2.UDPRoute, gateways if u == nil { panic("received nil udproute") } - udpRoute := &UDPRouteContext{ - GatewayControllerName: t.GatewayControllerName, - UDPRoute: u.DeepCopy(), - } + udpRoute := &UDPRouteContext{UDPRoute: u.DeepCopy()} // Find out if this route attaches to one of our Gateway's listeners, // and if so, get the list of listeners that allow it to attach for each @@ -1220,10 +1208,7 @@ func (t *Translator) ProcessTCPRoutes(tcpRoutes []*gwapiv1a2.TCPRoute, gateways if tcp == nil { panic("received nil tcproute") } - tcpRoute := &TCPRouteContext{ - GatewayControllerName: t.GatewayControllerName, - TCPRoute: tcp.DeepCopy(), - } + tcpRoute := &TCPRouteContext{TCPRoute: tcp.DeepCopy()} // Find out if this route attaches to one of our Gateway's listeners, // and if so, get the list of listeners that allow it to attach for each @@ -1379,7 +1364,7 @@ func (t *Translator) processTCPRouteParentRefs(tcpRoute *TCPRouteContext, resour func (t *Translator) processDestination(name string, backendRefContext BackendRefContext, parentRef *RouteParentContext, route RouteContext, resources *resource.Resources, ) (ds *ir.DestinationSetting, unstructuredRef *ir.UnstructuredRef, err status.Error) { - routeType := GetRouteType(route) + routeType := route.GetRouteType() weight := uint32(1) backendRef := GetBackendRef(backendRefContext) if backendRef.Weight != nil { @@ -1403,7 +1388,7 @@ func (t *Translator) processDestination(name string, backendRefContext BackendRe } var envoyProxy *egv1a1.EnvoyProxy - gatewayCtx := GetRouteParentContext(route, *parentRef.ParentReference).GetGateway() + gatewayCtx := GetRouteParentContext(route, *parentRef.ParentReference, t.GatewayControllerName).GetGateway() if gatewayCtx != nil { envoyProxy = gatewayCtx.envoyProxy } @@ -1731,7 +1716,7 @@ func (t *Translator) processAllowedListenersForParentRefs(routeContext RouteCont } relevantRoute = true - parentRefCtx := GetRouteParentContext(routeContext, parentRef) + parentRefCtx := GetRouteParentContext(routeContext, parentRef, t.GatewayControllerName) // Reset conditions since they will be recomputed during translation parentRefCtx.ResetConditions(routeContext) @@ -1750,7 +1735,7 @@ func (t *Translator) processAllowedListenersForParentRefs(routeContext RouteCont var allowedListeners []*ListenerContext for _, listener := range selectedListeners { - acceptedKind := GetRouteType(routeContext) + acceptedKind := routeContext.GetRouteType() if listener.AllowsKind(gwapiv1.RouteGroupKind{Group: GroupPtr(gwapiv1.GroupName), Kind: acceptedKind}) && listener.AllowsNamespace(resources.GetNamespace(routeContext.GetNamespace())) { allowedListeners = append(allowedListeners, listener) @@ -2066,10 +2051,10 @@ func backendAppProtocolToIRAppProtocol(ap egv1a1.AppProtocolType, defaultProtoco } } -func getStatPattern(routeContext RouteContext, parentRef *RouteParentContext) string { +func getStatPattern(routeContext RouteContext, parentRef *RouteParentContext, controllerName string) string { var pattern string var envoyProxy *egv1a1.EnvoyProxy - gatewayCtx := GetRouteParentContext(routeContext, *parentRef.ParentReference).GetGateway() + gatewayCtx := GetRouteParentContext(routeContext, *parentRef.ParentReference, controllerName).GetGateway() if gatewayCtx != nil { envoyProxy = gatewayCtx.envoyProxy } diff --git a/internal/gatewayapi/securitypolicy.go b/internal/gatewayapi/securitypolicy.go index e35127050a..59f7b1a6a3 100644 --- a/internal/gatewayapi/securitypolicy.go +++ b/internal/gatewayapi/securitypolicy.go @@ -64,7 +64,7 @@ func (t *Translator) ProcessSecurityPolicies(securityPolicies []*egv1a1.Security routeMap := map[policyTargetRouteKey]*policyRouteTargetContext{} for _, route := range routes { key := policyTargetRouteKey{ - Kind: string(GetRouteType(route)), + Kind: string(route.GetRouteType()), Name: route.GetName(), Namespace: route.GetNamespace(), } @@ -581,7 +581,7 @@ func (t *Translator) translateSecurityPolicyForRoute( prefix := irRoutePrefix(route) parentRefs := GetParentReferences(route) for _, p := range parentRefs { - parentRefCtx := GetRouteParentContext(route, p) + parentRefCtx := GetRouteParentContext(route, p, t.GatewayControllerName) gtwCtx := parentRefCtx.GetGateway() if gtwCtx == nil { continue diff --git a/internal/gatewayapi/validate.go b/internal/gatewayapi/validate.go index 9bbfc240cd..8a4edaa460 100644 --- a/internal/gatewayapi/validate.go +++ b/internal/gatewayapi/validate.go @@ -1095,14 +1095,7 @@ func validateRouteRuleSectionName( targetKey policyTargetRouteKey, route *policyRouteTargetContext, ) *status.PolicyResolveError { - found := false - for _, name := range GetRuleNames(route.RouteContext) { - if name == sectionName { - found = true - break - } - } - if !found { + if !route.HasRuleNames(sectionName) { message := fmt.Sprintf("No section name %s found for %s %s/%s", string(sectionName), targetKey.Kind, targetKey.Namespace, targetKey.Name) diff --git a/test/gobench/translate_test.go b/test/gobench/translate_test.go new file mode 100644 index 0000000000..7bb02c07df --- /dev/null +++ b/test/gobench/translate_test.go @@ -0,0 +1,226 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package fuzz + +import ( + "fmt" + "strings" + "testing" + + "github.com/envoyproxy/gateway/internal/cmd/egctl" + "github.com/envoyproxy/gateway/internal/gatewayapi/resource" +) + +// Reused YAML snippets. +const ( + baseYAML = `apiVersion: gateway.networking.k8s.io/v1 +kind: GatewayClass +metadata: + name: eg +spec: + controllerName: gateway.envoyproxy.io/gatewayclass-controller +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: eg + namespace: default +spec: + gatewayClassName: eg + listeners: + - name: http + protocol: HTTP + port: 80 +` + backendYAML = `--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: Backend +metadata: + name: provided-backend + namespace: default +spec: + endpoints: + - ip: + address: 0.0.0.0 + port: 8000 +` + grpcRouteYAML = `--- +apiVersion: gateway.networking.k8s.io/v1 +kind: GRPCRoute +metadata: + name: backend + namespace: default +spec: + parentRefs: + - name: eg + sectionName: grpc + hostnames: + - "www.grpc-example.com" + rules: + - matches: + - method: + service: com.example.Things + method: DoThing + headers: + - name: com.example.Header + value: foobar + backendRefs: + - name: provided-backend + port: 9000 +` + httpRouteYAML = `--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: backend + namespace: default +spec: + parentRefs: + - name: eg + hostnames: + - "www.example.com" + rules: + - backendRefs: + - name: provided-backend + port: 8000 +` + udpRouteYAML = `--- +apiVersion: gateway.networking.k8s.io/v1alpha2 +kind: UDPRoute +metadata: + name: backend + namespace: default +spec: + parentRefs: + - name: eg + sectionName: udp + rules: + - backendRefs: + - name: provided-backend + port: 3000 +` +) + +// Helpers for benchmark route generation. +func genHTTPRoutes(n int) string { + var sb strings.Builder + for i := 0; i < n; i++ { + sb.WriteString(fmt.Sprintf(`--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: backend-%d + namespace: default +spec: + parentRefs: + - name: eg + hostnames: + - "www.example-%d.com" + rules: + - backendRefs: + - name: provided-backend + port: 8000 +`, i, i)) + } + return sb.String() +} + +func genGRPCRoutes(n int) string { + var sb strings.Builder + for i := 0; i < n; i++ { + sb.WriteString(fmt.Sprintf(`--- +apiVersion: gateway.networking.k8s.io/v1 +kind: GRPCRoute +metadata: + name: backend-grpc-%d + namespace: default +spec: + parentRefs: + - name: eg + sectionName: grpc + hostnames: + - "www.grpc-%d.example.com" + rules: + - matches: + - method: + service: com.example.Service%d + method: Call + backendRefs: + - name: provided-backend + port: 9000 +`, i, i, i)) + } + return sb.String() +} + +func genUDPRoutes(n int) string { + var sb strings.Builder + for i := 0; i < n; i++ { + sb.WriteString(fmt.Sprintf(`--- +apiVersion: gateway.networking.k8s.io/v1alpha2 +kind: UDPRoute +metadata: + name: backend-udp-%d + namespace: default +spec: + parentRefs: + - name: eg + sectionName: udp + rules: + - backendRefs: + - name: provided-backend + port: %d +`, i, 3000+i)) + } + return sb.String() +} + +// Benchmark cases: small / medium / large. +func BenchmarkGatewayAPItoXDS(b *testing.B) { + type benchCase struct { + name string + yaml string + } + medium := baseYAML + backendYAML + + genHTTPRoutes(10) + + genGRPCRoutes(5) + + genUDPRoutes(2) + large := baseYAML + backendYAML + + genHTTPRoutes(100) + + genGRPCRoutes(50) + + genUDPRoutes(10) + + cases := []benchCase{ + { + name: "small", + yaml: baseYAML + httpRouteYAML + backendYAML, + }, + { + name: "medium", + yaml: medium, + }, + { + name: "large", + yaml: large, + }, + } + + for _, tc := range cases { + b.Run(tc.name, func(b *testing.B) { + rs, err := resource.LoadResourcesFromYAMLBytes([]byte(tc.yaml), true) + if err != nil { + b.Fatalf("load: %v", err) + } + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _, err = egctl.TranslateGatewayAPIToXds("default", "cluster.local", "all", rs) + if err != nil && strings.Contains(err.Error(), "failed to translate xds") { + b.Fatalf("%v", err) + } + } + }) + } +}