Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions api/v1alpha1/envoyextensionypolicy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ 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) ? !has(self.targetRef.sectionName) : true",message="this policy does not yet support the sectionName field"
// +kubebuilder:validation:XValidation:rule="has(self.targetRef) ? !(has(self.targetRef.sectionName) && self.targetRef.kind == 'Gateway') : true",message="this policy does not yet support the sectionName field for Gateway"
// +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, !has(ref.sectionName)) : true",message="this policy does not yet support the sectionName field"
// +kubebuilder:validation:XValidation:rule="has(self.targetRefs) ? self.targetRefs.all(ref, !(has(ref.sectionName) && ref.kind == 'Gateway')) : true",message="this policy does not yet support the sectionName field for Gateway"
type EnvoyExtensionPolicySpec struct {
PolicyTargetReferences `json:",inline"`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1662,18 +1662,21 @@ spec:
- 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 does not yet support the sectionName field
rule: 'has(self.targetRef) ? !has(self.targetRef.sectionName) : true'
- message: this policy does not yet support the sectionName field for
Gateway
rule: 'has(self.targetRef) ? !(has(self.targetRef.sectionName) && self.targetRef.kind
== ''Gateway'') : 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
rule: 'has(self.targetRefs) ? self.targetRefs.all(ref, ref.kind in [''Gateway'',
''HTTPRoute'', ''GRPCRoute'', ''UDPRoute'', ''TCPRoute'', ''TLSRoute''])
: true '
- message: this policy does not yet support the sectionName field
rule: 'has(self.targetRefs) ? self.targetRefs.all(ref, !has(ref.sectionName))
: true'
- message: this policy does not yet support the sectionName field for
Gateway
rule: 'has(self.targetRefs) ? self.targetRefs.all(ref, !(has(ref.sectionName)
&& ref.kind == ''Gateway'')) : true'
status:
description: Status defines the current status of EnvoyExtensionPolicy.
properties:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1661,18 +1661,21 @@ spec:
- 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 does not yet support the sectionName field
rule: 'has(self.targetRef) ? !has(self.targetRef.sectionName) : true'
- message: this policy does not yet support the sectionName field for
Gateway
rule: 'has(self.targetRef) ? !(has(self.targetRef.sectionName) && self.targetRef.kind
== ''Gateway'') : 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
rule: 'has(self.targetRefs) ? self.targetRefs.all(ref, ref.kind in [''Gateway'',
''HTTPRoute'', ''GRPCRoute'', ''UDPRoute'', ''TCPRoute'', ''TLSRoute''])
: true '
- message: this policy does not yet support the sectionName field
rule: 'has(self.targetRefs) ? self.targetRefs.all(ref, !has(ref.sectionName))
: true'
- message: this policy does not yet support the sectionName field for
Gateway
rule: 'has(self.targetRefs) ? self.targetRefs.all(ref, !(has(ref.sectionName)
&& ref.kind == ''Gateway'')) : true'
status:
description: Status defines the current status of EnvoyExtensionPolicy.
properties:
Expand Down
10 changes: 10 additions & 0 deletions examples/grpc-ext-proc/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,12 +284,16 @@ func (s *extProcServer) Process(srv envoy_service_proc_v3.ExternalProcessor_Proc
case *envoy_service_proc_v3.ProcessingRequest_ResponseHeaders:

respXDSRouteName := ""
respUpstreamPort := ""

if req.Attributes != nil {
if epa, ok := req.Attributes["envoy.filters.http.ext_proc"]; ok {
if rsa, ok := epa.Fields["xds.route_name"]; ok {
respXDSRouteName = rsa.GetStringValue()
}
if rsa, ok := epa.Fields["upstream.port"]; ok {
respUpstreamPort = rsa.GetStringValue()
}
}
}
forwardedDynamicMetadata := ""
Expand Down Expand Up @@ -324,6 +328,12 @@ func (s *extProcServer) Process(srv envoy_service_proc_v3.ExternalProcessor_Proc
RawValue: []byte(forwardedDynamicMetadata),
},
},
{
Header: &envoy_api_v3_core.HeaderValue{
Key: "x-response-upstream-port",
RawValue: []byte(respUpstreamPort),
},
},
},
},
},
Expand Down
222 changes: 153 additions & 69 deletions internal/gatewayapi/envoyextensionpolicy.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func (t *Translator) ProcessEnvoyExtensionPolicies(envoyExtensionPolicies []*egv
Name: route.GetName(),
Namespace: route.GetNamespace(),
}
routeMap[key] = &policyRouteTargetContext{RouteContext: route}
routeMap[key] = &policyRouteTargetContext{RouteContext: route, attachedToRouteRules: make(sets.Set[string])}
}

