diff --git a/api/v1alpha1/envoyextensionypolicy_types.go b/api/v1alpha1/envoyextensionypolicy_types.go index 4fadb982f3..aae5f5aa00 100644 --- a/api/v1alpha1/envoyextensionypolicy_types.go +++ b/api/v1alpha1/envoyextensionypolicy_types.go @@ -38,9 +38,9 @@ type EnvoyExtensionPolicy struct { // // +kubebuilder:validation:XValidation:rule="(has(self.targetRef) && !has(self.targetRefs)) || (!has(self.targetRef) && has(self.targetRefs)) || (has(self.targetSelectors) && self.targetSelectors.size() > 0) ", message="either targetRef or targetRefs must be used" // +kubebuilder:validation:XValidation:rule="has(self.targetRef) ? self.targetRef.group == 'gateway.networking.k8s.io' : true", message="this policy can only have a targetRef.group of gateway.networking.k8s.io" -// +kubebuilder:validation:XValidation:rule="has(self.targetRef) ? self.targetRef.kind in ['Gateway', 'HTTPRoute', 'GRPCRoute', 'UDPRoute', 'TCPRoute', 'TLSRoute'] : true", message="this policy can only have a targetRef.kind of Gateway/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute" +// +kubebuilder:validation:XValidation:rule="has(self.targetRef) ? self.targetRef.kind in ['Gateway', 'ListenerSet', 'HTTPRoute', 'GRPCRoute', 'UDPRoute', 'TCPRoute', 'TLSRoute'] : true", message="this policy can only have a targetRef.kind of Gateway/ListenerSet/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute" // +kubebuilder:validation:XValidation:rule="has(self.targetRefs) ? self.targetRefs.all(ref, ref.group == 'gateway.networking.k8s.io') : true ", message="this policy can only have a targetRefs[*].group of gateway.networking.k8s.io" -// +kubebuilder:validation:XValidation:rule="has(self.targetRefs) ? self.targetRefs.all(ref, ref.kind in ['Gateway', 'HTTPRoute', 'GRPCRoute', 'UDPRoute', 'TCPRoute', 'TLSRoute']) : true ", message="this policy can only have a targetRefs[*].kind of Gateway/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute" +// +kubebuilder:validation:XValidation:rule="has(self.targetRefs) ? self.targetRefs.all(ref, ref.kind in ['Gateway', 'ListenerSet', 'HTTPRoute', 'GRPCRoute', 'UDPRoute', 'TCPRoute', 'TLSRoute']) : true ", message="this policy can only have a targetRefs[*].kind of Gateway/ListenerSet/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute" type EnvoyExtensionPolicySpec struct { PolicyTargetReferences `json:",inline"` diff --git a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml index ced0356581..5bbf41a3d6 100644 --- a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml +++ b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml @@ -2317,16 +2317,17 @@ spec: - message: this policy can only have a targetRef.group of gateway.networking.k8s.io rule: 'has(self.targetRef) ? self.targetRef.group == ''gateway.networking.k8s.io'' : true' - - message: this policy can only have a targetRef.kind of Gateway/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute - rule: 'has(self.targetRef) ? self.targetRef.kind in [''Gateway'', ''HTTPRoute'', - ''GRPCRoute'', ''UDPRoute'', ''TCPRoute'', ''TLSRoute''] : true' + - message: this policy can only have a targetRef.kind of Gateway/ListenerSet/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute + rule: 'has(self.targetRef) ? self.targetRef.kind in [''Gateway'', ''ListenerSet'', + ''HTTPRoute'', ''GRPCRoute'', ''UDPRoute'', ''TCPRoute'', ''TLSRoute''] + : true' - message: this policy can only have a targetRefs[*].group of gateway.networking.k8s.io rule: 'has(self.targetRefs) ? self.targetRefs.all(ref, ref.group == ''gateway.networking.k8s.io'') : true ' - - message: this policy can only have a targetRefs[*].kind of Gateway/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute + - message: this policy can only have a targetRefs[*].kind of Gateway/ListenerSet/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute rule: 'has(self.targetRefs) ? self.targetRefs.all(ref, ref.kind in [''Gateway'', - ''HTTPRoute'', ''GRPCRoute'', ''UDPRoute'', ''TCPRoute'', ''TLSRoute'']) - : true ' + ''ListenerSet'', ''HTTPRoute'', ''GRPCRoute'', ''UDPRoute'', ''TCPRoute'', + ''TLSRoute'']) : true ' status: description: Status defines the current status of EnvoyExtensionPolicy. properties: diff --git a/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml b/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml index af2b874a7b..a2b138ec46 100644 --- a/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml +++ b/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml @@ -2316,16 +2316,17 @@ spec: - message: this policy can only have a targetRef.group of gateway.networking.k8s.io rule: 'has(self.targetRef) ? self.targetRef.group == ''gateway.networking.k8s.io'' : true' - - message: this policy can only have a targetRef.kind of Gateway/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute - rule: 'has(self.targetRef) ? self.targetRef.kind in [''Gateway'', ''HTTPRoute'', - ''GRPCRoute'', ''UDPRoute'', ''TCPRoute'', ''TLSRoute''] : true' + - message: this policy can only have a targetRef.kind of Gateway/ListenerSet/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute + rule: 'has(self.targetRef) ? self.targetRef.kind in [''Gateway'', ''ListenerSet'', + ''HTTPRoute'', ''GRPCRoute'', ''UDPRoute'', ''TCPRoute'', ''TLSRoute''] + : true' - message: this policy can only have a targetRefs[*].group of gateway.networking.k8s.io rule: 'has(self.targetRefs) ? self.targetRefs.all(ref, ref.group == ''gateway.networking.k8s.io'') : true ' - - message: this policy can only have a targetRefs[*].kind of Gateway/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute + - message: this policy can only have a targetRefs[*].kind of Gateway/ListenerSet/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute rule: 'has(self.targetRefs) ? self.targetRefs.all(ref, ref.kind in [''Gateway'', - ''HTTPRoute'', ''GRPCRoute'', ''UDPRoute'', ''TCPRoute'', ''TLSRoute'']) - : true ' + ''ListenerSet'', ''HTTPRoute'', ''GRPCRoute'', ''UDPRoute'', ''TCPRoute'', + ''TLSRoute'']) : true ' status: description: Status defines the current status of EnvoyExtensionPolicy. properties: diff --git a/internal/gatewayapi/envoyextensionpolicy.go b/internal/gatewayapi/envoyextensionpolicy.go index b320adbbe3..a06a2bdfb2 100644 --- a/internal/gatewayapi/envoyextensionpolicy.go +++ b/internal/gatewayapi/envoyextensionpolicy.go @@ -83,7 +83,11 @@ func (t *Translator) ProcessEnvoyExtensionPolicies( // EnvoyExtensionPolicies are already sorted by the provider layer // First build a map out of the routes and gateways for faster lookup since users might have thousands of routes or more. - routeMap := map[policyTargetRouteKey]*policyRouteTargetContext{} + routeMapSize := len(routes) + gatewayMapSize := len(gateways) + listenerSetMapSize := len(resources.ListenerSets) + + routeMap := make(map[policyTargetRouteKey]*policyRouteTargetContext, routeMapSize) for _, route := range routes { key := policyTargetRouteKey{ Kind: string(route.GetRouteType()), @@ -93,23 +97,31 @@ func (t *Translator) ProcessEnvoyExtensionPolicies( routeMap[key] = &policyRouteTargetContext{RouteContext: route} } - gatewayMap := map[types.NamespacedName]*policyGatewayTargetContext{} + gatewayMap := make(map[types.NamespacedName]*policyGatewayTargetContext, gatewayMapSize) for _, gw := range gateways { key := utils.NamespacedName(gw) gatewayMap[key] = &policyGatewayTargetContext{GatewayContext: gw} } - // Map of Gateway to the routes attached to it. - // The routes are grouped by sectionNames of their targetRefs. - gatewayRouteMap := make(map[string]map[string]sets.Set[string]) + listenerSetMap := make(map[types.NamespacedName]*policyListenerSetTargetContext, listenerSetMapSize) + for _, ls := range resources.ListenerSets { + key := utils.NamespacedName(ls) + listenerSetMap[key] = &policyListenerSetTargetContext{ListenerSet: ls} + } handledPolicies := make(map[types.NamespacedName]*egv1a1.EnvoyExtensionPolicy) + // overrides records child scopes whose policies displace policies attached + // to their parent scopes. + overrides := newPolicyScopeGraph() + // Translate // 1. First translate Policies targeting RouteRules // 2. Next translate Policies targeting xRoutes - // 3. Then translate Policies targeting Listeners - // 4. Finally, the policies targeting Gateways + // 3. Then translate Policies targeting ListenerSet Listeners + // 4. Then translate Policies targeting ListenerSets + // 5. Then translate Policies targeting Gateway Listeners + // 6. Finally, the policies targeting Gateways // Process the policies targeting RouteRules for i, currPolicy := range envoyExtensionPolicies { @@ -126,7 +138,7 @@ func (t *Translator) ProcessEnvoyExtensionPolicies( } t.processEnvoyExtensionPolicyForRoute(resources, xdsIR, - routeMap, gatewayRouteMap, policy, currTarget) + routeMap, listenerSetMap, overrides, policy, currTarget) } } } @@ -152,12 +164,64 @@ func (t *Translator) ProcessEnvoyExtensionPolicies( } t.processEnvoyExtensionPolicyForRoute(resources, xdsIR, - routeMap, gatewayRouteMap, policy, currTarget) + routeMap, listenerSetMap, overrides, policy, currTarget) + } + } + } + + // Only run the ListenerSet-specific translation when at least one ListenerSet exists. + // When none are present, no policy can successfully attach to a ListenerSet (the target resolves to + // nil and processing returns early), so these loops would be pure overhead. + if len(resources.ListenerSets) > 0 { + // Process the policies targeting ListenerSet Listeners + for i, currPolicy := range envoyExtensionPolicies { + policyName := utils.NamespacedName(currPolicy) + // Only resolve TargetRefs from targetRefs field since TargetSelectors can't specify sectionName. + targetRefs := resolvePolicyTargetsFromReferences(currPolicy.Spec.PolicyTargetReferences, currPolicy.Namespace) + for _, currTarget := range targetRefs { + if isListenerSetListener(currTarget) { + policy, found := handledPolicies[policyName] + if !found { + policy = envoyExtensionPolicies[i] + res = append(res, policy) + handledPolicies[policyName] = policy + } + + t.processEnvoyExtensionPolicyForListenerSet(resources, xdsIR, + gatewayMap, listenerSetMap, overrides, policy, currTarget) + } + } + } + + // Process the policies targeting ListenerSets + for i, currPolicy := range envoyExtensionPolicies { + policyName := utils.NamespacedName(currPolicy) + targetRefs := resolvePolicyTargets( + currPolicy.Spec.PolicyTargetReferences, + resources.ListenerSets, + resources.ReferenceGrants, + egv1a1.GroupName, + egv1a1.KindEnvoyExtensionPolicy, + currPolicy.Namespace, + t.GetNamespace, + ) + for _, currTarget := range targetRefs { + if isListenerSet(currTarget) { + policy, found := handledPolicies[policyName] + if !found { + policy = envoyExtensionPolicies[i] + res = append(res, policy) + handledPolicies[policyName] = policy + } + + t.processEnvoyExtensionPolicyForListenerSet(resources, xdsIR, + gatewayMap, listenerSetMap, overrides, policy, currTarget) + } } } } - // Process the policies targeting Listeners + // Process the policies targeting Gateway Listeners for i, currPolicy := range envoyExtensionPolicies { policyName := utils.NamespacedName(currPolicy) // Only resolve TargetRefs from targetRefs field since TargetSelectors can't specify sectionName. @@ -172,7 +236,7 @@ func (t *Translator) ProcessEnvoyExtensionPolicies( } t.processEnvoyExtensionPolicyForGateway(resources, xdsIR, - gatewayMap, gatewayRouteMap, policy, currTarget) + gatewayMap, overrides, policy, currTarget) } } } @@ -198,7 +262,7 @@ func (t *Translator) ProcessEnvoyExtensionPolicies( } t.processEnvoyExtensionPolicyForGateway(resources, xdsIR, - gatewayMap, gatewayRouteMap, policy, currTarget) + gatewayMap, overrides, policy, currTarget) } } } @@ -216,7 +280,8 @@ func (t *Translator) processEnvoyExtensionPolicyForRoute( resources *resource.Resources, xdsIR resource.XdsIRMap, routeMap map[policyTargetRouteKey]*policyRouteTargetContext, - gatewayRouteMap map[string]map[string]sets.Set[string], + listenerSetMap map[types.NamespacedName]*policyListenerSetTargetContext, + overrides policyScopeGraph, policy *egv1a1.EnvoyExtensionPolicy, currTarget policyTargetReferenceWithSectionName, ) { @@ -235,37 +300,54 @@ func (t *Translator) processEnvoyExtensionPolicyForRoute( return } - // Find the Gateway that the route belongs to and add it to the - // gatewayRouteMap and ancestor list, which will be used to check - // policy overrides and populate its ancestor status. + // Find the parent resource that the route belongs to and record its + // ancestor status and override relationship. parentRefs := GetManagedParentReferences(targetedRoute) + routeNN := utils.NamespacedName(targetedRoute) + routeAsChildScope := routeScope(routeNN) for _, p := range parentRefs { + parentNamespace := targetedRoute.GetNamespace() + if p.Namespace != nil { + parentNamespace = string(*p.Namespace) + } + parentNN := types.NamespacedName{Namespace: parentNamespace, Name: string(p.Name)} + if p.Kind == nil || *p.Kind == resource.KindGateway { - namespace := targetedRoute.GetNamespace() - if p.Namespace != nil { - namespace = string(*p.Namespace) - } - gwNN := types.NamespacedName{ - Namespace: namespace, - Name: string(p.Name), + // Record the Route under the Gateway scope it attaches to: + // Gateway listener when sectionName is set, otherwise Gateway. + if p.SectionName != nil { + overrides.Add(gatewayListenerScope(parentNN, *p.SectionName), routeAsChildScope) + } else { + overrides.Add(gatewayScope(parentNN), routeAsChildScope) } - key := gwNN.String() - if _, ok := gatewayRouteMap[key]; !ok { - gatewayRouteMap[key] = make(map[string]sets.Set[string]) + // Do need a section name since the policy is targeting to a route + ancestorRef := getAncestorRefForPolicy(parentNN, p.SectionName) + ancestorRefs = append(ancestorRefs, &ancestorRef) + } else if *p.Kind == resource.KindListenerSet { + // The Route attaches through a ListenerSet. Resolve the ListenerSet + // so its parent Gateway can be registered as structural containment; + // the Route relationship itself is recorded under the ListenerSet + // scope below. + lsCtx, ok := listenerSetMap[parentNN] + if !ok { + continue } - listenerRouteMap := gatewayRouteMap[key] - sectionName := "" - if p.SectionName != nil { - sectionName = string(*p.SectionName) + parentGwNN := types.NamespacedName{ + Name: string(lsCtx.Spec.ParentRef.Name), + Namespace: NamespaceDerefOr(lsCtx.Spec.ParentRef.Namespace, lsCtx.Namespace), } - if _, ok := listenerRouteMap[sectionName]; !ok { - listenerRouteMap[sectionName] = make(sets.Set[string]) + overrides.RegisterListenerSet(parentNN, parentGwNN) + + if p.SectionName != nil { + overrides.Add(listenerSetListenerScope(parentNN, *p.SectionName), routeAsChildScope) + } else { + overrides.Add(listenerSetScope(parentNN), routeAsChildScope) } - listenerRouteMap[sectionName].Insert(utils.NamespacedName(targetedRoute).String()) - // Do need a section name since the policy is targeting to a route - ancestorRef := getAncestorRefForPolicy(gwNN, p.SectionName) + // ListenerSet-attached Route policies report status against the + // ListenerSet itself. + ancestorRef := getAncestorRefForListenerSetPolicy(parentNN, p.SectionName) ancestorRefs = append(ancestorRefs, &ancestorRef) } } @@ -319,11 +401,105 @@ func (t *Translator) processEnvoyExtensionPolicyForRoute( } } +func (t *Translator) processEnvoyExtensionPolicyForListenerSet( + resources *resource.Resources, + xdsIR resource.XdsIRMap, + gatewayMap map[types.NamespacedName]*policyGatewayTargetContext, + listenerSetMap map[types.NamespacedName]*policyListenerSetTargetContext, + overrides policyScopeGraph, + policy *egv1a1.EnvoyExtensionPolicy, + currTarget policyTargetReferenceWithSectionName, +) { + var ( + targeted *gwapiv1.ListenerSet + resolveErr *status.PolicyResolveError + ) + + targeted, resolveErr = resolveEnvoyExtensionPolicyListenerSetTargetRef(currTarget, listenerSetMap) + // Skip if the ListenerSet is not found. The EnvoyExtensionPolicy may be + // reconciled by multiple controllers, and another controller may own it. + if targeted == nil { + return + } + + parentGatewayNN := types.NamespacedName{ + Name: string(targeted.Spec.ParentRef.Name), + Namespace: NamespaceDerefOr(targeted.Spec.ParentRef.Namespace, targeted.Namespace), + } + gateway, ok := gatewayMap[parentGatewayNN] + if !ok { + return + } + + // Use the ListenerSet itself as the policy ancestor (not the parent Gateway). + listenerSetNN := utils.NamespacedName(targeted) + ancestorRef := getAncestorRefForListenerSetPolicy(listenerSetNN, currTarget.SectionName) + + // Set conditions for resolve error, then skip current ListenerSet + if resolveErr != nil { + status.SetResolveErrorForPolicyAncestor(&policy.Status, + &ancestorRef, + t.GatewayControllerName, + policy.Generation, + resolveErr, + ) + return + } + + // Record the ListenerSet policy under the scope it attaches to. Listener + // policies are children of the ListenerSet scope; ListenerSet-wide policies + // are children of the parent Gateway scope. + if currTarget.SectionName != nil { + overrides.RegisterListenerSet(listenerSetNN, parentGatewayNN) + overrides.Add(listenerSetScope(listenerSetNN), listenerSetListenerScope(listenerSetNN, *currTarget.SectionName)) + } else { + overrides.Add(gatewayScope(parentGatewayNN), listenerSetScope(listenerSetNN)) + } + + if err := t.translateEnvoyExtensionPolicyForListenerSet(policy, currTarget, gateway.GatewayContext, targeted, xdsIR, resources); err != nil { + status.SetTranslationErrorForPolicyAncestor(&policy.Status, + &ancestorRef, + t.GatewayControllerName, + policy.Generation, + status.Error2ConditionMsg(err), + ) + } + + // Set Accepted condition if it is unset + status.SetAcceptedForPolicyAncestor(&policy.Status, &ancestorRef, t.GatewayControllerName, policy.Generation) + + // Check for deprecated fields and set warning if any are found + if deprecatedFields := deprecatedFieldsUsedInEnvoyExtensionPolicy(policy); len(deprecatedFields) > 0 { + status.SetDeprecatedFieldsWarningForPolicyAncestor(&policy.Status, &ancestorRef, t.GatewayControllerName, policy.Generation, deprecatedFields) + } + + // Determine this policy's own scope so we can look up overriding child scopes from the relation maps. + var lsParentScope policyScope + if currTarget.SectionName == nil { + lsParentScope = listenerSetScope(listenerSetNN) + } else { + lsParentScope = listenerSetListenerScope(listenerSetNN, *currTarget.SectionName) + } + + overriddenMessage := formatPolicyScopes(overrides.GetWithDescendants(lsParentScope)) + if overriddenMessage != "" { + status.SetConditionForPolicyAncestor(&policy.Status, + &ancestorRef, + t.GatewayControllerName, + egv1a1.PolicyConditionOverridden, + metav1.ConditionTrue, + egv1a1.PolicyReasonOverridden, + "This policy is being overridden by other envoyExtensionPolicies for "+overriddenMessage, + policy.Generation, + ) + } +} + func (t *Translator) processEnvoyExtensionPolicyForGateway( resources *resource.Resources, xdsIR resource.XdsIRMap, gatewayMap map[types.NamespacedName]*policyGatewayTargetContext, - gatewayRouteMap map[string]map[string]sets.Set[string], + overrides policyScopeGraph, policy *egv1a1.EnvoyExtensionPolicy, currTarget policyTargetReferenceWithSectionName, ) { @@ -357,6 +533,12 @@ func (t *Translator) processEnvoyExtensionPolicyForGateway( return } + // Record this policy as an override of the parent Gateway scope when the + // target is a Gateway listener (sectionName set). + if currTarget.SectionName != nil { + overrides.Add(gatewayScope(gatewayNN), gatewayListenerScope(gatewayNN, *currTarget.SectionName)) + } + // Set conditions for translation error if it got any if err := t.translateEnvoyExtensionPolicyForGateway(policy, currTarget, targetedGateway, xdsIR, resources); err != nil { status.SetTranslationErrorForPolicyAncestor(&policy.Status, @@ -375,9 +557,14 @@ func (t *Translator) processEnvoyExtensionPolicyForGateway( status.SetDeprecatedFieldsWarningForPolicyAncestor(&policy.Status, &ancestorRef, t.GatewayControllerName, policy.Generation, deprecatedFields) } - // Check if this policy is overridden by other policies targeting at route rule, route and listener levels - overriddenTargetsMessage := getOverriddenTargetsMessageForGateway( - gatewayMap[gatewayNN], gatewayRouteMap[gatewayNN.String()], currTarget.SectionName) + // Determine this policy's own scope so we can look up overriding child scopes from the relation maps. + var parentScope policyScope + if currTarget.SectionName == nil { + parentScope = gatewayScope(gatewayNN) + } else { + parentScope = gatewayListenerScope(gatewayNN, *currTarget.SectionName) + } + overriddenTargetsMessage := formatPolicyScopes(overrides.GetWithDescendants(parentScope)) if overriddenTargetsMessage != "" { status.SetConditionForPolicyAncestor(&policy.Status, &ancestorRef, @@ -412,7 +599,7 @@ func resolveEnvoyExtensionPolicyGatewayTargetRef( if err := validateGatewayListenerSectionName( *target.SectionName, key, - gateway.listeners, + gatewayDirectListeners(gateway.GatewayContext), ); err != nil { return gateway.GatewayContext, err } @@ -452,6 +639,65 @@ func resolveEnvoyExtensionPolicyGatewayTargetRef( return gateway.GatewayContext, nil } +func resolveEnvoyExtensionPolicyListenerSetTargetRef( + target policyTargetReferenceWithSectionName, + listenerSets map[types.NamespacedName]*policyListenerSetTargetContext, +) (*gwapiv1.ListenerSet, *status.PolicyResolveError) { + // Find the ListenerSet + key := types.NamespacedName{ + Name: string(target.Name), + Namespace: string(target.Namespace), + } + ls, ok := listenerSets[key] + if !ok { + return nil, nil + } + + // If sectionName is set, make sure its valid + if target.SectionName != nil { + if err := validateListenerSetListenerSectionName( + *target.SectionName, + key, + ls.Spec.Listeners, + ); err != nil { + return ls.ListenerSet, err + } + } + + if target.SectionName == nil { + // Check if another policy targeting the same ListenerSet exists + if ls.attached { + message := fmt.Sprintf("Unable to target ListenerSet %s, another EnvoyExtensionPolicy has already attached to it", + string(target.Name)) + + return ls.ListenerSet, &status.PolicyResolveError{ + Reason: gwapiv1.PolicyReasonConflicted, + Message: message, + } + } + ls.attached = true + } else { + listenerName := string(*target.SectionName) + if ls.attachedToListeners != nil && ls.attachedToListeners.Has(listenerName) { + message := fmt.Sprintf("Unable to target Listener %s/%s, another EnvoyExtensionPolicy has already attached to it", + string(target.Name), listenerName) + + return ls.ListenerSet, &status.PolicyResolveError{ + Reason: gwapiv1.PolicyReasonConflicted, + Message: message, + } + } + if ls.attachedToListeners == nil { + ls.attachedToListeners = make(sets.Set[string]) + } + ls.attachedToListeners.Insert(listenerName) + } + + listenerSets[key] = ls + + return ls.ListenerSet, nil +} + func resolveEnvoyExtensionPolicyRouteTargetRef( target policyTargetReferenceWithSectionName, routes map[policyTargetRouteKey]*policyRouteTargetContext, @@ -631,6 +877,39 @@ func (t *Translator) translateEnvoyExtensionPolicyForGateway( gateway *GatewayContext, xdsIR resource.XdsIRMap, resources *resource.Resources, +) error { + return t.translateEnvoyExtensionPolicyForListeners( + policy, + gateway, + xdsIR, + resources, + gatewayPolicyTargetListeners(gateway, target), + ) +} + +func (t *Translator) translateEnvoyExtensionPolicyForListenerSet( + policy *egv1a1.EnvoyExtensionPolicy, + target policyTargetReferenceWithSectionName, + gateway *GatewayContext, + listenerSet *gwapiv1.ListenerSet, + xdsIR resource.XdsIRMap, + resources *resource.Resources, +) error { + return t.translateEnvoyExtensionPolicyForListeners( + policy, + gateway, + xdsIR, + resources, + listenerSetPolicyTargetListeners(gateway, listenerSet, target), + ) +} + +func (t *Translator) translateEnvoyExtensionPolicyForListeners( + policy *egv1a1.EnvoyExtensionPolicy, + gateway *GatewayContext, + xdsIR resource.XdsIRMap, + resources *resource.Resources, + targetListeners []*ListenerContext, ) error { var ( extProcs []ir.ExtProc @@ -662,22 +941,20 @@ func (t *Translator) translateEnvoyExtensionPolicyForGateway( irKey := t.getIRKey(gateway.Gateway) // Should exist since we've validated this x := xdsIR[irKey] - - policyTarget := irStringKey(policy.Namespace, string(target.Name)) + listenerNames := sets.New[string]() + for _, listener := range targetListeners { + listenerNames.Insert(irListenerName(listener)) + } routesWithDirectResponse := sets.New[string]() for _, http := range x.HTTP { - gatewayName := extractGatewayNameFromListener(http.Name) - if t.MergeGateways && gatewayName != policyTarget { - continue - } - // If specified the sectionName must match listenerName from ir listener metadata. - if target.SectionName != nil && string(*target.SectionName) != http.Metadata.SectionName { + if !listenerNames.Has(http.Name) { continue } - // A Policy targeting the specific scope(xRoute rule, xRoute, Gateway listener) wins over a policy - // targeting a lesser specific scope(Gateway). + // A Policy targeting the specific scope(xRoute rule, xRoute, Gateway + // listener, ListenerSet listener) wins over a policy targeting a lesser + // specific scope(Gateway/ListenerSet). for _, r := range http.Routes { // if already set - there's a specific level policy, so skip if r.EnvoyExtensions != nil { diff --git a/internal/gatewayapi/helpers.go b/internal/gatewayapi/helpers.go index deef7d41b6..fa4f0a39b4 100644 --- a/internal/gatewayapi/helpers.go +++ b/internal/gatewayapi/helpers.go @@ -487,6 +487,54 @@ func irListenerName(listener *ListenerContext) string { return fmt.Sprintf("%s/%s/%s", listener.gateway.Namespace, listener.gateway.Name, listener.Name) } +func gatewayDirectListeners(gtwCtx *GatewayContext) []*ListenerContext { + listeners := make([]*ListenerContext, 0, len(gtwCtx.listeners)) + for _, listener := range gtwCtx.listeners { + if listener.isFromListenerSet() { + continue + } + listeners = append(listeners, listener) + } + return listeners +} + +func gatewayPolicyTargetListeners( + gtwCtx *GatewayContext, + target policyTargetReferenceWithSectionName, +) []*ListenerContext { + listeners := make([]*ListenerContext, 0, len(gtwCtx.listeners)) + for _, listener := range gtwCtx.listeners { + if target.SectionName != nil { + if listener.isFromListenerSet() || listener.Name != *target.SectionName { + continue + } + } + listeners = append(listeners, listener) + } + return listeners +} + +func listenerSetPolicyTargetListeners( + gtwCtx *GatewayContext, + listenerSet *gwapiv1.ListenerSet, + target policyTargetReferenceWithSectionName, +) []*ListenerContext { + listeners := make([]*ListenerContext, 0, len(gtwCtx.listeners)) + for _, listener := range gtwCtx.listeners { + if !listener.isFromListenerSet() { + continue + } + if listener.listenerSet.Namespace != listenerSet.Namespace || listener.listenerSet.Name != listenerSet.Name { + continue + } + if target.SectionName != nil && listener.Name != *target.SectionName { + continue + } + listeners = append(listeners, listener) + } + return listeners +} + func irListenerPortName(proto ir.ProtocolType, port int32) string { return strings.ToLower(fmt.Sprintf("%s-%d", proto, port)) } @@ -1521,42 +1569,6 @@ func getOverriddenTargetsMessageForRoute( return "" } -func getOverriddenTargetsMessageForGateway( - targetContext *policyGatewayTargetContext, - listenerRouteMap map[string]sets.Set[string], - sectionName *gwapiv1.SectionName, -) string { - var listeners, routes []string - if sectionName == nil { - if targetContext != nil { - listeners = targetContext.attachedToListeners.UnsortedList() - } - for _, routeSet := range listenerRouteMap { - routes = append(routes, routeSet.UnsortedList()...) - } - } else if listenerRouteMap != nil { - if routeSet, ok := listenerRouteMap[string(*sectionName)]; ok { - routes = routeSet.UnsortedList() - } - if routeSet, ok := listenerRouteMap[""]; ok { - routes = append(routes, routeSet.UnsortedList()...) - } - } - if len(listeners) > 0 { - sort.Strings(listeners) - if len(routes) > 0 { - sort.Strings(routes) - return fmt.Sprintf("these listeners: %v and these routes: %v", listeners, routes) - } else { - return fmt.Sprintf("these listeners: %v", listeners) - } - } else if len(routes) > 0 { - sort.Strings(routes) - return fmt.Sprintf("these routes: %v", routes) - } - return "" -} - // getOverriddenAndMergedTargetsMessageForGateway generates status messages for policies // indicating which listeners and routes are being overridden or merged. func getOverriddenAndMergedTargetsMessageForGateway( diff --git a/internal/gatewayapi/securitypolicy.go b/internal/gatewayapi/securitypolicy.go index 1d8ca4bc93..30d2f4de86 100644 --- a/internal/gatewayapi/securitypolicy.go +++ b/internal/gatewayapi/securitypolicy.go @@ -1443,54 +1443,6 @@ func (t *Translator) translateSecurityPolicyForRoute( return errs } -func gatewaySecurityPolicyTargetListeners( - gtwCtx *GatewayContext, - target policyTargetReferenceWithSectionName, -) []*ListenerContext { - listeners := make([]*ListenerContext, 0, len(gtwCtx.listeners)) - for _, listener := range gtwCtx.listeners { - if target.SectionName != nil { - if listener.isFromListenerSet() || listener.Name != *target.SectionName { - continue - } - } - listeners = append(listeners, listener) - } - return listeners -} - -func gatewayDirectListeners(gtwCtx *GatewayContext) []*ListenerContext { - listeners := make([]*ListenerContext, 0, len(gtwCtx.listeners)) - for _, listener := range gtwCtx.listeners { - if listener.isFromListenerSet() { - continue - } - listeners = append(listeners, listener) - } - return listeners -} - -func listenerSetSecurityPolicyTargetListeners( - gtwCtx *GatewayContext, - listenerSet *gwapiv1.ListenerSet, - target policyTargetReferenceWithSectionName, -) []*ListenerContext { - listeners := make([]*ListenerContext, 0, len(gtwCtx.listeners)) - for _, listener := range gtwCtx.listeners { - if !listener.isFromListenerSet() { - continue - } - if listener.listenerSet.Namespace != listenerSet.Namespace || listener.listenerSet.Name != listenerSet.Name { - continue - } - if target.SectionName != nil && listener.Name != *target.SectionName { - continue - } - listeners = append(listeners, listener) - } - return listeners -} - func (t *Translator) translateSecurityPolicyForListenerSet( policy *egv1a1.SecurityPolicy, gtwCtx *GatewayContext, @@ -1504,7 +1456,7 @@ func (t *Translator) translateSecurityPolicyForListenerSet( gtwCtx, resources, xdsIR, - listenerSetSecurityPolicyTargetListeners(gtwCtx, listenerSet, target), + listenerSetPolicyTargetListeners(gtwCtx, listenerSet, target), ) } @@ -1520,7 +1472,7 @@ func (t *Translator) translateSecurityPolicyForGateway( gtwCtx, resources, xdsIR, - gatewaySecurityPolicyTargetListeners(gtwCtx, target), + gatewayPolicyTargetListeners(gtwCtx, target), ) } diff --git a/internal/gatewayapi/testdata/envoyextensionpolicy-listenerset-invalid.in.yaml b/internal/gatewayapi/testdata/envoyextensionpolicy-listenerset-invalid.in.yaml new file mode 100644 index 0000000000..f7c6a0e6db --- /dev/null +++ b/internal/gatewayapi/testdata/envoyextensionpolicy-listenerset-invalid.in.yaml @@ -0,0 +1,118 @@ +envoyextensionpolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyExtensionPolicy + metadata: + namespace: envoy-gateway + name: target-listenerset-section + spec: + lua: + - type: Inline + inline: | + function envoy_on_request(request_handle) + request_handle:logInfo("listenerset-section") + end + targetRefs: + - group: gateway.networking.k8s.io + kind: ListenerSet + name: ext-listeners + sectionName: ext-http +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyExtensionPolicy + metadata: + namespace: envoy-gateway + name: target-listenerset-section-conflict + spec: + lua: + - type: Inline + inline: | + function envoy_on_request(request_handle) + request_handle:logInfo("listenerset-section-conflict") + end + targetRefs: + - group: gateway.networking.k8s.io + kind: ListenerSet + name: ext-listeners + sectionName: ext-http +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyExtensionPolicy + metadata: + namespace: envoy-gateway + name: target-listenerset-invalid-section + spec: + lua: + - type: Inline + inline: | + function envoy_on_request(request_handle) + request_handle:logInfo("listenerset-invalid-section") + end + targetRefs: + - group: gateway.networking.k8s.io + kind: ListenerSet + name: ext-listeners + sectionName: missing-listener +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyExtensionPolicy + metadata: + namespace: envoy-gateway + name: target-listenerset + spec: + lua: + - type: Inline + inline: | + function envoy_on_request(request_handle) + request_handle:logInfo("listenerset") + end + targetRefs: + - group: gateway.networking.k8s.io + kind: ListenerSet + name: ext-listeners +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyExtensionPolicy + metadata: + namespace: envoy-gateway + name: target-listenerset-conflict + spec: + lua: + - type: Inline + inline: | + function envoy_on_request(request_handle) + request_handle:logInfo("listenerset-conflict") + end + targetRefs: + - group: gateway.networking.k8s.io + kind: ListenerSet + name: ext-listeners +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: composite-gateway + spec: + gatewayClassName: envoy-gateway-class + allowedListeners: + namespaces: + from: Same + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: Same +listenerSets: +- apiVersion: gateway.networking.k8s.io/v1 + kind: ListenerSet + metadata: + namespace: envoy-gateway + name: ext-listeners + spec: + parentRef: + name: composite-gateway + listeners: + - name: ext-http + protocol: HTTP + port: 8081 + allowedRoutes: + namespaces: + from: Same diff --git a/internal/gatewayapi/testdata/envoyextensionpolicy-listenerset-invalid.out.yaml b/internal/gatewayapi/testdata/envoyextensionpolicy-listenerset-invalid.out.yaml new file mode 100644 index 0000000000..67fff3b192 --- /dev/null +++ b/internal/gatewayapi/testdata/envoyextensionpolicy-listenerset-invalid.out.yaml @@ -0,0 +1,351 @@ +envoyExtensionPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyExtensionPolicy + metadata: + name: target-listenerset-section + namespace: envoy-gateway + spec: + lua: + - inline: | + function envoy_on_request(request_handle) + request_handle:logInfo("listenerset-section") + end + type: Inline + targetRefs: + - group: gateway.networking.k8s.io + kind: ListenerSet + name: ext-listeners + sectionName: ext-http + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: ListenerSet + name: ext-listeners + namespace: envoy-gateway + sectionName: ext-http + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: gateway.envoyproxy.io/gatewayclass-controller +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyExtensionPolicy + metadata: + name: target-listenerset-section-conflict + namespace: envoy-gateway + spec: + lua: + - inline: | + function envoy_on_request(request_handle) + request_handle:logInfo("listenerset-section-conflict") + end + type: Inline + targetRefs: + - group: gateway.networking.k8s.io + kind: ListenerSet + name: ext-listeners + sectionName: ext-http + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: ListenerSet + name: ext-listeners + namespace: envoy-gateway + sectionName: ext-http + conditions: + - lastTransitionTime: null + message: Unable to target Listener ext-listeners/ext-http, another EnvoyExtensionPolicy + has already attached to it + reason: Conflicted + status: "False" + type: Accepted + controllerName: gateway.envoyproxy.io/gatewayclass-controller +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyExtensionPolicy + metadata: + name: target-listenerset-invalid-section + namespace: envoy-gateway + spec: + lua: + - inline: | + function envoy_on_request(request_handle) + request_handle:logInfo("listenerset-invalid-section") + end + type: Inline + targetRefs: + - group: gateway.networking.k8s.io + kind: ListenerSet + name: ext-listeners + sectionName: missing-listener + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: ListenerSet + name: ext-listeners + namespace: envoy-gateway + sectionName: missing-listener + conditions: + - lastTransitionTime: null + message: No section name missing-listener found for ListenerSet envoy-gateway/ext-listeners + reason: TargetNotFound + status: "False" + type: Accepted + controllerName: gateway.envoyproxy.io/gatewayclass-controller +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyExtensionPolicy + metadata: + name: target-listenerset + namespace: envoy-gateway + spec: + lua: + - inline: | + function envoy_on_request(request_handle) + request_handle:logInfo("listenerset") + end + type: Inline + targetRefs: + - group: gateway.networking.k8s.io + kind: ListenerSet + name: ext-listeners + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: ListenerSet + name: ext-listeners + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: 'This policy is being overridden by other envoyExtensionPolicies + for these listenerset listeners: [envoy-gateway/ext-listeners/ext-http]' + reason: Overridden + status: "True" + type: Overridden + controllerName: gateway.envoyproxy.io/gatewayclass-controller +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyExtensionPolicy + metadata: + name: target-listenerset-conflict + namespace: envoy-gateway + spec: + lua: + - inline: | + function envoy_on_request(request_handle) + request_handle:logInfo("listenerset-conflict") + end + type: Inline + targetRefs: + - group: gateway.networking.k8s.io + kind: ListenerSet + name: ext-listeners + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: ListenerSet + name: ext-listeners + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Unable to target ListenerSet ext-listeners, another EnvoyExtensionPolicy + has already attached to it + reason: Conflicted + status: "False" + type: Accepted + controllerName: gateway.envoyproxy.io/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + name: composite-gateway + namespace: envoy-gateway + spec: + allowedListeners: + namespaces: + from: Same + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http + port: 80 + protocol: HTTP + status: + attachedListenerSets: 1 + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +infraIR: + envoy-gateway/composite-gateway: + proxy: + listeners: + - name: envoy-gateway/composite-gateway/http + ports: + - containerPort: 10080 + name: http-80 + protocol: HTTP + servicePort: 80 + - name: envoy-gateway/composite-gateway/envoy-gateway/ext-listeners/ext-http + ports: + - containerPort: 8081 + name: http-8081 + protocol: HTTP + servicePort: 8081 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: composite-gateway + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + ownerReference: + kind: GatewayClass + name: envoy-gateway-class + name: envoy-gateway/composite-gateway + namespace: envoy-gateway-system +listenerSets: +- apiVersion: gateway.networking.k8s.io/v1 + kind: ListenerSet + metadata: + name: ext-listeners + namespace: envoy-gateway + spec: + listeners: + - allowedRoutes: + namespaces: + from: Same + name: ext-http + port: 8081 + protocol: HTTP + parentRef: + name: composite-gateway + status: + conditions: + - lastTransitionTime: null + message: All listeners are accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: All listeners are programmed + reason: Programmed + status: "True" + type: Programmed + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + - lastTransitionTime: null + message: No conflicts detected + reason: NoConflicts + status: "False" + type: Conflicted + name: ext-http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +xdsIR: + envoy-gateway/composite-gateway: + accessLog: + json: + - path: /dev/stdout + globalResources: + proxyServiceCluster: + metadata: + kind: Service + name: envoy-envoy-gateway-composite-gateway-5ac11688 + namespace: envoy-gateway-system + sectionName: "8080" + name: envoy-gateway/composite-gateway + settings: + - addressType: IP + endpoints: + - host: 7.6.5.4 + port: 8080 + zone: zone1 + metadata: + kind: Service + name: envoy-envoy-gateway-composite-gateway-5ac11688 + namespace: envoy-gateway-system + sectionName: "8080" + name: envoy-gateway/composite-gateway + protocol: TCP + http: + - address: 0.0.0.0 + externalPort: 80 + hostnames: + - '*' + metadata: + kind: Gateway + name: composite-gateway + namespace: envoy-gateway + sectionName: http + name: envoy-gateway/composite-gateway/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + - address: 0.0.0.0 + externalPort: 8081 + hostnames: + - '*' + metadata: + kind: Gateway + name: composite-gateway + namespace: envoy-gateway + sectionName: ext-http + name: envoy-gateway/composite-gateway/envoy-gateway/ext-listeners/ext-http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 8081 + readyListener: + address: 0.0.0.0 + ipFamily: IPv4 + path: /ready + port: 19003 diff --git a/internal/gatewayapi/testdata/envoyextensionpolicy-listenerset-same-name-listeners.in.yaml b/internal/gatewayapi/testdata/envoyextensionpolicy-listenerset-same-name-listeners.in.yaml new file mode 100644 index 0000000000..2a8801e962 --- /dev/null +++ b/internal/gatewayapi/testdata/envoyextensionpolicy-listenerset-same-name-listeners.in.yaml @@ -0,0 +1,158 @@ +# this testfile confirms that when Gateway/ListenerSet has a same-name listener and +# EnvoyExtensionPolicy targets the same-name listener, the policy is accepted. +envoyextensionpolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyExtensionPolicy + metadata: + namespace: envoy-gateway + name: target-gateway-shared + spec: + lua: + - type: Inline + inline: | + function envoy_on_request(request_handle) + request_handle:logInfo("gateway-shared") + end + targetRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: composite-gateway + sectionName: shared +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyExtensionPolicy + metadata: + namespace: envoy-gateway + name: target-listenerset-shared + spec: + lua: + - type: Inline + inline: | + function envoy_on_request(request_handle) + request_handle:logInfo("listenerset-shared") + end + targetRefs: + - group: gateway.networking.k8s.io + kind: ListenerSet + name: ext-listeners + sectionName: shared +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyExtensionPolicy + metadata: + namespace: envoy-gateway + name: target-other-listenerset-shared + spec: + lua: + - type: Inline + inline: | + function envoy_on_request(request_handle) + request_handle:logInfo("other-listenerset-shared") + end + targetRefs: + - group: gateway.networking.k8s.io + kind: ListenerSet + name: other-listeners + sectionName: shared +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: route-on-gateway + spec: + parentRefs: + - namespace: envoy-gateway + kind: Gateway + group: gateway.networking.k8s.io + name: composite-gateway + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: route-on-listenerset + spec: + parentRefs: + - namespace: envoy-gateway + kind: ListenerSet + group: gateway.networking.k8s.io + name: ext-listeners + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: route-on-other-listenerset + spec: + parentRefs: + - namespace: envoy-gateway + kind: ListenerSet + group: gateway.networking.k8s.io + name: other-listeners + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: composite-gateway + spec: + gatewayClassName: envoy-gateway-class + allowedListeners: + namespaces: + from: All + listeners: + - name: shared + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +listenerSets: +- apiVersion: gateway.networking.k8s.io/v1 + kind: ListenerSet + metadata: + namespace: envoy-gateway + name: ext-listeners + spec: + parentRef: + name: composite-gateway + listeners: + - name: shared + protocol: HTTP + port: 8081 + allowedRoutes: + namespaces: + from: All +- apiVersion: gateway.networking.k8s.io/v1 + kind: ListenerSet + metadata: + namespace: envoy-gateway + name: other-listeners + spec: + parentRef: + name: composite-gateway + listeners: + - name: shared + protocol: HTTP + port: 8082 + allowedRoutes: + namespaces: + from: All diff --git a/internal/gatewayapi/testdata/envoyextensionpolicy-listenerset-same-name-listeners.out.yaml b/internal/gatewayapi/testdata/envoyextensionpolicy-listenerset-same-name-listeners.out.yaml new file mode 100644 index 0000000000..e20142ef28 --- /dev/null +++ b/internal/gatewayapi/testdata/envoyextensionpolicy-listenerset-same-name-listeners.out.yaml @@ -0,0 +1,588 @@ +envoyExtensionPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyExtensionPolicy + metadata: + name: target-listenerset-shared + namespace: envoy-gateway + spec: + lua: + - inline: | + function envoy_on_request(request_handle) + request_handle:logInfo("listenerset-shared") + end + type: Inline + targetRefs: + - group: gateway.networking.k8s.io + kind: ListenerSet + name: ext-listeners + sectionName: shared + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: ListenerSet + name: ext-listeners + namespace: envoy-gateway + sectionName: shared + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: gateway.envoyproxy.io/gatewayclass-controller +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyExtensionPolicy + metadata: + name: target-other-listenerset-shared + namespace: envoy-gateway + spec: + lua: + - inline: | + function envoy_on_request(request_handle) + request_handle:logInfo("other-listenerset-shared") + end + type: Inline + targetRefs: + - group: gateway.networking.k8s.io + kind: ListenerSet + name: other-listeners + sectionName: shared + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: ListenerSet + name: other-listeners + namespace: envoy-gateway + sectionName: shared + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: gateway.envoyproxy.io/gatewayclass-controller +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyExtensionPolicy + metadata: + name: target-gateway-shared + namespace: envoy-gateway + spec: + lua: + - inline: | + function envoy_on_request(request_handle) + request_handle:logInfo("gateway-shared") + end + type: Inline + targetRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: composite-gateway + sectionName: shared + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: composite-gateway + namespace: envoy-gateway + sectionName: shared + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: gateway.envoyproxy.io/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + name: composite-gateway + namespace: envoy-gateway + spec: + allowedListeners: + namespaces: + from: All + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: shared + port: 80 + protocol: HTTP + status: + attachedListenerSets: 2 + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: shared + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + name: route-on-gateway + namespace: default + spec: + parentRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: composite-gateway + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + group: gateway.networking.k8s.io + kind: Gateway + name: composite-gateway + namespace: envoy-gateway +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + name: route-on-listenerset + namespace: default + spec: + parentRefs: + - group: gateway.networking.k8s.io + kind: ListenerSet + name: ext-listeners + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + group: gateway.networking.k8s.io + kind: ListenerSet + name: ext-listeners + namespace: envoy-gateway +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + name: route-on-other-listenerset + namespace: default + spec: + parentRefs: + - group: gateway.networking.k8s.io + kind: ListenerSet + name: other-listeners + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + group: gateway.networking.k8s.io + kind: ListenerSet + name: other-listeners + namespace: envoy-gateway +infraIR: + envoy-gateway/composite-gateway: + proxy: + listeners: + - name: envoy-gateway/composite-gateway/shared + ports: + - containerPort: 10080 + name: http-80 + protocol: HTTP + servicePort: 80 + - name: envoy-gateway/composite-gateway/envoy-gateway/ext-listeners/shared + ports: + - containerPort: 8081 + name: http-8081 + protocol: HTTP + servicePort: 8081 + - name: envoy-gateway/composite-gateway/envoy-gateway/other-listeners/shared + ports: + - containerPort: 8082 + name: http-8082 + protocol: HTTP + servicePort: 8082 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: composite-gateway + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + ownerReference: + kind: GatewayClass + name: envoy-gateway-class + name: envoy-gateway/composite-gateway + namespace: envoy-gateway-system +listenerSets: +- apiVersion: gateway.networking.k8s.io/v1 + kind: ListenerSet + metadata: + name: ext-listeners + namespace: envoy-gateway + spec: + listeners: + - allowedRoutes: + namespaces: + from: All + name: shared + port: 8081 + protocol: HTTP + parentRef: + name: composite-gateway + status: + conditions: + - lastTransitionTime: null + message: All listeners are accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: All listeners are programmed + reason: Programmed + status: "True" + type: Programmed + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + - lastTransitionTime: null + message: No conflicts detected + reason: NoConflicts + status: "False" + type: Conflicted + name: shared + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1 + kind: ListenerSet + metadata: + name: other-listeners + namespace: envoy-gateway + spec: + listeners: + - allowedRoutes: + namespaces: + from: All + name: shared + port: 8082 + protocol: HTTP + parentRef: + name: composite-gateway + status: + conditions: + - lastTransitionTime: null + message: All listeners are accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: All listeners are programmed + reason: Programmed + status: "True" + type: Programmed + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + - lastTransitionTime: null + message: No conflicts detected + reason: NoConflicts + status: "False" + type: Conflicted + name: shared + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +xdsIR: + envoy-gateway/composite-gateway: + accessLog: + json: + - path: /dev/stdout + globalResources: + proxyServiceCluster: + metadata: + kind: Service + name: envoy-envoy-gateway-composite-gateway-5ac11688 + namespace: envoy-gateway-system + sectionName: "8080" + name: envoy-gateway/composite-gateway + settings: + - addressType: IP + endpoints: + - host: 7.6.5.4 + port: 8080 + zone: zone1 + metadata: + kind: Service + name: envoy-envoy-gateway-composite-gateway-5ac11688 + namespace: envoy-gateway-system + sectionName: "8080" + name: envoy-gateway/composite-gateway + protocol: TCP + http: + - address: 0.0.0.0 + externalPort: 80 + hostnames: + - '*' + metadata: + kind: Gateway + name: composite-gateway + namespace: envoy-gateway + sectionName: shared + name: envoy-gateway/composite-gateway/shared + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - destination: + metadata: + kind: HTTPRoute + name: route-on-gateway + namespace: default + name: httproute/default/route-on-gateway/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + metadata: + kind: Service + name: service-1 + namespace: default + sectionName: "8080" + name: httproute/default/route-on-gateway/rule/0/backend/0 + protocol: HTTP + weight: 1 + envoyExtensions: + luas: + - Code: | + function envoy_on_request(request_handle) + request_handle:logInfo("gateway-shared") + end + FilterContext: null + Name: envoyextensionpolicy/envoy-gateway/target-gateway-shared/lua/0 + hostname: '*' + isHTTP2: false + metadata: + kind: HTTPRoute + name: route-on-gateway + namespace: default + name: httproute/default/route-on-gateway/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / + - address: 0.0.0.0 + externalPort: 8081 + hostnames: + - '*' + metadata: + kind: Gateway + name: composite-gateway + namespace: envoy-gateway + sectionName: shared + name: envoy-gateway/composite-gateway/envoy-gateway/ext-listeners/shared + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 8081 + routes: + - destination: + metadata: + kind: HTTPRoute + name: route-on-listenerset + namespace: default + name: httproute/default/route-on-listenerset/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + metadata: + kind: Service + name: service-1 + namespace: default + sectionName: "8080" + name: httproute/default/route-on-listenerset/rule/0/backend/0 + protocol: HTTP + weight: 1 + envoyExtensions: + luas: + - Code: | + function envoy_on_request(request_handle) + request_handle:logInfo("listenerset-shared") + end + FilterContext: null + Name: envoyextensionpolicy/envoy-gateway/target-listenerset-shared/lua/0 + hostname: '*' + isHTTP2: false + metadata: + kind: HTTPRoute + name: route-on-listenerset + namespace: default + name: httproute/default/route-on-listenerset/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / + - address: 0.0.0.0 + externalPort: 8082 + hostnames: + - '*' + metadata: + kind: Gateway + name: composite-gateway + namespace: envoy-gateway + sectionName: shared + name: envoy-gateway/composite-gateway/envoy-gateway/other-listeners/shared + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 8082 + routes: + - destination: + metadata: + kind: HTTPRoute + name: route-on-other-listenerset + namespace: default + name: httproute/default/route-on-other-listenerset/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + metadata: + kind: Service + name: service-1 + namespace: default + sectionName: "8080" + name: httproute/default/route-on-other-listenerset/rule/0/backend/0 + protocol: HTTP + weight: 1 + envoyExtensions: + luas: + - Code: | + function envoy_on_request(request_handle) + request_handle:logInfo("other-listenerset-shared") + end + FilterContext: null + Name: envoyextensionpolicy/envoy-gateway/target-other-listenerset-shared/lua/0 + hostname: '*' + isHTTP2: false + metadata: + kind: HTTPRoute + name: route-on-other-listenerset + namespace: default + name: httproute/default/route-on-other-listenerset/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / + readyListener: + address: 0.0.0.0 + ipFamily: IPv4 + path: /ready + port: 19003 diff --git a/internal/gatewayapi/testdata/envoyextensionpolicy-listenerset.in.yaml b/internal/gatewayapi/testdata/envoyextensionpolicy-listenerset.in.yaml new file mode 100644 index 0000000000..ce9530ec42 --- /dev/null +++ b/internal/gatewayapi/testdata/envoyextensionpolicy-listenerset.in.yaml @@ -0,0 +1,165 @@ +envoyextensionpolicies: +# this policy status +# - Accepted: true +# - Overridden: true +# - envoy-gateway/ext-listeners/ext-http-1 +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyExtensionPolicy + metadata: + namespace: envoy-gateway + name: target-listenerset + spec: + lua: + - type: Inline + inline: | + function envoy_on_request(request_handle) + request_handle:logInfo("listenerset") + end + targetRefs: + - group: gateway.networking.k8s.io + kind: ListenerSet + name: ext-listeners +# this policy status +# - Accepted: true +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyExtensionPolicy + metadata: + namespace: envoy-gateway + name: target-listenerset-section + spec: + lua: + - type: Inline + inline: | + function envoy_on_request(request_handle) + request_handle:logInfo("listenerset-section") + end + targetRefs: + - group: gateway.networking.k8s.io + kind: ListenerSet + name: ext-listeners + sectionName: ext-http-1 +# this policy status +# - Accepted: true +# - Overridden: true +# - envoy-gateway/ext-listeners/ext-http-1 +# - envoy-gateway/ext-listeners +# - envoy-gateway/composite-gateway/http-8080 +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyExtensionPolicy + metadata: + namespace: envoy-gateway + name: target-gw + spec: + lua: + - type: Inline + inline: | + function envoy_on_request(request_handle) + request_handle:logInfo("gateway") + end + targetRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: composite-gateway +# this policy status +# - Accepted: true +# Not overridden because it is attached to a gw section level, ListenerSet policy is sibling. +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyExtensionPolicy + metadata: + namespace: envoy-gateway + name: target-gw-section + spec: + lua: + - type: Inline + inline: | + function envoy_on_request(request_handle) + request_handle:logInfo("gateway-section") + end + targetRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: composite-gateway + sectionName: http-8080 +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: route-on-gw + spec: + parentRefs: + - namespace: envoy-gateway + kind: Gateway + group: gateway.networking.k8s.io + name: composite-gateway + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: route-on-ls + spec: + parentRefs: + - namespace: envoy-gateway + kind: ListenerSet + group: gateway.networking.k8s.io + name: ext-listeners + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: composite-gateway + spec: + gatewayClassName: envoy-gateway-class + allowedListeners: + namespaces: + from: All + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + - name: http-8080 + protocol: HTTP + port: 8080 + allowedRoutes: + namespaces: + from: All +listenerSets: +- apiVersion: gateway.networking.k8s.io/v1 + kind: ListenerSet + metadata: + namespace: envoy-gateway + name: ext-listeners + spec: + parentRef: + name: composite-gateway + listeners: + - name: ext-http-1 + protocol: HTTP + port: 8081 + allowedRoutes: + namespaces: + from: All + - name: ext-http-2 + protocol: HTTP + port: 8082 + allowedRoutes: + namespaces: + from: All diff --git a/internal/gatewayapi/testdata/envoyextensionpolicy-listenerset.out.yaml b/internal/gatewayapi/testdata/envoyextensionpolicy-listenerset.out.yaml new file mode 100644 index 0000000000..3ec3c1f5b9 --- /dev/null +++ b/internal/gatewayapi/testdata/envoyextensionpolicy-listenerset.out.yaml @@ -0,0 +1,659 @@ +envoyExtensionPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyExtensionPolicy + metadata: + name: target-listenerset-section + namespace: envoy-gateway + spec: + lua: + - inline: | + function envoy_on_request(request_handle) + request_handle:logInfo("listenerset-section") + end + type: Inline + targetRefs: + - group: gateway.networking.k8s.io + kind: ListenerSet + name: ext-listeners + sectionName: ext-http-1 + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: ListenerSet + name: ext-listeners + namespace: envoy-gateway + sectionName: ext-http-1 + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: gateway.envoyproxy.io/gatewayclass-controller +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyExtensionPolicy + metadata: + name: target-listenerset + namespace: envoy-gateway + spec: + lua: + - inline: | + function envoy_on_request(request_handle) + request_handle:logInfo("listenerset") + end + type: Inline + targetRefs: + - group: gateway.networking.k8s.io + kind: ListenerSet + name: ext-listeners + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: ListenerSet + name: ext-listeners + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: 'This policy is being overridden by other envoyExtensionPolicies + for these listenerset listeners: [envoy-gateway/ext-listeners/ext-http-1]' + reason: Overridden + status: "True" + type: Overridden + controllerName: gateway.envoyproxy.io/gatewayclass-controller +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyExtensionPolicy + metadata: + name: target-gw-section + namespace: envoy-gateway + spec: + lua: + - inline: | + function envoy_on_request(request_handle) + request_handle:logInfo("gateway-section") + end + type: Inline + targetRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: composite-gateway + sectionName: http-8080 + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: composite-gateway + namespace: envoy-gateway + sectionName: http-8080 + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: gateway.envoyproxy.io/gatewayclass-controller +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyExtensionPolicy + metadata: + name: target-gw + namespace: envoy-gateway + spec: + lua: + - inline: | + function envoy_on_request(request_handle) + request_handle:logInfo("gateway") + end + type: Inline + targetRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: composite-gateway + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: composite-gateway + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: 'This policy is being overridden by other envoyExtensionPolicies + for these gateway listeners: [envoy-gateway/composite-gateway/http-8080] + and these listenersets: [envoy-gateway/ext-listeners] and these listenerset + listeners: [envoy-gateway/ext-listeners/ext-http-1]' + reason: Overridden + status: "True" + type: Overridden + controllerName: gateway.envoyproxy.io/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + name: composite-gateway + namespace: envoy-gateway + spec: + allowedListeners: + namespaces: + from: All + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + - allowedRoutes: + namespaces: + from: All + name: http-8080 + port: 8080 + protocol: HTTP + status: + attachedListenerSets: 1 + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-8080 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + name: route-on-gw + namespace: default + spec: + parentRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: composite-gateway + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + group: gateway.networking.k8s.io + kind: Gateway + name: composite-gateway + namespace: envoy-gateway +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + name: route-on-ls + namespace: default + spec: + parentRefs: + - group: gateway.networking.k8s.io + kind: ListenerSet + name: ext-listeners + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + group: gateway.networking.k8s.io + kind: ListenerSet + name: ext-listeners + namespace: envoy-gateway +infraIR: + envoy-gateway/composite-gateway: + proxy: + listeners: + - name: envoy-gateway/composite-gateway/http + ports: + - containerPort: 10080 + name: http-80 + protocol: HTTP + servicePort: 80 + - name: envoy-gateway/composite-gateway/http-8080 + ports: + - containerPort: 8080 + name: http-8080 + protocol: HTTP + servicePort: 8080 + - name: envoy-gateway/composite-gateway/envoy-gateway/ext-listeners/ext-http-1 + ports: + - containerPort: 8081 + name: http-8081 + protocol: HTTP + servicePort: 8081 + - name: envoy-gateway/composite-gateway/envoy-gateway/ext-listeners/ext-http-2 + ports: + - containerPort: 8082 + name: http-8082 + protocol: HTTP + servicePort: 8082 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: composite-gateway + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + ownerReference: + kind: GatewayClass + name: envoy-gateway-class + name: envoy-gateway/composite-gateway + namespace: envoy-gateway-system +listenerSets: +- apiVersion: gateway.networking.k8s.io/v1 + kind: ListenerSet + metadata: + name: ext-listeners + namespace: envoy-gateway + spec: + listeners: + - allowedRoutes: + namespaces: + from: All + name: ext-http-1 + port: 8081 + protocol: HTTP + - allowedRoutes: + namespaces: + from: All + name: ext-http-2 + port: 8082 + protocol: HTTP + parentRef: + name: composite-gateway + status: + conditions: + - lastTransitionTime: null + message: All listeners are accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: All listeners are programmed + reason: Programmed + status: "True" + type: Programmed + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + - lastTransitionTime: null + message: No conflicts detected + reason: NoConflicts + status: "False" + type: Conflicted + name: ext-http-1 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + - lastTransitionTime: null + message: No conflicts detected + reason: NoConflicts + status: "False" + type: Conflicted + name: ext-http-2 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +xdsIR: + envoy-gateway/composite-gateway: + accessLog: + json: + - path: /dev/stdout + globalResources: + proxyServiceCluster: + metadata: + kind: Service + name: envoy-envoy-gateway-composite-gateway-5ac11688 + namespace: envoy-gateway-system + sectionName: "8080" + name: envoy-gateway/composite-gateway + settings: + - addressType: IP + endpoints: + - host: 7.6.5.4 + port: 8080 + zone: zone1 + metadata: + kind: Service + name: envoy-envoy-gateway-composite-gateway-5ac11688 + namespace: envoy-gateway-system + sectionName: "8080" + name: envoy-gateway/composite-gateway + protocol: TCP + http: + - address: 0.0.0.0 + externalPort: 80 + hostnames: + - '*' + metadata: + kind: Gateway + name: composite-gateway + namespace: envoy-gateway + sectionName: http + name: envoy-gateway/composite-gateway/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - destination: + metadata: + kind: HTTPRoute + name: route-on-gw + namespace: default + name: httproute/default/route-on-gw/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + metadata: + kind: Service + name: service-1 + namespace: default + sectionName: "8080" + name: httproute/default/route-on-gw/rule/0/backend/0 + protocol: HTTP + weight: 1 + envoyExtensions: + luas: + - Code: | + function envoy_on_request(request_handle) + request_handle:logInfo("gateway") + end + FilterContext: null + Name: envoyextensionpolicy/envoy-gateway/target-gw/lua/0 + hostname: '*' + isHTTP2: false + metadata: + kind: HTTPRoute + name: route-on-gw + namespace: default + name: httproute/default/route-on-gw/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / + - address: 0.0.0.0 + externalPort: 8080 + hostnames: + - '*' + metadata: + kind: Gateway + name: composite-gateway + namespace: envoy-gateway + sectionName: http-8080 + name: envoy-gateway/composite-gateway/http-8080 + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 8080 + routes: + - destination: + metadata: + kind: HTTPRoute + name: route-on-gw + namespace: default + name: httproute/default/route-on-gw/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + metadata: + kind: Service + name: service-1 + namespace: default + sectionName: "8080" + name: httproute/default/route-on-gw/rule/0/backend/0 + protocol: HTTP + weight: 1 + envoyExtensions: + luas: + - Code: | + function envoy_on_request(request_handle) + request_handle:logInfo("gateway-section") + end + FilterContext: null + Name: envoyextensionpolicy/envoy-gateway/target-gw-section/lua/0 + hostname: '*' + isHTTP2: false + metadata: + kind: HTTPRoute + name: route-on-gw + namespace: default + name: httproute/default/route-on-gw/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / + - address: 0.0.0.0 + externalPort: 8081 + hostnames: + - '*' + metadata: + kind: Gateway + name: composite-gateway + namespace: envoy-gateway + sectionName: ext-http-1 + name: envoy-gateway/composite-gateway/envoy-gateway/ext-listeners/ext-http-1 + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 8081 + routes: + - destination: + metadata: + kind: HTTPRoute + name: route-on-ls + namespace: default + name: httproute/default/route-on-ls/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + metadata: + kind: Service + name: service-1 + namespace: default + sectionName: "8080" + name: httproute/default/route-on-ls/rule/0/backend/0 + protocol: HTTP + weight: 1 + envoyExtensions: + luas: + - Code: | + function envoy_on_request(request_handle) + request_handle:logInfo("listenerset-section") + end + FilterContext: null + Name: envoyextensionpolicy/envoy-gateway/target-listenerset-section/lua/0 + hostname: '*' + isHTTP2: false + metadata: + kind: HTTPRoute + name: route-on-ls + namespace: default + name: httproute/default/route-on-ls/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / + - address: 0.0.0.0 + externalPort: 8082 + hostnames: + - '*' + metadata: + kind: Gateway + name: composite-gateway + namespace: envoy-gateway + sectionName: ext-http-2 + name: envoy-gateway/composite-gateway/envoy-gateway/ext-listeners/ext-http-2 + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 8082 + routes: + - destination: + metadata: + kind: HTTPRoute + name: route-on-ls + namespace: default + name: httproute/default/route-on-ls/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + metadata: + kind: Service + name: service-1 + namespace: default + sectionName: "8080" + name: httproute/default/route-on-ls/rule/0/backend/0 + protocol: HTTP + weight: 1 + envoyExtensions: + luas: + - Code: | + function envoy_on_request(request_handle) + request_handle:logInfo("listenerset") + end + FilterContext: null + Name: envoyextensionpolicy/envoy-gateway/target-listenerset/lua/0 + hostname: '*' + isHTTP2: false + metadata: + kind: HTTPRoute + name: route-on-ls + namespace: default + name: httproute/default/route-on-ls/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / + readyListener: + address: 0.0.0.0 + ipFamily: IPv4 + path: /ready + port: 19003 diff --git a/internal/gatewayapi/testdata/envoyextensionpolicy-route-listenerset.in.yaml b/internal/gatewayapi/testdata/envoyextensionpolicy-route-listenerset.in.yaml new file mode 100644 index 0000000000..079e58832e --- /dev/null +++ b/internal/gatewayapi/testdata/envoyextensionpolicy-route-listenerset.in.yaml @@ -0,0 +1,188 @@ +envoyextensionpolicies: +# this policy status +# - Accepted: true +# - Overridden: true +# - default/route-on-ls +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyExtensionPolicy + metadata: + namespace: envoy-gateway + name: target-listenerset-section + spec: + lua: + - type: Inline + inline: | + function envoy_on_request(request_handle) + request_handle:logInfo("listenerset-section") + end + targetRefs: + - group: gateway.networking.k8s.io + kind: ListenerSet + name: ext-listeners + sectionName: ext-http-1 +# this policy status +# - Accepted: true +# - Overridden: true +# - envoy-gateway/ext-listeners/ext-http-1 +# - default/route-on-ls +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyExtensionPolicy + metadata: + namespace: envoy-gateway + name: target-listenerset + spec: + lua: + - type: Inline + inline: | + function envoy_on_request(request_handle) + request_handle:logInfo("listenerset") + end + targetRefs: + - group: gateway.networking.k8s.io + kind: ListenerSet + name: ext-listeners +# this policy status +# - Accepted: true +# - Overridden: true +# - envoy-gateway/composite-gateway/http-8080 +# - envoy-gateway/ext-listeners +# - envoy-gateway/ext-listeners/ext-http-1 +# - default/route-on-ls +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyExtensionPolicy + metadata: + namespace: envoy-gateway + name: target-gw + spec: + lua: + - type: Inline + inline: | + function envoy_on_request(request_handle) + request_handle:logInfo("gateway") + end + targetRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: composite-gateway +# this policy status +# - Accepted: true +# Not overridden because it is attached to a gw section level, ListenerSet policies are sibling scopes. +# A route policy attached through the ListenerSet does not affect a different Gateway section listener. +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyExtensionPolicy + metadata: + namespace: envoy-gateway + name: target-gw-section + spec: + lua: + - type: Inline + inline: | + function envoy_on_request(request_handle) + request_handle:logInfo("gateway-section") + end + targetRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: composite-gateway + sectionName: http-8080 +# this policy status +# - Accepted: true +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyExtensionPolicy + metadata: + namespace: default + name: target-route + spec: + lua: + - type: Inline + inline: | + function envoy_on_request(request_handle) + request_handle:logInfo("route") + end + targetRefs: + - group: gateway.networking.k8s.io + kind: HTTPRoute + name: route-on-ls +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: route-on-gw + spec: + parentRefs: + - namespace: envoy-gateway + kind: Gateway + group: gateway.networking.k8s.io + name: composite-gateway + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: route-on-ls + spec: + parentRefs: + - namespace: envoy-gateway + kind: ListenerSet + group: gateway.networking.k8s.io + name: ext-listeners + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: composite-gateway + spec: + gatewayClassName: envoy-gateway-class + allowedListeners: + namespaces: + from: All + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + - name: http-8080 + protocol: HTTP + port: 8080 + allowedRoutes: + namespaces: + from: All +listenerSets: +- apiVersion: gateway.networking.k8s.io/v1 + kind: ListenerSet + metadata: + namespace: envoy-gateway + name: ext-listeners + spec: + parentRef: + name: composite-gateway + listeners: + - name: ext-http-1 + protocol: HTTP + port: 8081 + allowedRoutes: + namespaces: + from: All + - name: ext-http-2 + protocol: HTTP + port: 8082 + allowedRoutes: + namespaces: + from: All diff --git a/internal/gatewayapi/testdata/envoyextensionpolicy-route-listenerset.out.yaml b/internal/gatewayapi/testdata/envoyextensionpolicy-route-listenerset.out.yaml new file mode 100644 index 0000000000..376026565e --- /dev/null +++ b/internal/gatewayapi/testdata/envoyextensionpolicy-route-listenerset.out.yaml @@ -0,0 +1,696 @@ +envoyExtensionPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyExtensionPolicy + metadata: + name: target-listenerset-section + namespace: envoy-gateway + spec: + lua: + - inline: | + function envoy_on_request(request_handle) + request_handle:logInfo("listenerset-section") + end + type: Inline + targetRefs: + - group: gateway.networking.k8s.io + kind: ListenerSet + name: ext-listeners + sectionName: ext-http-1 + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: ListenerSet + name: ext-listeners + namespace: envoy-gateway + sectionName: ext-http-1 + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: 'This policy is being overridden by other envoyExtensionPolicies + for these routes: [default/route-on-ls]' + reason: Overridden + status: "True" + type: Overridden + controllerName: gateway.envoyproxy.io/gatewayclass-controller +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyExtensionPolicy + metadata: + name: target-listenerset + namespace: envoy-gateway + spec: + lua: + - inline: | + function envoy_on_request(request_handle) + request_handle:logInfo("listenerset") + end + type: Inline + targetRefs: + - group: gateway.networking.k8s.io + kind: ListenerSet + name: ext-listeners + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: ListenerSet + name: ext-listeners + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: 'This policy is being overridden by other envoyExtensionPolicies + for these listenerset listeners: [envoy-gateway/ext-listeners/ext-http-1] + and these routes: [default/route-on-ls]' + reason: Overridden + status: "True" + type: Overridden + controllerName: gateway.envoyproxy.io/gatewayclass-controller +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyExtensionPolicy + metadata: + name: target-route + namespace: default + spec: + lua: + - inline: | + function envoy_on_request(request_handle) + request_handle:logInfo("route") + end + type: Inline + targetRefs: + - group: gateway.networking.k8s.io + kind: HTTPRoute + name: route-on-ls + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: ListenerSet + name: ext-listeners + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: gateway.envoyproxy.io/gatewayclass-controller +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyExtensionPolicy + metadata: + name: target-gw-section + namespace: envoy-gateway + spec: + lua: + - inline: | + function envoy_on_request(request_handle) + request_handle:logInfo("gateway-section") + end + type: Inline + targetRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: composite-gateway + sectionName: http-8080 + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: composite-gateway + namespace: envoy-gateway + sectionName: http-8080 + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: gateway.envoyproxy.io/gatewayclass-controller +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyExtensionPolicy + metadata: + name: target-gw + namespace: envoy-gateway + spec: + lua: + - inline: | + function envoy_on_request(request_handle) + request_handle:logInfo("gateway") + end + type: Inline + targetRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: composite-gateway + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: composite-gateway + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: 'This policy is being overridden by other envoyExtensionPolicies + for these gateway listeners: [envoy-gateway/composite-gateway/http-8080] + and these listenersets: [envoy-gateway/ext-listeners] and these listenerset + listeners: [envoy-gateway/ext-listeners/ext-http-1] and these routes: [default/route-on-ls]' + reason: Overridden + status: "True" + type: Overridden + controllerName: gateway.envoyproxy.io/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + name: composite-gateway + namespace: envoy-gateway + spec: + allowedListeners: + namespaces: + from: All + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + - allowedRoutes: + namespaces: + from: All + name: http-8080 + port: 8080 + protocol: HTTP + status: + attachedListenerSets: 1 + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-8080 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + name: route-on-gw + namespace: default + spec: + parentRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: composite-gateway + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + group: gateway.networking.k8s.io + kind: Gateway + name: composite-gateway + namespace: envoy-gateway +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + name: route-on-ls + namespace: default + spec: + parentRefs: + - group: gateway.networking.k8s.io + kind: ListenerSet + name: ext-listeners + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + group: gateway.networking.k8s.io + kind: ListenerSet + name: ext-listeners + namespace: envoy-gateway +infraIR: + envoy-gateway/composite-gateway: + proxy: + listeners: + - name: envoy-gateway/composite-gateway/http + ports: + - containerPort: 10080 + name: http-80 + protocol: HTTP + servicePort: 80 + - name: envoy-gateway/composite-gateway/http-8080 + ports: + - containerPort: 8080 + name: http-8080 + protocol: HTTP + servicePort: 8080 + - name: envoy-gateway/composite-gateway/envoy-gateway/ext-listeners/ext-http-1 + ports: + - containerPort: 8081 + name: http-8081 + protocol: HTTP + servicePort: 8081 + - name: envoy-gateway/composite-gateway/envoy-gateway/ext-listeners/ext-http-2 + ports: + - containerPort: 8082 + name: http-8082 + protocol: HTTP + servicePort: 8082 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: composite-gateway + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + ownerReference: + kind: GatewayClass + name: envoy-gateway-class + name: envoy-gateway/composite-gateway + namespace: envoy-gateway-system +listenerSets: +- apiVersion: gateway.networking.k8s.io/v1 + kind: ListenerSet + metadata: + name: ext-listeners + namespace: envoy-gateway + spec: + listeners: + - allowedRoutes: + namespaces: + from: All + name: ext-http-1 + port: 8081 + protocol: HTTP + - allowedRoutes: + namespaces: + from: All + name: ext-http-2 + port: 8082 + protocol: HTTP + parentRef: + name: composite-gateway + status: + conditions: + - lastTransitionTime: null + message: All listeners are accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: All listeners are programmed + reason: Programmed + status: "True" + type: Programmed + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + - lastTransitionTime: null + message: No conflicts detected + reason: NoConflicts + status: "False" + type: Conflicted + name: ext-http-1 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + - lastTransitionTime: null + message: No conflicts detected + reason: NoConflicts + status: "False" + type: Conflicted + name: ext-http-2 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +xdsIR: + envoy-gateway/composite-gateway: + accessLog: + json: + - path: /dev/stdout + globalResources: + proxyServiceCluster: + metadata: + kind: Service + name: envoy-envoy-gateway-composite-gateway-5ac11688 + namespace: envoy-gateway-system + sectionName: "8080" + name: envoy-gateway/composite-gateway + settings: + - addressType: IP + endpoints: + - host: 7.6.5.4 + port: 8080 + zone: zone1 + metadata: + kind: Service + name: envoy-envoy-gateway-composite-gateway-5ac11688 + namespace: envoy-gateway-system + sectionName: "8080" + name: envoy-gateway/composite-gateway + protocol: TCP + http: + - address: 0.0.0.0 + externalPort: 80 + hostnames: + - '*' + metadata: + kind: Gateway + name: composite-gateway + namespace: envoy-gateway + sectionName: http + name: envoy-gateway/composite-gateway/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - destination: + metadata: + kind: HTTPRoute + name: route-on-gw + namespace: default + name: httproute/default/route-on-gw/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + metadata: + kind: Service + name: service-1 + namespace: default + sectionName: "8080" + name: httproute/default/route-on-gw/rule/0/backend/0 + protocol: HTTP + weight: 1 + envoyExtensions: + luas: + - Code: | + function envoy_on_request(request_handle) + request_handle:logInfo("gateway") + end + FilterContext: null + Name: envoyextensionpolicy/envoy-gateway/target-gw/lua/0 + hostname: '*' + isHTTP2: false + metadata: + kind: HTTPRoute + name: route-on-gw + namespace: default + name: httproute/default/route-on-gw/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / + - address: 0.0.0.0 + externalPort: 8080 + hostnames: + - '*' + metadata: + kind: Gateway + name: composite-gateway + namespace: envoy-gateway + sectionName: http-8080 + name: envoy-gateway/composite-gateway/http-8080 + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 8080 + routes: + - destination: + metadata: + kind: HTTPRoute + name: route-on-gw + namespace: default + name: httproute/default/route-on-gw/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + metadata: + kind: Service + name: service-1 + namespace: default + sectionName: "8080" + name: httproute/default/route-on-gw/rule/0/backend/0 + protocol: HTTP + weight: 1 + envoyExtensions: + luas: + - Code: | + function envoy_on_request(request_handle) + request_handle:logInfo("gateway-section") + end + FilterContext: null + Name: envoyextensionpolicy/envoy-gateway/target-gw-section/lua/0 + hostname: '*' + isHTTP2: false + metadata: + kind: HTTPRoute + name: route-on-gw + namespace: default + name: httproute/default/route-on-gw/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / + - address: 0.0.0.0 + externalPort: 8081 + hostnames: + - '*' + metadata: + kind: Gateway + name: composite-gateway + namespace: envoy-gateway + sectionName: ext-http-1 + name: envoy-gateway/composite-gateway/envoy-gateway/ext-listeners/ext-http-1 + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 8081 + routes: + - destination: + metadata: + kind: HTTPRoute + name: route-on-ls + namespace: default + name: httproute/default/route-on-ls/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + metadata: + kind: Service + name: service-1 + namespace: default + sectionName: "8080" + name: httproute/default/route-on-ls/rule/0/backend/0 + protocol: HTTP + weight: 1 + envoyExtensions: + luas: + - Code: | + function envoy_on_request(request_handle) + request_handle:logInfo("route") + end + FilterContext: null + Name: envoyextensionpolicy/default/target-route/lua/0 + hostname: '*' + isHTTP2: false + metadata: + kind: HTTPRoute + name: route-on-ls + namespace: default + name: httproute/default/route-on-ls/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / + - address: 0.0.0.0 + externalPort: 8082 + hostnames: + - '*' + metadata: + kind: Gateway + name: composite-gateway + namespace: envoy-gateway + sectionName: ext-http-2 + name: envoy-gateway/composite-gateway/envoy-gateway/ext-listeners/ext-http-2 + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 8082 + routes: + - destination: + metadata: + kind: HTTPRoute + name: route-on-ls + namespace: default + name: httproute/default/route-on-ls/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + metadata: + kind: Service + name: service-1 + namespace: default + sectionName: "8080" + name: httproute/default/route-on-ls/rule/0/backend/0 + protocol: HTTP + weight: 1 + envoyExtensions: + luas: + - Code: | + function envoy_on_request(request_handle) + request_handle:logInfo("route") + end + FilterContext: null + Name: envoyextensionpolicy/default/target-route/lua/0 + hostname: '*' + isHTTP2: false + metadata: + kind: HTTPRoute + name: route-on-ls + namespace: default + name: httproute/default/route-on-ls/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / + readyListener: + address: 0.0.0.0 + ipFamily: IPv4 + path: /ready + port: 19003 diff --git a/internal/gatewayapi/testdata/envoyextensionpolicy-section-name-override.out.yaml b/internal/gatewayapi/testdata/envoyextensionpolicy-section-name-override.out.yaml index 2312844481..4fca32d00b 100644 --- a/internal/gatewayapi/testdata/envoyextensionpolicy-section-name-override.out.yaml +++ b/internal/gatewayapi/testdata/envoyextensionpolicy-section-name-override.out.yaml @@ -149,7 +149,8 @@ envoyExtensionPolicies: type: Warning - lastTransitionTime: null message: 'This policy is being overridden by other envoyExtensionPolicies - for these listeners: [listener-1] and these routes: [default/httproute-1]' + for these gateway listeners: [envoy-gateway/gateway-1/listener-1] and these + routes: [default/httproute-1]' reason: Overridden status: "True" type: Overridden diff --git a/release-notes/current/new_features/9425-listenerset-attachment-envoyextensionpolicy.md b/release-notes/current/new_features/9425-listenerset-attachment-envoyextensionpolicy.md new file mode 100644 index 0000000000..4afded3e48 --- /dev/null +++ b/release-notes/current/new_features/9425-listenerset-attachment-envoyextensionpolicy.md @@ -0,0 +1 @@ +Added support for `ListenerSet` as a `targetRef` kind in `EnvoyExtensionPolicy`. \ No newline at end of file diff --git a/test/cel-validation/envoyextensionpolicy_test.go b/test/cel-validation/envoyextensionpolicy_test.go index d838c0c2d2..dfa6598ce9 100644 --- a/test/cel-validation/envoyextensionpolicy_test.go +++ b/test/cel-validation/envoyextensionpolicy_test.go @@ -73,6 +73,42 @@ func TestEnvoyExtensionPolicyTarget(t *testing.T) { }, wantErrors: []string{}, }, + { + desc: "valid listenerset targetRef", + mutate: func(eep *egv1a1.EnvoyExtensionPolicy) { + eep.Spec = egv1a1.EnvoyExtensionPolicySpec{ + PolicyTargetReferences: egv1a1.PolicyTargetReferences{ + TargetRef: &gwapiv1.LocalPolicyTargetReferenceWithSectionName{ + LocalPolicyTargetReference: gwapiv1.LocalPolicyTargetReference{ + Group: gwapiv1.Group("gateway.networking.k8s.io"), + Kind: gwapiv1.Kind("ListenerSet"), + Name: gwapiv1.ObjectName("listener-set"), + }, + }, + }, + } + }, + wantErrors: []string{}, + }, + { + desc: "valid listenerset targetRefs", + mutate: func(eep *egv1a1.EnvoyExtensionPolicy) { + eep.Spec = egv1a1.EnvoyExtensionPolicySpec{ + PolicyTargetReferences: egv1a1.PolicyTargetReferences{ + TargetRefs: []gwapiv1.LocalPolicyTargetReferenceWithSectionName{ + { + LocalPolicyTargetReference: gwapiv1.LocalPolicyTargetReference{ + Group: gwapiv1.Group("gateway.networking.k8s.io"), + Kind: gwapiv1.Kind("ListenerSet"), + Name: gwapiv1.ObjectName("listener-set"), + }, + }, + }, + }, + } + }, + wantErrors: []string{}, + }, { desc: "both targetRef and targetRefs", mutate: func(eep *egv1a1.EnvoyExtensionPolicy) { @@ -129,7 +165,7 @@ func TestEnvoyExtensionPolicyTarget(t *testing.T) { }, wantErrors: []string{ "spec: Invalid value:", - ": this policy can only have a targetRef.kind of Gateway/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute", + ": this policy can only have a targetRef.kind of Gateway/ListenerSet/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute", }, }, { @@ -151,7 +187,7 @@ func TestEnvoyExtensionPolicyTarget(t *testing.T) { }, wantErrors: []string{ "spec: Invalid value:", - ": this policy can only have a targetRefs[*].kind of Gateway/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute", + ": this policy can only have a targetRefs[*].kind of Gateway/ListenerSet/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute", }, }, { @@ -214,7 +250,7 @@ func TestEnvoyExtensionPolicyTarget(t *testing.T) { wantErrors: []string{ "spec: Invalid value:", ": this policy can only have a targetRef.group of gateway.networking.k8s.io", - ": this policy can only have a targetRef.kind of Gateway/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute", + ": this policy can only have a targetRef.kind of Gateway/ListenerSet/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute", }, }, { @@ -237,7 +273,7 @@ func TestEnvoyExtensionPolicyTarget(t *testing.T) { wantErrors: []string{ "spec: Invalid value:", ": this policy can only have a targetRefs[*].group of gateway.networking.k8s.io", - ": this policy can only have a targetRefs[*].kind of Gateway/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute", + ": this policy can only have a targetRefs[*].kind of Gateway/ListenerSet/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute", }, }, { diff --git a/test/e2e/testdata/envoyextensionpolicy-listenerset.yaml b/test/e2e/testdata/envoyextensionpolicy-listenerset.yaml new file mode 100644 index 0000000000..94683da723 --- /dev/null +++ b/test/e2e/testdata/envoyextensionpolicy-listenerset.yaml @@ -0,0 +1,288 @@ +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: eep-listenerset-policy + namespace: gateway-conformance-infra +spec: + gatewayClassName: "{GATEWAY_CLASS_NAME}" + allowedListeners: + namespaces: + from: Same + listeners: + - name: core + port: 18190 + protocol: HTTP + allowedRoutes: + namespaces: + from: Same +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: ListenerSet +metadata: + name: eep-listenerset-policy-ls + namespace: gateway-conformance-infra +spec: + parentRef: + name: eep-listenerset-policy + namespace: gateway-conformance-infra + listeners: + - name: ls-http + port: 18191 + protocol: HTTP + allowedRoutes: + namespaces: + from: Same +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: eep-listenerset-gateway-route + namespace: gateway-conformance-infra +spec: + parentRefs: + - name: eep-listenerset-policy + rules: + - matches: + - path: + type: PathPrefix + value: /gateway-policy + backendRefs: + - name: infra-backend-v1 + port: 8080 +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: eep-listenerset-policy-route + namespace: gateway-conformance-infra +spec: + parentRefs: + - group: gateway.networking.k8s.io + kind: ListenerSet + namespace: gateway-conformance-infra + name: eep-listenerset-policy-ls + sectionName: ls-http + rules: + - matches: + - path: + type: PathPrefix + value: /listenerset-policy + backendRefs: + - name: infra-backend-v1 + port: 8080 +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyExtensionPolicy +metadata: + name: eep-listenerset-policy + namespace: gateway-conformance-infra +spec: + targetRefs: + - group: gateway.networking.k8s.io + kind: ListenerSet + name: eep-listenerset-policy-ls + extProc: + - backendRefs: + - name: grpc-ext-proc + namespace: gateway-conformance-infra + port: 9002 + processingMode: + request: {} + response: {} +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: eep-listener-policy-gateway + namespace: gateway-conformance-infra +spec: + gatewayClassName: "{GATEWAY_CLASS_NAME}" + allowedListeners: + namespaces: + from: Same + listeners: + - name: core + port: 18192 + protocol: HTTP + allowedRoutes: + namespaces: + from: Same +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: ListenerSet +metadata: + name: eep-listener-policy-ls + namespace: gateway-conformance-infra +spec: + parentRef: + name: eep-listener-policy-gateway + namespace: gateway-conformance-infra + listeners: + - name: section-http + port: 18193 + protocol: HTTP + allowedRoutes: + namespaces: + from: Same + - name: other-http + port: 18194 + protocol: HTTP + allowedRoutes: + namespaces: + from: Same +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: eep-listener-policy-route + namespace: gateway-conformance-infra +spec: + parentRefs: + - group: gateway.networking.k8s.io + kind: ListenerSet + namespace: gateway-conformance-infra + name: eep-listener-policy-ls + sectionName: section-http + rules: + - matches: + - path: + type: PathPrefix + value: /listener-policy + backendRefs: + - name: infra-backend-v1 + port: 8080 +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: eep-listener-policy-other-route + namespace: gateway-conformance-infra +spec: + parentRefs: + - group: gateway.networking.k8s.io + kind: ListenerSet + namespace: gateway-conformance-infra + name: eep-listener-policy-ls + sectionName: other-http + rules: + - matches: + - path: + type: PathPrefix + value: /listener-policy-other + backendRefs: + - name: infra-backend-v1 + port: 8080 +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyExtensionPolicy +metadata: + name: eep-listener-policy + namespace: gateway-conformance-infra +spec: + targetRefs: + - group: gateway.networking.k8s.io + kind: ListenerSet + name: eep-listener-policy-ls + sectionName: section-http + extProc: + - backendRefs: + - name: grpc-ext-proc + namespace: gateway-conformance-infra + port: 9002 + processingMode: + request: {} + response: {} +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: eep-route-policy-gateway + namespace: gateway-conformance-infra +spec: + gatewayClassName: "{GATEWAY_CLASS_NAME}" + allowedListeners: + namespaces: + from: Same + listeners: + - name: core + port: 18195 + protocol: HTTP + allowedRoutes: + namespaces: + from: Same +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: ListenerSet +metadata: + name: eep-route-policy-ls + namespace: gateway-conformance-infra +spec: + parentRef: + name: eep-route-policy-gateway + namespace: gateway-conformance-infra + listeners: + - name: route-http + port: 18196 + protocol: HTTP + allowedRoutes: + namespaces: + from: Same +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: eep-route-policy-route + namespace: gateway-conformance-infra +spec: + parentRefs: + - group: gateway.networking.k8s.io + kind: ListenerSet + namespace: gateway-conformance-infra + name: eep-route-policy-ls + sectionName: route-http + rules: + - matches: + - path: + type: PathPrefix + value: /route-policy + backendRefs: + - name: infra-backend-v1 + port: 8080 +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyExtensionPolicy +metadata: + name: eep-route-policy + namespace: gateway-conformance-infra +spec: + targetRefs: + - group: gateway.networking.k8s.io + kind: HTTPRoute + name: eep-route-policy-route + extProc: + - backendRefs: + - name: grpc-ext-proc + namespace: gateway-conformance-infra + port: 9002 + processingMode: + request: {} + response: {} +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: BackendTLSPolicy +metadata: + name: grpc-ext-proc-btls + namespace: gateway-conformance-infra +spec: + targetRefs: + - group: '' + kind: Service + name: grpc-ext-proc + sectionName: grpc + validation: + caCertificateRefs: + - name: grpc-ext-proc-ca + group: '' + kind: ConfigMap + hostname: grpc-ext-proc.envoygateway diff --git a/test/e2e/tests/envoyextensionpolicy_listenerset.go b/test/e2e/tests/envoyextensionpolicy_listenerset.go new file mode 100644 index 0000000000..9f5b469dcc --- /dev/null +++ b/test/e2e/tests/envoyextensionpolicy_listenerset.go @@ -0,0 +1,193 @@ +// 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. + +//go:build e2e + +package tests + +import ( + "testing" + + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" + httputils "sigs.k8s.io/gateway-api/conformance/utils/http" + "sigs.k8s.io/gateway-api/conformance/utils/kubernetes" + "sigs.k8s.io/gateway-api/conformance/utils/suite" +) + +func init() { + ConformanceTests = append(ConformanceTests, EnvoyExtensionPolicyListenerSetTest) +} + +var EnvoyExtensionPolicyListenerSetTest = suite.ConformanceTest{ + ShortName: "EnvoyExtensionPolicyListenerSet", + Description: "EnvoyExtensionPolicy targeting ListenerSets, ListenerSet listeners, and ListenerSet-attached routes", + Manifests: []string{"testdata/ext-proc-service.yaml", "testdata/envoyextensionpolicy-listenerset.yaml"}, + Test: func(t *testing.T, suite *suite.ConformanceTestSuite) { + ns := "gateway-conformance-infra" + podReady := corev1.PodCondition{Type: corev1.PodReady, Status: corev1.ConditionTrue} + + WaitForPods(t, suite.Client, ns, map[string]string{"app": "grpc-ext-proc"}, corev1.PodRunning, &podReady) + + t.Run("listenerset policy only applies to listenerset listeners", func(t *testing.T) { + gwNN := types.NamespacedName{Name: "eep-listenerset-policy", Namespace: ns} + lsNN := types.NamespacedName{Name: "eep-listenerset-policy-ls", Namespace: ns} + + gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted( + t, + suite.Client, + suite.TimeoutConfig, + suite.ControllerName, + kubernetes.NewGatewayRef(gwNN), + types.NamespacedName{Name: "eep-listenerset-gateway-route", Namespace: ns}, + ) + + routeParents := []gwapiv1.RouteParentStatus{ + createListenerSetParent(suite.ControllerName, lsNN.Name, "ls-http"), + } + kubernetes.RouteMustHaveParents( + t, + suite.Client, + suite.TimeoutConfig, + types.NamespacedName{Name: "eep-listenerset-policy-route", Namespace: ns}, + routeParents, + false, + &gwapiv1.HTTPRoute{}, + ) + + EnvoyExtensionPolicyMustBeAccepted( + t, + suite.Client, + types.NamespacedName{Name: "eep-listenerset-policy", Namespace: ns}, + suite.ControllerName, + listenerSetPolicyAncestor(ns, lsNN.Name, ""), + ) + + lsAddr := getListenerAddr(gwAddr, "18191") + expectNoExtProcHeaders(t, suite, gwAddr, "/gateway-policy", ns) + expectExtProcHeaders(t, suite, lsAddr, "/listenerset-policy", ns) + }) + + t.Run("listenerset listener policy only applies to the selected listener", func(t *testing.T) { + gwNN := types.NamespacedName{Name: "eep-listener-policy-gateway", Namespace: ns} + lsNN := types.NamespacedName{Name: "eep-listener-policy-ls", Namespace: ns} + + gwAddr, err := kubernetes.WaitForGatewayAddress(t, suite.Client, suite.TimeoutConfig, kubernetes.NewGatewayRef(gwNN)) + require.NoError(t, err) + + kubernetes.RouteMustHaveParents( + t, + suite.Client, + suite.TimeoutConfig, + types.NamespacedName{Name: "eep-listener-policy-route", Namespace: ns}, + []gwapiv1.RouteParentStatus{ + createListenerSetParent(suite.ControllerName, lsNN.Name, "section-http"), + }, + false, + &gwapiv1.HTTPRoute{}, + ) + kubernetes.RouteMustHaveParents( + t, + suite.Client, + suite.TimeoutConfig, + types.NamespacedName{Name: "eep-listener-policy-other-route", Namespace: ns}, + []gwapiv1.RouteParentStatus{ + createListenerSetParent(suite.ControllerName, lsNN.Name, "other-http"), + }, + false, + &gwapiv1.HTTPRoute{}, + ) + + EnvoyExtensionPolicyMustBeAccepted( + t, + suite.Client, + types.NamespacedName{Name: "eep-listener-policy", Namespace: ns}, + suite.ControllerName, + listenerSetPolicyAncestor(ns, lsNN.Name, "section-http"), + ) + + listenerAddr := getListenerAddr(gwAddr, "18193") + otherListenerAddr := getListenerAddr(gwAddr, "18194") + expectExtProcHeaders(t, suite, listenerAddr, "/listener-policy", ns) + expectNoExtProcHeaders(t, suite, otherListenerAddr, "/listener-policy-other", ns) + }) + + t.Run("route policy attached through listenerset applies", func(t *testing.T) { + gwNN := types.NamespacedName{Name: "eep-route-policy-gateway", Namespace: ns} + lsNN := types.NamespacedName{Name: "eep-route-policy-ls", Namespace: ns} + routeNN := types.NamespacedName{Name: "eep-route-policy-route", Namespace: ns} + + gwAddr, err := kubernetes.WaitForGatewayAddress(t, suite.Client, suite.TimeoutConfig, kubernetes.NewGatewayRef(gwNN)) + require.NoError(t, err) + + routeParents := []gwapiv1.RouteParentStatus{ + createListenerSetParent(suite.ControllerName, lsNN.Name, "route-http"), + } + kubernetes.RouteMustHaveParents(t, suite.Client, suite.TimeoutConfig, routeNN, routeParents, false, &gwapiv1.HTTPRoute{}) + + EnvoyExtensionPolicyMustBeAccepted( + t, + suite.Client, + types.NamespacedName{Name: "eep-route-policy", Namespace: ns}, + suite.ControllerName, + listenerSetPolicyAncestor(ns, lsNN.Name, "route-http"), + ) + + routeAddr := getListenerAddr(gwAddr, "18196") + expectExtProcHeaders(t, suite, routeAddr, "/route-policy", ns) + }) + }, +} + +func expectExtProcHeaders(t *testing.T, suite *suite.ConformanceTestSuite, addr, path, namespace string) { + t.Helper() + + expectedResponse := httputils.ExpectedResponse{ + Request: httputils.Request{ + Path: path, + }, + ExpectedRequest: &httputils.ExpectedRequest{ + Request: httputils.Request{ + Path: path, + Headers: map[string]string{ + "x-request-ext-processed": "true", + }, + }, + }, + Response: httputils.Response{ + StatusCodes: []int{200}, + Headers: map[string]string{ + "x-response-ext-processed": "true", + }, + }, + Namespace: namespace, + } + + httputils.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, addr, expectedResponse) +} + +func expectNoExtProcHeaders(t *testing.T, suite *suite.ConformanceTestSuite, addr, path, namespace string) { + t.Helper() + + expectedResponse := httputils.ExpectedResponse{ + Request: httputils.Request{ + Path: path, + }, + ExpectedRequest: &httputils.ExpectedRequest{ + Request: httputils.Request{ + Path: path, + }, + }, + Response: httputils.Response{ + StatusCodes: []int{200}, + AbsentHeaders: []string{"x-response-ext-processed"}, + }, + Namespace: namespace, + } + + httputils.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, addr, expectedResponse) +} diff --git a/test/helm/gateway-crds-helm/all.out.yaml b/test/helm/gateway-crds-helm/all.out.yaml index 3db681cbe8..c8762063ab 100644 --- a/test/helm/gateway-crds-helm/all.out.yaml +++ b/test/helm/gateway-crds-helm/all.out.yaml @@ -32899,16 +32899,17 @@ spec: - message: this policy can only have a targetRef.group of gateway.networking.k8s.io rule: 'has(self.targetRef) ? self.targetRef.group == ''gateway.networking.k8s.io'' : true' - - message: this policy can only have a targetRef.kind of Gateway/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute - rule: 'has(self.targetRef) ? self.targetRef.kind in [''Gateway'', ''HTTPRoute'', - ''GRPCRoute'', ''UDPRoute'', ''TCPRoute'', ''TLSRoute''] : true' + - message: this policy can only have a targetRef.kind of Gateway/ListenerSet/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute + rule: 'has(self.targetRef) ? self.targetRef.kind in [''Gateway'', ''ListenerSet'', + ''HTTPRoute'', ''GRPCRoute'', ''UDPRoute'', ''TCPRoute'', ''TLSRoute''] + : true' - message: this policy can only have a targetRefs[*].group of gateway.networking.k8s.io rule: 'has(self.targetRefs) ? self.targetRefs.all(ref, ref.group == ''gateway.networking.k8s.io'') : true ' - - message: this policy can only have a targetRefs[*].kind of Gateway/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute + - message: this policy can only have a targetRefs[*].kind of Gateway/ListenerSet/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute rule: 'has(self.targetRefs) ? self.targetRefs.all(ref, ref.kind in [''Gateway'', - ''HTTPRoute'', ''GRPCRoute'', ''UDPRoute'', ''TCPRoute'', ''TLSRoute'']) - : true ' + ''ListenerSet'', ''HTTPRoute'', ''GRPCRoute'', ''UDPRoute'', ''TCPRoute'', + ''TLSRoute'']) : true ' status: description: Status defines the current status of EnvoyExtensionPolicy. properties: diff --git a/test/helm/gateway-crds-helm/e2e.out.yaml b/test/helm/gateway-crds-helm/e2e.out.yaml index 0849fd858a..a207a78488 100644 --- a/test/helm/gateway-crds-helm/e2e.out.yaml +++ b/test/helm/gateway-crds-helm/e2e.out.yaml @@ -8837,16 +8837,17 @@ spec: - message: this policy can only have a targetRef.group of gateway.networking.k8s.io rule: 'has(self.targetRef) ? self.targetRef.group == ''gateway.networking.k8s.io'' : true' - - message: this policy can only have a targetRef.kind of Gateway/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute - rule: 'has(self.targetRef) ? self.targetRef.kind in [''Gateway'', ''HTTPRoute'', - ''GRPCRoute'', ''UDPRoute'', ''TCPRoute'', ''TLSRoute''] : true' + - message: this policy can only have a targetRef.kind of Gateway/ListenerSet/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute + rule: 'has(self.targetRef) ? self.targetRef.kind in [''Gateway'', ''ListenerSet'', + ''HTTPRoute'', ''GRPCRoute'', ''UDPRoute'', ''TCPRoute'', ''TLSRoute''] + : true' - message: this policy can only have a targetRefs[*].group of gateway.networking.k8s.io rule: 'has(self.targetRefs) ? self.targetRefs.all(ref, ref.group == ''gateway.networking.k8s.io'') : true ' - - message: this policy can only have a targetRefs[*].kind of Gateway/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute + - message: this policy can only have a targetRefs[*].kind of Gateway/ListenerSet/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute rule: 'has(self.targetRefs) ? self.targetRefs.all(ref, ref.kind in [''Gateway'', - ''HTTPRoute'', ''GRPCRoute'', ''UDPRoute'', ''TCPRoute'', ''TLSRoute'']) - : true ' + ''ListenerSet'', ''HTTPRoute'', ''GRPCRoute'', ''UDPRoute'', ''TCPRoute'', + ''TLSRoute'']) : true ' status: description: Status defines the current status of EnvoyExtensionPolicy. properties: diff --git a/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml b/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml index 813bedd5f9..7e52abacf9 100644 --- a/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml +++ b/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml @@ -8837,16 +8837,17 @@ spec: - message: this policy can only have a targetRef.group of gateway.networking.k8s.io rule: 'has(self.targetRef) ? self.targetRef.group == ''gateway.networking.k8s.io'' : true' - - message: this policy can only have a targetRef.kind of Gateway/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute - rule: 'has(self.targetRef) ? self.targetRef.kind in [''Gateway'', ''HTTPRoute'', - ''GRPCRoute'', ''UDPRoute'', ''TCPRoute'', ''TLSRoute''] : true' + - message: this policy can only have a targetRef.kind of Gateway/ListenerSet/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute + rule: 'has(self.targetRef) ? self.targetRef.kind in [''Gateway'', ''ListenerSet'', + ''HTTPRoute'', ''GRPCRoute'', ''UDPRoute'', ''TCPRoute'', ''TLSRoute''] + : true' - message: this policy can only have a targetRefs[*].group of gateway.networking.k8s.io rule: 'has(self.targetRefs) ? self.targetRefs.all(ref, ref.group == ''gateway.networking.k8s.io'') : true ' - - message: this policy can only have a targetRefs[*].kind of Gateway/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute + - message: this policy can only have a targetRefs[*].kind of Gateway/ListenerSet/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute rule: 'has(self.targetRefs) ? self.targetRefs.all(ref, ref.kind in [''Gateway'', - ''HTTPRoute'', ''GRPCRoute'', ''UDPRoute'', ''TCPRoute'', ''TLSRoute'']) - : true ' + ''ListenerSet'', ''HTTPRoute'', ''GRPCRoute'', ''UDPRoute'', ''TCPRoute'', + ''TLSRoute'']) : true ' status: description: Status defines the current status of EnvoyExtensionPolicy. properties: