Skip to content
2 changes: 1 addition & 1 deletion api/v1alpha1/backendtrafficpolicy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ type BackendTrafficPolicy struct {
// +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.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.mergeType) || ((!has(self.targetRef) || self.targetRef.kind in ['HTTPRoute', 'GRPCRoute', 'UDPRoute', 'TCPRoute', 'TLSRoute']) && (!has(self.targetRefs) || self.targetRefs.all(ref, ref.kind in ['HTTPRoute', 'GRPCRoute', 'UDPRoute', 'TCPRoute', 'TLSRoute'])) && (!has(self.targetSelectors) || self.targetSelectors.all(sel, sel.kind in ['HTTPRoute', 'GRPCRoute', 'UDPRoute', 'TCPRoute', 'TLSRoute'])))", message="mergeType can only be used with xRoute targets"
Comment thread
mehara-rothila marked this conversation as resolved.
// +kubebuilder:validation:XValidation:rule="!has(self.compression) || !has(self.compressor)", message="either compression or compressor can be set, not both"
// +kubebuilder:validation:XValidation:rule="!has(self.requestBuffer) || !has(self.httpUpgrade) || self.httpUpgrade.size() == 0", message="requestBuffer cannot be used together with httpUpgrade"
// +kubebuilder:validation:XValidation:rule="!has(self.admissionControl) || ((!has(self.targetRef) || self.targetRef.kind in ['Gateway', 'HTTPRoute', 'GRPCRoute']) && (!has(self.targetRefs) || self.targetRefs.all(ref, ref.kind in ['Gateway', 'HTTPRoute', 'GRPCRoute'])) && (!has(self.targetSelectors) || self.targetSelectors.all(sel, sel.kind in ['Gateway', 'HTTPRoute', 'GRPCRoute'])))", message="admissionControl can only be used with HTTPRoute, GRPCRoute, or Gateway targets"
Expand All @@ -56,7 +57,6 @@ type BackendTrafficPolicySpec struct {
// This field cannot be set when targeting a parent resource (Gateway).
// If unset, no merging occurs, and only the most specific configuration takes effect.
//
// +kubebuilder:validation:XValidation:rule="self != 'Replace'",message="Replace is not a valid MergeType for BackendTrafficPolicySpec"
// +optional
MergeType *MergeType `json:"mergeType,omitempty"`
Comment thread
mehara-rothila marked this conversation as resolved.
Comment thread
mehara-rothila marked this conversation as resolved.

Expand Down
1 change: 0 additions & 1 deletion api/v1alpha1/securitypolicy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ type SecurityPolicySpec struct {
// Currently, this field can only be set when targeting xRoute resources.
// If unset, no merging occurs, and only the most specific configuration takes effect.
//
// +kubebuilder:validation:XValidation:rule="self != 'Replace'",message="Replace is not a valid MergeType for SecurityPolicy"
// +optional
MergeType *MergeType `json:"mergeType,omitempty"`
Comment thread
mehara-rothila marked this conversation as resolved.

Expand Down
4 changes: 4 additions & 0 deletions api/v1alpha1/shared_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,10 @@ type KubernetesPatchSpec struct {
// Type is the type of merge operation to perform
//
// By default, StrategicMerge is used as the patch type.
// Replace is not supported for Kubernetes resource patches, as it would
// replace the entire generated resource with the patch value.
//
// +kubebuilder:validation:Enum=StrategicMerge;JSONMerge
// +optional
Type *MergeType `json:"type,omitempty"`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1439,9 +1439,6 @@ spec:
This field cannot be set when targeting a parent resource (Gateway).
If unset, no merging occurs, and only the most specific configuration takes effect.
type: string
x-kubernetes-validations:
- message: Replace is not a valid MergeType for BackendTrafficPolicySpec
rule: self != 'Replace'
proxyProtocol:
description: ProxyProtocol enables the Proxy Protocol when communicating
with the backend.
Expand Down Expand Up @@ -3387,6 +3384,13 @@ spec:
rule: 'has(self.targetRefs) ? self.targetRefs.all(ref, ref.kind in [''Gateway'',
''HTTPRoute'', ''GRPCRoute'', ''UDPRoute'', ''TCPRoute'', ''TLSRoute''])
: true '
- message: mergeType can only be used with xRoute targets
rule: '!has(self.mergeType) || ((!has(self.targetRef) || self.targetRef.kind
in [''HTTPRoute'', ''GRPCRoute'', ''UDPRoute'', ''TCPRoute'', ''TLSRoute''])
&& (!has(self.targetRefs) || self.targetRefs.all(ref, ref.kind in
[''HTTPRoute'', ''GRPCRoute'', ''UDPRoute'', ''TCPRoute'', ''TLSRoute'']))
&& (!has(self.targetSelectors) || self.targetSelectors.all(sel, sel.kind
in [''HTTPRoute'', ''GRPCRoute'', ''UDPRoute'', ''TCPRoute'', ''TLSRoute''])))'
- message: either compression or compressor can be set, not both
rule: '!has(self.compression) || !has(self.compressor)'
- message: requestBuffer cannot be used together with httpUpgrade
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1360,6 +1360,11 @@ spec:
Type is the type of merge operation to perform