gatewayMap := map[types.NamespacedName]*policyGatewayTargetContext{}
Expand All @@ -79,79 +79,44 @@ func (t *Translator) ProcessEnvoyExtensionPolicies(envoyExtensionPolicies []*egv
handledPolicies := make(map[types.NamespacedName]*egv1a1.EnvoyExtensionPolicy)

// Translate
// 1. First translate Policies targeting xRoutes
// 2. Finally, the policies targeting Gateways
// 1. First translate Policies targeting RouteRules
// 2. First translate Policies targeting xRoutes
// 3. Finally, the policies targeting Gateways

// Process the policies targeting xRoutes
// Process the policies targeting RouteRules
for _, currPolicy := range envoyExtensionPolicies {
policyName := utils.NamespacedName(currPolicy)
targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, routes)
for _, currTarget := range targetRefs {
if currTarget.Kind != resource.KindGateway {
if currTarget.Kind != resource.KindGateway && currTarget.SectionName != nil { // Route Rule Target
policy, found := handledPolicies[policyName]
if !found {
policy = currPolicy.DeepCopy()
res = append(res, policy)
handledPolicies[policyName] = policy
res = append(res, policy)
}

// Negative statuses have already been assigned so its safe to skip
route, resolveErr := resolveEEPolicyRouteTargetRef(policy, currTarget, routeMap)
if route == nil {
continue
}

// 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.
parentRefs := GetParentReferences(route)
ancestorRefs := make([]gwapiv1a2.ParentReference, 0, len(parentRefs))
for _, p := range parentRefs {
if p.Kind == nil || *p.Kind == resource.KindGateway {
namespace := route.GetNamespace()
if p.Namespace != nil {
namespace = string(*p.Namespace)
}
gwNN := types.NamespacedName{
Namespace: namespace,
Name: string(p.Name),
}

key := gwNN.String()
if _, ok := gatewayRouteMap[key]; !ok {
gatewayRouteMap[key] = make(sets.Set[string])
}
gatewayRouteMap[key].Insert(utils.NamespacedName(route).String())

// Do need a section name since the policy is targeting to a route
ancestorRefs = append(ancestorRefs, getAncestorRefForPolicy(gwNN, p.SectionName))
}
}

// Set conditions for resolve error, then skip current xroute
if resolveErr != nil {
status.SetResolveErrorForPolicyAncestors(&policy.Status,
ancestorRefs,
t.GatewayControllerName,
policy.Generation,
resolveErr,
)

continue
t.processEnvoyExtensionPolicyForHTTPRoute(resources, xdsIR,
routeMap, gatewayRouteMap, policy, currTarget)
}
}
}
// Process the policies targeting xRoutes
for _, currPolicy := range envoyExtensionPolicies {
policyName := utils.NamespacedName(currPolicy)
targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, routes)
for _, currTarget := range targetRefs {
if currTarget.Kind != resource.KindGateway && currTarget.SectionName == nil { // Route Target
policy, found := handledPolicies[policyName]
if !found {
policy = currPolicy.DeepCopy()
res = append(res, policy)
handledPolicies[policyName] = policy
}

// Set conditions for translation error if it got any
if err := t.translateEnvoyExtensionPolicyForRoute(policy, route, xdsIR, resources); err != nil {
status.SetTranslationErrorForPolicyAncestors(&policy.Status,
ancestorRefs,
t.GatewayControllerName,
policy.Generation,
status.Error2ConditionMsg(err),
)
}
t.processEnvoyExtensionPolicyForHTTPRoute(resources, xdsIR,
routeMap, gatewayRouteMap, policy, currTarget)

// Set Accepted condition if it is unset
status.SetAcceptedForPolicyAncestors(&policy.Status, ancestorRefs, t.GatewayControllerName, policy.Generation)
}
}
}
Expand Down Expand Up @@ -283,19 +248,52 @@ func resolveEEPolicyRouteTargetRef(policy *egv1a1.EnvoyExtensionPolicy, target g
return nil, nil
}

// Check if another policy targeting the same xRoute exists
if route.attached {
message := fmt.Sprintf("Unable to target %s %s, another EnvoyExtensionPolicy has already attached to it",
string(target.Kind), string(target.Name))
// If sectionName is set, make sure its valid
if target.SectionName != nil {
found := false
for _, r := range route.RouteContext.(*HTTPRouteContext).Spec.Rules {
if r.Name != nil && *r.Name == *target.SectionName {
found = true
break
}
}
if !found {
message := fmt.Sprintf("No section name %s found for %s %s/%s",
string(*target.SectionName), string(target.Kind), policy.Namespace, string(target.Name))

return route.RouteContext, &status.PolicyResolveError{
Reason: gwapiv1a2.PolicyReasonConflicted,
Message: message,
return route.RouteContext, &status.PolicyResolveError{
Reason: gwapiv1a2.PolicyReasonTargetNotFound,
Message: message,
}
}
}

// Set context and save
route.attached = true
if target.SectionName == nil {
// Check if another policy targeting the same xRoute exists
if route.attached {
message := fmt.Sprintf("Unable to target %s %s, another EnvoyExtensionPolicy has already attached to it",
string(target.Kind), string(target.Name))

return route.RouteContext, &status.PolicyResolveError{
Reason: gwapiv1a2.PolicyReasonConflicted,
Message: message,
}
}

// Set context and save
route.attached = true
} else {
routeRuleName := string(*target.SectionName)
if route.attachedToRouteRules.Has(routeRuleName) {
message := fmt.Sprintf("Unable to target RouteRule %s/%s, another EnvoyExtensionPolicy has already attached to it",
string(target.Name), routeRuleName)
return route.RouteContext, &status.PolicyResolveError{
Reason: gwapiv1a2.PolicyReasonConflicted,
Message: message,
}
}
route.attachedToRouteRules.Insert(routeRuleName)
}
routes[key] = route

return route.RouteContext, nil
Expand Down Expand Up @@ -882,3 +880,89 @@ func irConfigNameForWasm(policy client.Object, index int) string {
irConfigName(policy),
strconv.Itoa(index))
}

func (t *Translator) processEnvoyExtensionPolicyForHTTPRoute(
resources *resource.Resources,
xdsIR resource.XdsIRMap,
routeMap map[policyTargetRouteKey]*policyRouteTargetContext,
gatewayRouteMap map[string]sets.Set[string],
policy *egv1a1.EnvoyExtensionPolicy,
currTarget gwapiv1a2.LocalPolicyTargetReferenceWithSectionName,
) {
// Negative statuses have already been assigned so its safe to skip
route, resolveErr := resolveEEPolicyRouteTargetRef(policy, currTarget, routeMap)
if route == nil {
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.
parentRefs := GetParentReferences(route)
ancestorRefs := make([]gwapiv1a2.ParentReference, 0, len(parentRefs))
for _, p := range parentRefs {
if p.Kind == nil || *p.Kind == resource.KindGateway {
namespace := route.GetNamespace()
if p.Namespace != nil {
namespace = string(*p.Namespace)
}
gwNN := types.NamespacedName{
Namespace: namespace,
Name: string(p.Name),
}

key := gwNN.String()
if _, ok := gatewayRouteMap[key]; !ok {
gatewayRouteMap[key] = make(sets.Set[string])
}
gatewayRouteMap[key].Insert(utils.NamespacedName(route).String())

// Do need a section name since the policy is targeting to a route
ancestorRefs = append(ancestorRefs, getAncestorRefForPolicy(gwNN, p.SectionName))
}
}

// Set conditions for resolve error, then skip current xroute
if resolveErr != nil {
status.SetResolveErrorForPolicyAncestors(&policy.Status,
ancestorRefs,
t.GatewayControllerName,
policy.Generation,
resolveErr,
)

return
}

// Set conditions for translation error if it got any
if err := t.translateEnvoyExtensionPolicyForRoute(policy, route, xdsIR, resources); err != nil {
status.SetTranslationErrorForPolicyAncestors(&policy.Status,
ancestorRefs,
t.GatewayControllerName,
policy.Generation,
status.Error2ConditionMsg(err),
)
}

// Set Accepted condition if it is unset
status.SetAcceptedForPolicyAncestors(&policy.Status, ancestorRefs, t.GatewayControllerName, policy.Generation)

// Check if this policy is overridden by other policies targeting at route rule levels
key := policyTargetRouteKey{
Kind: string(currTarget.Kind),
Name: string(currTarget.Name),
Namespace: policy.Namespace,
}
overriddenTargetsMessage := getOverriddenTargetsMessageForHTTPRoute(routeMap[key], currTarget.SectionName)
if overriddenTargetsMessage != "" {
status.SetConditionForPolicyAncestors(&policy.Status,
ancestorRefs,
t.GatewayControllerName,
egv1a1.PolicyConditionOverridden,
metav1.ConditionTrue,
egv1a1.PolicyReasonOverridden,
"This policy is being overridden by other envoyExtensionPolicy for "+overriddenTargetsMessage,
policy.Generation,
)
}
}
Loading
Loading