By default, StrategicMerge is used as the patch type.
Replace is not supported for Kubernetes resource patches, as it would
replace the entire generated resource with the patch value.
enum:
- StrategicMerge
- JSONMerge
type: string
value:
description: Object contains the raw configuration
Expand Down Expand Up @@ -6917,6 +6922,11 @@ spec:
Type is the type of merge operation to perform

By default, StrategicMerge is used as the patch type.
Replace is not supported for Kubernetes resource patches, as it would
replace the entire generated resource with the patch value.
enum:
- StrategicMerge
- JSONMerge
type: string
value:
description: Object contains the raw configuration
Expand Down Expand Up @@ -11062,6 +11072,11 @@ spec:
Type is the type of merge operation to perform

By default, StrategicMerge is used as the patch type.
Replace is not supported for Kubernetes resource patches, as it would
replace the entire generated resource with the patch value.
enum:
- StrategicMerge
- JSONMerge
type: string
value:
description: Object contains the raw configuration
Expand Down Expand Up @@ -11112,6 +11127,11 @@ spec:
Type is the type of merge operation to perform

By default, StrategicMerge is used as the patch type.
Replace is not supported for Kubernetes resource patches, as it would
replace the entire generated resource with the patch value.
enum:
- StrategicMerge
- JSONMerge
type: string
value:
description: Object contains the raw configuration
Expand Down Expand Up @@ -11203,6 +11223,11 @@ spec:
Type is the type of merge operation to perform

By default, StrategicMerge is used as the patch type.
Replace is not supported for Kubernetes resource patches, as it would
replace the entire generated resource with the patch value.
enum:
- StrategicMerge
- JSONMerge
type: string
value:
description: Object contains the raw configuration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5469,9 +5469,6 @@ spec:
Currently, this field can only be set when targeting xRoute resources.
If unset, no merging occurs, and only the most specific configuration takes effect.
type: string
x-kubernetes-validations:
- message: Replace is not a valid MergeType for SecurityPolicy
rule: self != 'Replace'
oidc:
description: OIDC defines the configuration for the OpenID Connect
(OIDC) authentication.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1438,9 +1438,6 @@ spec:
This field cannot be set when targeting a parent resource (Gateway).
If unset, no merging occurs, and only the most specific configuration takes effect.
type: string
x-kubernetes-validations:
- message: Replace is not a valid MergeType for BackendTrafficPolicySpec
rule: self != 'Replace'
proxyProtocol:
description: ProxyProtocol enables the Proxy Protocol when communicating
with the backend.
Expand Down Expand Up @@ -3386,6 +3383,13 @@ spec:
rule: 'has(self.targetRefs) ? self.targetRefs.all(ref, ref.kind in [''Gateway'',
''HTTPRoute'', ''GRPCRoute'', ''UDPRoute'', ''TCPRoute'', ''TLSRoute''])
: true '
- message: mergeType can only be used with xRoute targets
rule: '!has(self.mergeType) || ((!has(self.targetRef) || self.targetRef.kind
in [''HTTPRoute'', ''GRPCRoute'', ''UDPRoute'', ''TCPRoute'', ''TLSRoute''])
&& (!has(self.targetRefs) || self.targetRefs.all(ref, ref.kind in
[''HTTPRoute'', ''GRPCRoute'', ''UDPRoute'', ''TCPRoute'', ''TLSRoute'']))
&& (!has(self.targetSelectors) || self.targetSelectors.all(sel, sel.kind
in [''HTTPRoute'', ''GRPCRoute'', ''UDPRoute'', ''TCPRoute'', ''TLSRoute''])))'
- message: either compression or compressor can be set, not both
rule: '!has(self.compression) || !has(self.compressor)'
- message: requestBuffer cannot be used together with httpUpgrade
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1359,6 +1359,11 @@ spec:
Type is the type of merge operation to perform

By default, StrategicMerge is used as the patch type.
Replace is not supported for Kubernetes resource patches, as it would
replace the entire generated resource with the patch value.
enum:
- StrategicMerge
- JSONMerge
type: string
value:
description: Object contains the raw configuration
Expand Down Expand Up @@ -6916,6 +6921,11 @@ spec:
Type is the type of merge operation to perform

By default, StrategicMerge is used as the patch type.
Replace is not supported for Kubernetes resource patches, as it would
replace the entire generated resource with the patch value.
enum:
- StrategicMerge
- JSONMerge
type: string
value:
description: Object contains the raw configuration
Expand Down Expand Up @@ -11061,6 +11071,11 @@ spec:
Type is the type of merge operation to perform

By default, StrategicMerge is used as the patch type.
Replace is not supported for Kubernetes resource patches, as it would
replace the entire generated resource with the patch value.
enum:
- StrategicMerge
- JSONMerge
type: string
value:
description: Object contains the raw configuration
Expand Down Expand Up @@ -11111,6 +11126,11 @@ spec:
Type is the type of merge operation to perform

By default, StrategicMerge is used as the patch type.
Replace is not supported for Kubernetes resource patches, as it would
replace the entire generated resource with the patch value.
enum:
- StrategicMerge
- JSONMerge
type: string
value:
description: Object contains the raw configuration
Expand Down Expand Up @@ -11202,6 +11222,11 @@ spec:
Type is the type of merge operation to perform

By default, StrategicMerge is used as the patch type.
Replace is not supported for Kubernetes resource patches, as it would
replace the entire generated resource with the patch value.
enum:
- StrategicMerge
- JSONMerge
type: string
value:
description: Object contains the raw configuration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5468,9 +5468,6 @@ spec:
Currently, this field can only be set when targeting xRoute resources.
If unset, no merging occurs, and only the most specific configuration takes effect.
type: string
x-kubernetes-validations:
- message: Replace is not a valid MergeType for SecurityPolicy
rule: self != 'Replace'
oidc:
description: OIDC defines the configuration for the OpenID Connect
(OIDC) authentication.
Expand Down
32 changes: 31 additions & 1 deletion internal/gatewayapi/backendtrafficpolicy.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,9 @@ func (t *Translator) ProcessBackendTrafficPolicies(
backendTrafficPolicies := resources.BackendTrafficPolicies
// BackendTrafficPolicies are already sorted by the provider layer

// Tracking is only valid during one translation across multiple routes and gateways.
t.replacedTrafficPolicyRoutes = sets.New[string]()

routeMapSize := len(routes)
gatewayMapSize := len(gateways)
policyMapSize := len(backendTrafficPolicies)
Expand Down Expand Up @@ -882,7 +885,9 @@ func (t *Translator) translateBackendTrafficPolicyForRouteWithMerge(
// Replace the rate limit in the merged features if successful
tf.RateLimit = mergedRL
}
} else if policy.Spec.RateLimit == nil && parentPolicy.Spec.RateLimit != nil {
} else if policy.Spec.RateLimit == nil &&
parentPolicy.Spec.RateLimit != nil &&
*policy.Spec.MergeType != egv1a1.Replace {
Comment thread
mehara-rothila marked this conversation as resolved.
// Case 2: Only gateway policy has rate limits - preserve gateway policy's rule names
tfGW, _ := t.buildTrafficFeatures(parentPolicy, nil)
if tfGW != nil && tfGW.RateLimit != nil {
Expand Down Expand Up @@ -914,6 +919,14 @@ func (t *Translator) applyTrafficFeatureToRoute(route RouteContext,
routeStatName = ptr.Deref(tf.Telemetry.Metrics.RouteStatName, "")
}

// A policy with mergeType Replace discards the parent policy configuration
// entirely, so claim the matched TCP/UDP routes to prevent the parent
// Gateway/Listener policy from back-filling fields this policy omitted.
replaceClaimsRoutes := policy.Spec.MergeType != nil && *policy.Spec.MergeType == egv1a1.Replace
if replaceClaimsRoutes && t.replacedTrafficPolicyRoutes == nil {
t.replacedTrafficPolicyRoutes = sets.New[string]()
}

prefix := irRoutePrefix(route)
for _, tcp := range x.TCP {
// if listenerName is not nil, only apply to the specific listener
Expand All @@ -927,6 +940,9 @@ func (t *Translator) applyTrafficFeatureToRoute(route RouteContext,
continue
}
if strings.HasPrefix(r.Destination.Name, prefix) {
if replaceClaimsRoutes {
t.replacedTrafficPolicyRoutes.Insert(replacedRouteKey(tcp.Name, r.Destination.Name))
}
// only set attributes which weren't already set by a more
// specific policy
setIfNil(&r.LoadBalancer, tf.LoadBalancer)
Expand Down Expand Up @@ -956,6 +972,9 @@ func (t *Translator) applyTrafficFeatureToRoute(route RouteContext,
continue
}
if strings.HasPrefix(r.Destination.Name, prefix) {
if replaceClaimsRoutes {
t.replacedTrafficPolicyRoutes.Insert(replacedRouteKey(udp.Name, r.Destination.Name))
}
// only set attributes which weren't already set by a more
// specific policy
setIfNil(&r.LoadBalancer, tf.LoadBalancer)
Expand Down Expand Up @@ -1245,6 +1264,11 @@ func (t *Translator) translateBackendTrafficPolicyForGateway(
}

for _, r := range tcp.Routes {
// Skip routes claimed by a route-scoped policy with mergeType Replace,
// which discards the parent policy configuration entirely.
if t.replacedTrafficPolicyRoutes.Has(replacedRouteKey(tcp.Name, r.Destination.Name)) {
continue
}
// only set attributes which weren't already set by a more
// specific policy
setIfNil(&r.LoadBalancer, tf.LoadBalancer)
Expand Down Expand Up @@ -1276,6 +1300,12 @@ func (t *Translator) translateBackendTrafficPolicyForGateway(

route := udp.Route

// Skip routes claimed by a route-scoped policy with mergeType Replace,
// which discards the parent policy configuration entirely.
if t.replacedTrafficPolicyRoutes.Has(replacedRouteKey(udp.Name, route.Destination.Name)) {
continue
}

// only set attributes which weren't already set by a more
// specific policy
setIfNil(&route.LoadBalancer, tf.LoadBalancer)
Expand Down
8 changes: 8 additions & 0 deletions internal/gatewayapi/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,14 @@ func irTCPRouteName(route RouteContext) string {
return fmt.Sprintf("%s/%s/%s", strings.ToLower(string(route.GetRouteType())), route.GetNamespace(), route.GetName())
}

// replacedRouteKey identifies a TCP/UDP IR route within an IR listener. It is
// used to record routes claimed by a route-scoped policy with mergeType Replace
// so that parent Gateway/Listener policies skip them entirely instead of
// back-filling fields the replacing policy intentionally omitted.
func replacedRouteKey(listenerName, destinationName string) string {
return fmt.Sprintf("%s/%s", listenerName, destinationName)
}

func irUDPRouteName(route RouteContext) string {
return irTCPRouteName(route)
}
Expand Down
Loading
Loading