diff --git a/api/v1alpha1/ratelimit_types.go b/api/v1alpha1/ratelimit_types.go index 21727bb057..d3cd36bca2 100644 --- a/api/v1alpha1/ratelimit_types.go +++ b/api/v1alpha1/ratelimit_types.go @@ -49,19 +49,8 @@ type GlobalRateLimit struct { // matches two rules, one rate limited and one not, the final decision will be // to rate limit the request. // - // +patchMergeKey:"name" - // +patchStrategy:"merge" // +kubebuilder:validation:MaxItems=64 - Rules []RateLimitRule `json:"rules" patchMergeKey:"name" patchStrategy:"merge"` - - // Shared determines whether the rate limit rules apply across all the policy targets. - // If set to true, the rule is treated as a common bucket and is shared across all policy targets (xRoutes). - // Default: false. - // - // +optional - // +notImplementedHide - // +kubebuilder:default=false - Shared *bool `json:"shared,omitempty"` + Rules []RateLimitRule `json:"rules"` } // LocalRateLimit defines local rate limit configuration. @@ -71,23 +60,15 @@ type LocalRateLimit struct { // matches two rules, one with 10rps and one with 20rps, the final limit will // be based on the rule with 10rps. // - // +patchMergeKey:"name" - // +patchStrategy:"merge" - // // +optional // +kubebuilder:validation:MaxItems=16 // +kubebuilder:validation:XValidation:rule="self.all(foo, !has(foo.cost) || !has(foo.cost.response))", message="response cost is not supported for Local Rate Limits" - Rules []RateLimitRule `json:"rules" patchMergeKey:"name" patchStrategy:"merge"` + Rules []RateLimitRule `json:"rules"` } // RateLimitRule defines the semantics for matching attributes // from the incoming requests, and setting limits for them. type RateLimitRule struct { - // Name is the name of the rule. This is used to identify the rule - // in the Envoy configuration and as a unique identifier for merging. - // - // +optional - Name string `json:"name,omitempty"` // ClientSelectors holds the list of select conditions to select // specific clients using attributes from the traffic flow. // All individual select conditions must hold True for this rule @@ -118,6 +99,12 @@ type RateLimitRule struct { // // +optional Cost *RateLimitCost `json:"cost,omitempty"` + // Shared determines whether this rate limit rule applies across all the policy targets. + // If set to true, the rule is treated as a common bucket and is shared across all policy targets (xRoutes). + // Default: false. + // + // +optional + Shared *bool `json:"shared,omitempty"` } type RateLimitCost struct { diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index ed3f92b808..87c150497f 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -2853,11 +2853,6 @@ func (in *GlobalRateLimit) DeepCopyInto(out *GlobalRateLimit) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - if in.Shared != nil { - in, out := &in.Shared, &out.Shared - *out = new(bool) - **out = **in - } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GlobalRateLimit. @@ -5306,6 +5301,11 @@ func (in *RateLimitRule) DeepCopyInto(out *RateLimitRule) { *out = new(RateLimitCost) (*in).DeepCopyInto(*out) } + if in.Shared != nil { + in, out := &in.Shared, &out.Shared + *out = new(bool) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RateLimitRule. diff --git a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml index 66c01520e6..4aeb42b0aa 100644 --- a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml +++ b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml @@ -948,23 +948,17 @@ spec: - requests - unit type: object - name: + shared: description: |- - Name is the name of the rule. This is used to identify the rule - in the Envoy configuration and as a unique identifier for merging. - type: string + Shared determines whether this rate limit rule applies across all the policy targets. + If set to true, the rule is treated as a common bucket and is shared across all policy targets (xRoutes). + Default: false. + type: boolean required: - limit type: object maxItems: 64 type: array - shared: - default: false - description: |- - Shared determines whether the rate limit rules apply across all the policy targets. - If set to true, the rule is treated as a common bucket and is shared across all policy targets (xRoutes). - Default: false. - type: boolean required: - rules type: object @@ -1203,11 +1197,12 @@ spec: - requests - unit type: object - name: + shared: description: |- - Name is the name of the rule. This is used to identify the rule - in the Envoy configuration and as a unique identifier for merging. - type: string + Shared determines whether this rate limit rule applies across all the policy targets. + If set to true, the rule is treated as a common bucket and is shared across all policy targets (xRoutes). + Default: false. + type: boolean required: - limit type: object diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml index af436290d9..2f79f196d2 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml @@ -947,23 +947,17 @@ spec: - requests - unit type: object - name: + shared: description: |- - Name is the name of the rule. This is used to identify the rule - in the Envoy configuration and as a unique identifier for merging. - type: string + Shared determines whether this rate limit rule applies across all the policy targets. + If set to true, the rule is treated as a common bucket and is shared across all policy targets (xRoutes). + Default: false. + type: boolean required: - limit type: object maxItems: 64 type: array - shared: - default: false - description: |- - Shared determines whether the rate limit rules apply across all the policy targets. - If set to true, the rule is treated as a common bucket and is shared across all policy targets (xRoutes). - Default: false. - type: boolean required: - rules type: object @@ -1202,11 +1196,12 @@ spec: - requests - unit type: object - name: + shared: description: |- - Name is the name of the rule. This is used to identify the rule - in the Envoy configuration and as a unique identifier for merging. - type: string + Shared determines whether this rate limit rule applies across all the policy targets. + If set to true, the rule is treated as a common bucket and is shared across all policy targets (xRoutes). + Default: false. + type: boolean required: - limit type: object diff --git a/internal/gatewayapi/backendtrafficpolicy.go b/internal/gatewayapi/backendtrafficpolicy.go index 75fcd286a9..b670a2ea28 100644 --- a/internal/gatewayapi/backendtrafficpolicy.go +++ b/internal/gatewayapi/backendtrafficpolicy.go @@ -429,13 +429,33 @@ func (t *Translator) translateBackendTrafficPolicyForRouteWithMerge( if err != nil { return fmt.Errorf("error merging policies: %w", err) } + + // Build traffic features from the merged policy tf, errs := t.buildTrafficFeatures(mergedPolicy, resources) if tf == nil { // should not happen return nil } - // Apply IR to relevant gateway routes + // Since GlobalRateLimit merge relies on IR auto-generated key: (//rule/) + // We can't simply merge the BTP's using utils.Merge() we need to specifically merge the GlobalRateLimit.Rules using IR fields. + // Since ir.TrafficFeatures is not a built-in Kubernetes API object with defined merging strategies and it does not support a deep merge (for lists/maps). + if policy.Spec.RateLimit != nil && gwPolicy.Spec.RateLimit != nil { + tfGW, _ := t.buildTrafficFeatures(gwPolicy, resources) + tfRoute, _ := t.buildTrafficFeatures(policy, resources) + + if tfGW != nil && tfRoute != nil && + tfGW.RateLimit != nil && tfRoute.RateLimit != nil { + + mergedRL, err := utils.MergeRL(tfGW.RateLimit, tfRoute.RateLimit, *policy.Spec.MergeType) + if err != nil { + return fmt.Errorf("error merging rate limits: %w", err) + } + // Replace the rate limit in the merged features if successful + tf.RateLimit = mergedRL + } + } + x, ok := xdsIR[t.IRKey(gatewayNN)] if !ok { // should not happen. @@ -489,7 +509,6 @@ func applyTrafficFeatureToRoute(route RouteContext, } r.Traffic = tf.DeepCopy() - r.Traffic.Name = irTrafficName(policy) if localTo, err := buildClusterSettingsTimeout(policy.Spec.ClusterSettings); err == nil { r.Traffic.Timeout = localTo @@ -695,7 +714,6 @@ func (t *Translator) translateBackendTrafficPolicyForGateway( } r.Traffic = tf.DeepCopy() - r.Traffic.Name = irTrafficName(policy) // Update the Host field in HealthCheck, now that we have access to the Route Hostname. r.Traffic.HealthCheck.SetHTTPHostIfAbsent(r.Hostname) @@ -769,7 +787,7 @@ func (t *Translator) buildLocalRateLimit(policy *egv1a1.BackendTrafficPolicy) (* var err error var irRule *ir.RateLimitRule irRules := make([]*ir.RateLimitRule, 0) - for _, rule := range local.Rules { + for i, rule := range local.Rules { // We don't process the rule without clientSelectors here because it's // previously used as the default route-level limit. if len(rule.ClientSelectors) == 0 { @@ -780,6 +798,8 @@ func (t *Translator) buildLocalRateLimit(policy *egv1a1.BackendTrafficPolicy) (* if err != nil { return nil, err } + // Set the Name field as //rule/ + irRule.Name = irRuleName(policy.Namespace, policy.Name, i) irRules = append(irRules, irRule) } @@ -804,8 +824,7 @@ func (t *Translator) buildGlobalRateLimit(policy *egv1a1.BackendTrafficPolicy) ( global := policy.Spec.RateLimit.Global rateLimit := &ir.RateLimit{ Global: &ir.GlobalRateLimit{ - Rules: make([]*ir.RateLimitRule, len(global.Rules)), - Shared: global.Shared, + Rules: make([]*ir.RateLimitRule, len(global.Rules)), }, } @@ -816,6 +835,8 @@ func (t *Translator) buildGlobalRateLimit(policy *egv1a1.BackendTrafficPolicy) ( if err != nil { return nil, err } + // Set the Name field as //rule/ + irRules[i].Name = irRuleName(policy.Namespace, policy.Name, i) } return rateLimit, nil @@ -828,6 +849,7 @@ func buildRateLimitRule(rule egv1a1.RateLimitRule) (*ir.RateLimitRule, error) { Unit: ir.RateLimitUnit(rule.Limit.Unit), }, HeaderMatches: make([]*ir.StringMatch, 0), + Shared: rule.Shared, } for _, match := range rule.ClientSelectors { diff --git a/internal/gatewayapi/helpers.go b/internal/gatewayapi/helpers.go index 20d9a5663f..81cf59f3fb 100644 --- a/internal/gatewayapi/helpers.go +++ b/internal/gatewayapi/helpers.go @@ -408,6 +408,10 @@ func irDestinationSettingName(destName string, backendIdx int) string { return fmt.Sprintf("%s/backend/%d", destName, backendIdx) } +func irRuleName(policyNamespace, policyName string, ruleIndex int) string { + return fmt.Sprintf("%s/%s/rule/%d", policyNamespace, policyName, ruleIndex) +} + // irTLSConfigs produces a defaulted IR TLSConfig func irTLSConfigs(tlsSecrets ...*corev1.Secret) *ir.TLSConfig { if len(tlsSecrets) == 0 { @@ -450,11 +454,6 @@ func irTLSCACertName(namespace, name string) string { return fmt.Sprintf("%s/%s/%s", namespace, name, caCertKey) } -// Helper function to format the policy name and namespace -func irTrafficName(policy *egv1a1.BackendTrafficPolicy) string { - return fmt.Sprintf("%s/%s", policy.Namespace, policy.Name) -} - func IsMergeGatewaysEnabled(resources *resource.Resources) bool { return resources.EnvoyProxyForGatewayClass != nil && resources.EnvoyProxyForGatewayClass.Spec.MergeGateways != nil && *resources.EnvoyProxyForGatewayClass.Spec.MergeGateways } diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-buffer-limit-out-of-range-error.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-buffer-limit-out-of-range-error.out.yaml index 7a971b9462..8906c3c116 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-buffer-limit-out-of-range-error.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-buffer-limit-out-of-range-error.out.yaml @@ -284,7 +284,6 @@ xdsIR: namespace: default name: grpcroute/default/grpcroute-1/rule/0/match/-1/* traffic: - name: envoy-gateway/policy-for-gateway timeout: http: connectionIdleTimeout: 16s diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-buffer-limit-with-invalid-value.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-buffer-limit-with-invalid-value.out.yaml index 9f79eab20e..55f4c88547 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-buffer-limit-with-invalid-value.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-buffer-limit-with-invalid-value.out.yaml @@ -284,7 +284,6 @@ xdsIR: namespace: default name: grpcroute/default/grpcroute-1/rule/0/match/-1/* traffic: - name: envoy-gateway/policy-for-gateway timeout: http: connectionIdleTimeout: 16s diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-buffer-limit.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-buffer-limit.out.yaml index fb4ae09a9a..b3d716c811 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-buffer-limit.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-buffer-limit.out.yaml @@ -284,7 +284,6 @@ xdsIR: namespace: default name: grpcroute/default/grpcroute-1/rule/0/match/-1/* traffic: - name: envoy-gateway/policy-for-gateway timeout: http: connectionIdleTimeout: 16s @@ -340,7 +339,6 @@ xdsIR: traffic: backendConnection: bufferLimit: 100000000 - name: default/policy-for-route readyListener: address: 0.0.0.0 ipFamily: IPv4 diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-compression.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-compression.out.yaml index aa1385507a..df89978b39 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-compression.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-compression.out.yaml @@ -171,7 +171,6 @@ xdsIR: compression: - type: Brotli - type: Gzip - name: default/policy-for-route readyListener: address: 0.0.0.0 ipFamily: IPv4 diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-dns-lookup-family.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-dns-lookup-family.out.yaml index a7aba41328..0db7d5ab16 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-dns-lookup-family.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-dns-lookup-family.out.yaml @@ -449,7 +449,6 @@ xdsIR: dnsRefreshRate: 5s lookupFamily: IPv6 respectDnsTtl: false - name: default/backend-traffic-policy - destination: name: grpcroute/default/grpcroute-1/rule/0 settings: @@ -495,7 +494,6 @@ xdsIR: dnsRefreshRate: 5s lookupFamily: IPv6 respectDnsTtl: false - name: default/backend-traffic-policy readyListener: address: 0.0.0.0 ipFamily: IPv4 diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-http-upgrade-spdy.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-http-upgrade-spdy.out.yaml index 689cb9c294..1903430df1 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-http-upgrade-spdy.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-http-upgrade-spdy.out.yaml @@ -169,7 +169,6 @@ xdsIR: traffic: httpUpgrade: - spdy/3.1 - name: default/policy-for-route readyListener: address: 0.0.0.0 ipFamily: IPv4 diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-http-upgrade-websocket.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-http-upgrade-websocket.out.yaml index 0cefe0206d..5e43ac6652 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-http-upgrade-websocket.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-http-upgrade-websocket.out.yaml @@ -171,7 +171,6 @@ xdsIR: httpUpgrade: - websocket - spdy/3.1 - name: default/policy-for-route readyListener: address: 0.0.0.0 ipFamily: IPv4 diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-override-replace.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-override-replace.out.yaml index e43484d457..ad527fceec 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-override-replace.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-override-replace.out.yaml @@ -313,7 +313,6 @@ xdsIR: loadBalancer: consistentHash: sourceIP: true - name: default/policy-for-route-1 - destination: name: httproute/default/httproute-2/rule/0 settings: @@ -338,7 +337,6 @@ xdsIR: traffic: loadBalancer: random: {} - name: envoy-gateway/policy-for-gateway-1 timeout: http: connectionIdleTimeout: 21s @@ -366,8 +364,7 @@ xdsIR: distinct: false name: "" prefix: /baz - traffic: - name: default/policy-for-route-3 + traffic: {} readyListener: address: 0.0.0.0 ipFamily: IPv4 diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-request-buffer.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-request-buffer.out.yaml index 4b8fd42178..4b0955aab6 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-request-buffer.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-request-buffer.out.yaml @@ -288,7 +288,6 @@ xdsIR: name: "" prefix: /foo traffic: - name: envoy-gateway/policy-for-gateway requestBuffer: limit: 4Mi readyListener: @@ -338,7 +337,6 @@ xdsIR: name: "" prefix: /foo traffic: - name: default/policy-for-route requestBuffer: limit: 4Mi readyListener: diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-status-conditions.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-status-conditions.out.yaml index fdefe01e1e..daf9e5d208 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-status-conditions.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-status-conditions.out.yaml @@ -584,8 +584,7 @@ xdsIR: distinct: false name: "" prefix: / - traffic: - name: envoy-gateway/target-httproute-in-gateway-1 + traffic: {} readyListener: address: 0.0.0.0 ipFamily: IPv4 @@ -632,8 +631,7 @@ xdsIR: name: grpcroute-1 namespace: envoy-gateway name: grpcroute/envoy-gateway/grpcroute-1/rule/0/match/0/* - traffic: - name: envoy-gateway/target-grpcroute-in-gateway-2 + traffic: {} readyListener: address: 0.0.0.0 ipFamily: IPv4 diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-status-fault-injection.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-status-fault-injection.out.yaml index a180cead0b..41e2c5a0cc 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-status-fault-injection.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-status-fault-injection.out.yaml @@ -371,7 +371,6 @@ xdsIR: delay: fixedDelay: 5.4s percentage: 80 - name: default/policy-for-grpcroute readyListener: address: 0.0.0.0 ipFamily: IPv4 @@ -423,7 +422,6 @@ xdsIR: abort: httpStatus: 14 percentage: 0.01 - name: envoy-gateway/policy-for-gateway - destination: name: httproute/default/httproute-1/rule/0 settings: @@ -453,7 +451,6 @@ xdsIR: delay: fixedDelay: 5.4s percentage: 80 - name: default/policy-for-route readyListener: address: 0.0.0.0 ipFamily: IPv4 diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-strategic-merge-global-ratelimit.in.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-strategic-merge-global-ratelimit.in.yaml new file mode 100644 index 0000000000..585a70dc01 --- /dev/null +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-strategic-merge-global-ratelimit.in.yaml @@ -0,0 +1,115 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-2 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + - namespace: envoy-gateway + name: gateway-2 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +backendTrafficPolicies: + - apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: policy-for-gateway1 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + timeout: + tcp: + connectTimeout: 15s + http: + connectionIdleTimeout: 16s + maxConnectionDuration: 17s + httpUpgrade: + - type: websocket + rateLimit: + type: Global + global: + rules: + - clientSelectors: + - headers: + - name: x-user-id + type: Exact + value: two + limit: + requests: 5 + unit: Hour + shared: true + - apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: default + name: policy-for-route + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + mergeType: StrategicMerge + timeout: + tcp: + connectTimeout: 10s + connection: + bufferLimit: 100M + httpUpgrade: + - type: "spdy/3.1" + rateLimit: + type: Global + global: + rules: + - clientSelectors: + - headers: + - name: x-user-id + type: Exact + value: one + limit: + requests: 5 + unit: Hour + shared: true diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-strategic-merge-global-ratelimit.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-strategic-merge-global-ratelimit.out.yaml new file mode 100644 index 0000000000..c70c9d724a --- /dev/null +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-strategic-merge-global-ratelimit.out.yaml @@ -0,0 +1,443 @@ +backendTrafficPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-route + namespace: default + spec: + connection: + bufferLimit: 100M + httpUpgrade: + - type: spdy/3.1 + mergeType: StrategicMerge + rateLimit: + global: + rules: + - clientSelectors: + - headers: + - name: x-user-id + type: Exact + value: one + limit: + requests: 5 + unit: Hour + shared: true + type: Global + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + timeout: + tcp: + connectTimeout: 10s + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Merged with policy envoy-gateway/policy-for-gateway1 + reason: Merged + status: "True" + type: Merged + controllerName: gateway.envoyproxy.io/gatewayclass-controller + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + sectionName: http + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: gateway.envoyproxy.io/gatewayclass-controller + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + sectionName: 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: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway1 + namespace: envoy-gateway + spec: + httpUpgrade: + - type: websocket + rateLimit: + global: + rules: + - clientSelectors: + - headers: + - name: x-user-id + type: Exact + value: two + limit: + requests: 5 + unit: Hour + shared: true + type: Global + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + timeout: + http: + connectionIdleTimeout: 16s + maxConnectionDuration: 17s + tcp: + connectTimeout: 15s + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: 'This policy is being merged by other backendTrafficPolicies for + these routes: [default/httproute-1]' + reason: Merged + status: "True" + type: Merged + controllerName: gateway.envoyproxy.io/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + 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 +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + 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 +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + - name: gateway-2 + namespace: envoy-gateway + sectionName: http + 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: + name: gateway-1 + namespace: envoy-gateway + sectionName: http + - 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: + name: gateway-2 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http-80 + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 + namespace: envoy-gateway-system + envoy-gateway/gateway-2: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-2/http + ports: + - containerPort: 10080 + name: http-80 + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-2 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-2 + namespace: envoy-gateway-system +xdsIR: + envoy-gateway/gateway-1: + accessLog: + json: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + metadata: + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + sectionName: http + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + name: httproute/default/httproute-1/rule/0/backend/0 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + isHTTP2: false + metadata: + kind: HTTPRoute + name: httproute-1 + namespace: default + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + traffic: + backendConnection: + bufferLimit: 100000000 + httpUpgrade: + - spdy/3.1 + - websocket + rateLimit: + global: + rules: + - headerMatches: + - distinct: false + exact: one + name: x-user-id + limit: + requests: 5 + unit: Hour + name: default/policy-for-route/rule/0 + shared: true + - headerMatches: + - distinct: false + exact: two + name: x-user-id + limit: + requests: 5 + unit: Hour + name: envoy-gateway/policy-for-gateway1/rule/0 + shared: true + timeout: + http: + connectionIdleTimeout: 16s + maxConnectionDuration: 17s + tcp: + connectTimeout: 10s + readyListener: + address: 0.0.0.0 + ipFamily: IPv4 + path: /ready + port: 19003 + envoy-gateway/gateway-2: + accessLog: + json: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + metadata: + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + sectionName: http + name: envoy-gateway/gateway-2/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + name: httproute/default/httproute-1/rule/0/backend/0 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + isHTTP2: false + metadata: + kind: HTTPRoute + name: httproute-1 + namespace: default + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + traffic: + backendConnection: + bufferLimit: 100000000 + httpUpgrade: + - spdy/3.1 + rateLimit: + global: + rules: + - headerMatches: + - distinct: false + exact: one + name: x-user-id + limit: + requests: 5 + unit: Hour + name: default/policy-for-route/rule/0 + shared: true + timeout: + tcp: + connectTimeout: 10s + readyListener: + address: 0.0.0.0 + ipFamily: IPv4 + path: /ready + port: 19003 diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-strategic-merge-local-ratelimit.in.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-strategic-merge-local-ratelimit.in.yaml new file mode 100644 index 0000000000..3a9c8ccf77 --- /dev/null +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-strategic-merge-local-ratelimit.in.yaml @@ -0,0 +1,113 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-2 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + - namespace: envoy-gateway + name: gateway-2 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +backendTrafficPolicies: + - apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: policy-for-gateway1 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + timeout: + tcp: + connectTimeout: 15s + http: + connectionIdleTimeout: 16s + maxConnectionDuration: 17s + httpUpgrade: + - type: websocket + rateLimit: + type: Local + local: + rules: + - clientSelectors: + - headers: + - name: x-user-id + type: Exact + value: two + limit: + requests: 5 + unit: Hour + - apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: default + name: policy-for-route + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + mergeType: StrategicMerge + timeout: + tcp: + connectTimeout: 10s + connection: + bufferLimit: 100M + httpUpgrade: + - type: "spdy/3.1" + rateLimit: + type: Local + local: + rules: + - clientSelectors: + - headers: + - name: x-user-id + type: Exact + value: one + limit: + requests: 5 + unit: Hour diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-strategic-merge-local-ratelimit.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-strategic-merge-local-ratelimit.out.yaml new file mode 100644 index 0000000000..e0eb784ada --- /dev/null +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-strategic-merge-local-ratelimit.out.yaml @@ -0,0 +1,444 @@ +backendTrafficPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-route + namespace: default + spec: + connection: + bufferLimit: 100M + httpUpgrade: + - type: spdy/3.1 + mergeType: StrategicMerge + rateLimit: + local: + rules: + - clientSelectors: + - headers: + - name: x-user-id + type: Exact + value: one + limit: + requests: 5 + unit: Hour + type: Local + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + timeout: + tcp: + connectTimeout: 10s + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Merged with policy envoy-gateway/policy-for-gateway1 + reason: Merged + status: "True" + type: Merged + controllerName: gateway.envoyproxy.io/gatewayclass-controller + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + sectionName: http + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: gateway.envoyproxy.io/gatewayclass-controller + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + sectionName: 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: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway1 + namespace: envoy-gateway + spec: + httpUpgrade: + - type: websocket + rateLimit: + local: + rules: + - clientSelectors: + - headers: + - name: x-user-id + type: Exact + value: two + limit: + requests: 5 + unit: Hour + type: Local + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + timeout: + http: + connectionIdleTimeout: 16s + maxConnectionDuration: 17s + tcp: + connectTimeout: 15s + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: 'This policy is being merged by other backendTrafficPolicies for + these routes: [default/httproute-1]' + reason: Merged + status: "True" + type: Merged + controllerName: gateway.envoyproxy.io/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + 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 +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + 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 +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + - name: gateway-2 + namespace: envoy-gateway + sectionName: http + 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: + name: gateway-1 + namespace: envoy-gateway + sectionName: http + - 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: + name: gateway-2 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http-80 + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 + namespace: envoy-gateway-system + envoy-gateway/gateway-2: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-2/http + ports: + - containerPort: 10080 + name: http-80 + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-2 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-2 + namespace: envoy-gateway-system +xdsIR: + envoy-gateway/gateway-1: + accessLog: + json: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + metadata: + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + sectionName: http + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + name: httproute/default/httproute-1/rule/0/backend/0 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + isHTTP2: false + metadata: + kind: HTTPRoute + name: httproute-1 + namespace: default + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + traffic: + backendConnection: + bufferLimit: 100000000 + httpUpgrade: + - spdy/3.1 + - websocket + rateLimit: + local: + default: + requests: 4294967295 + unit: Second + rules: + - headerMatches: + - distinct: false + exact: one + name: x-user-id + limit: + requests: 5 + unit: Hour + name: default/policy-for-route/rule/0 + - headerMatches: + - distinct: false + exact: two + name: x-user-id + limit: + requests: 5 + unit: Hour + name: envoy-gateway/policy-for-gateway1/rule/0 + timeout: + http: + connectionIdleTimeout: 16s + maxConnectionDuration: 17s + tcp: + connectTimeout: 10s + readyListener: + address: 0.0.0.0 + ipFamily: IPv4 + path: /ready + port: 19003 + envoy-gateway/gateway-2: + accessLog: + json: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + metadata: + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + sectionName: http + name: envoy-gateway/gateway-2/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + name: httproute/default/httproute-1/rule/0/backend/0 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + isHTTP2: false + metadata: + kind: HTTPRoute + name: httproute-1 + namespace: default + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + traffic: + backendConnection: + bufferLimit: 100000000 + httpUpgrade: + - spdy/3.1 + rateLimit: + local: + default: + requests: 4294967295 + unit: Second + rules: + - headerMatches: + - distinct: false + exact: one + name: x-user-id + limit: + requests: 5 + unit: Hour + name: default/policy-for-route/rule/0 + timeout: + tcp: + connectTimeout: 10s + readyListener: + address: 0.0.0.0 + ipFamily: IPv4 + path: /ready + port: 19003 diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-strategic-merge-with-multi-parents.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-strategic-merge-with-multi-parents.out.yaml index 70ff8aee37..1c466803e1 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-strategic-merge-with-multi-parents.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-strategic-merge-with-multi-parents.out.yaml @@ -317,7 +317,6 @@ xdsIR: httpUpgrade: - spdy/3.1 - websocket - name: default/policy-for-route timeout: http: connectionIdleTimeout: 16s @@ -375,7 +374,6 @@ xdsIR: bufferLimit: 100000000 httpUpgrade: - spdy/3.1 - name: default/policy-for-route timeout: tcp: connectTimeout: 10s diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-strategic-merge.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-strategic-merge.out.yaml index 8905ab639d..f7cdccc6c8 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-strategic-merge.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-strategic-merge.out.yaml @@ -297,7 +297,6 @@ xdsIR: httpUpgrade: - spdy/3.1 - websocket - name: default/merged-policy-for-route timeout: http: connectionIdleTimeout: 16s @@ -324,7 +323,6 @@ xdsIR: traffic: backendConnection: bufferLimit: 100000000 - name: default/policy-for-route readyListener: address: 0.0.0.0 ipFamily: IPv4 diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-tracing.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-tracing.out.yaml index 03a2b28d43..c1996019f4 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-tracing.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-tracing.out.yaml @@ -185,7 +185,6 @@ xdsIR: name: "" prefix: / traffic: - name: default/policy-for-route telemetry: tracing: customTags: diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-use-client-protocol.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-use-client-protocol.out.yaml index c03c4cbc11..d3cb4bdd73 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-use-client-protocol.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-use-client-protocol.out.yaml @@ -164,8 +164,7 @@ xdsIR: distinct: false name: "" prefix: / - traffic: - name: envoy-gateway/policy-for-gateway + traffic: {} useClientProtocol: true readyListener: address: 0.0.0.0 diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-circuitbreakers.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-circuitbreakers.out.yaml index 34f11b574a..919e9fdb8e 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-circuitbreakers.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-circuitbreakers.out.yaml @@ -294,7 +294,6 @@ xdsIR: maxParallelRetries: 1024 maxPendingRequests: 1 maxRequestsPerConnection: 1 - name: envoy-gateway/policy-for-gateway readyListener: address: 0.0.0.0 ipFamily: IPv4 @@ -348,7 +347,6 @@ xdsIR: maxParallelRetries: 24 maxPendingRequests: 42 maxRequestsPerConnection: 42 - name: default/policy-for-route readyListener: address: 0.0.0.0 ipFamily: IPv4 diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-dns-settings.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-dns-settings.out.yaml index c92652e8d4..d42ca5614c 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-dns-settings.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-dns-settings.out.yaml @@ -360,7 +360,6 @@ xdsIR: dns: dnsRefreshRate: 10s respectDnsTtl: true - name: envoy-gateway/policy-for-all-routes-in-gateway-1 readyListener: address: 0.0.0.0 ipFamily: IPv4 @@ -411,7 +410,6 @@ xdsIR: dns: dnsRefreshRate: 5s respectDnsTtl: false - name: default/policy-for-route-2 - destination: name: httproute/default/httproute-1/rule/0 settings: @@ -437,7 +435,6 @@ xdsIR: dns: dnsRefreshRate: 1s respectDnsTtl: true - name: default/policy-for-route-1 readyListener: address: 0.0.0.0 ipFamily: IPv4 diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-healthcheck.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-healthcheck.out.yaml index 75d73b9a78..96dff6b971 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-healthcheck.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-healthcheck.out.yaml @@ -736,7 +736,6 @@ xdsIR: interval: 2s maxEjectionPercent: 100 splitExternalLocalOriginErrors: false - name: envoy-gateway/policy-for-gateway - destination: name: grpcroute/default/grpcroute-3/rule/0 settings: @@ -763,7 +762,6 @@ xdsIR: interval: 3s timeout: 1s unhealthyThreshold: 3 - name: default/policy-for-grpc-route-3 - destination: name: grpcroute/default/grpcroute-2/rule/0 settings: @@ -789,7 +787,6 @@ xdsIR: interval: 3s timeout: 1s unhealthyThreshold: 3 - name: default/policy-for-grpc-route readyListener: address: 0.0.0.0 ipFamily: IPv4 @@ -856,7 +853,6 @@ xdsIR: interval: 10s maxEjectionPercent: 10 splitExternalLocalOriginErrors: false - name: default/policy-for-route-2 - destination: name: httproute/default/httproute-3/rule/0 settings: @@ -898,7 +894,6 @@ xdsIR: interval: 8ms maxEjectionPercent: 11 splitExternalLocalOriginErrors: false - name: default/policy-for-route-3 - destination: name: httproute/default/httproute-4/rule/0 settings: @@ -935,7 +930,6 @@ xdsIR: interval: 5s timeout: 1s unhealthyThreshold: 3 - name: default/policy-for-route-4 - destination: name: httproute/default/httproute-1/rule/0 settings: @@ -981,7 +975,6 @@ xdsIR: interval: 1s maxEjectionPercent: 100 splitExternalLocalOriginErrors: false - name: default/policy-for-route-1 readyListener: address: 0.0.0.0 ipFamily: IPv4 diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-http2.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-http2.out.yaml index a353466758..9b75868372 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-http2.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-http2.out.yaml @@ -291,7 +291,6 @@ xdsIR: initialStreamWindowSize: 1073741824 maxConcurrentStreams: 500 resetStreamOnError: false - name: envoy-gateway/policy-for-gateway readyListener: address: 0.0.0.0 ipFamily: IPv4 @@ -344,7 +343,6 @@ xdsIR: initialStreamWindowSize: 524288000 maxConcurrentStreams: 200 resetStreamOnError: true - name: default/policy-for-route readyListener: address: 0.0.0.0 ipFamily: IPv4 diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-httproute-timeout.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-httproute-timeout.out.yaml index d0285325ea..de18621593 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-httproute-timeout.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-httproute-timeout.out.yaml @@ -246,8 +246,7 @@ xdsIR: name: "" prefix: / timeout: 2m10s - traffic: - name: default/policy-for-http-route-1 + traffic: {} useClientProtocol: true - destination: name: httproute/default/httproute-2/rule/0 @@ -276,7 +275,6 @@ xdsIR: consistentHash: cookie: name: test - name: envoy-gateway/policy-for-gateway useClientProtocol: true readyListener: address: 0.0.0.0 diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-loadbalancer.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-loadbalancer.out.yaml index 2b69a9babc..3a5678c79b 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-loadbalancer.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-loadbalancer.out.yaml @@ -458,7 +458,6 @@ xdsIR: traffic: loadBalancer: random: {} - name: envoy-gateway/policy-for-gateway readyListener: address: 0.0.0.0 ipFamily: IPv4 @@ -510,7 +509,6 @@ xdsIR: leastRequest: slowStart: window: 5m0s - name: default/policy-for-route2 - destination: name: httproute/default/httproute-3/rule/0 settings: @@ -537,7 +535,6 @@ xdsIR: consistentHash: cookie: name: test - name: default/policy-for-route3 - destination: name: httproute/default/httproute-1/rule/0 settings: @@ -564,7 +561,6 @@ xdsIR: consistentHash: sourceIP: true tableSize: 524287 - name: default/policy-for-route readyListener: address: 0.0.0.0 ipFamily: IPv4 diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-local-ratelimit-default-route-level-limit.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-local-ratelimit-default-route-level-limit.out.yaml index 6faf1dd119..fffa3d8ef9 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-local-ratelimit-default-route-level-limit.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-local-ratelimit-default-route-level-limit.out.yaml @@ -188,7 +188,6 @@ xdsIR: name: "" prefix: / traffic: - name: envoy-gateway/policy-for-gateway rateLimit: local: default: @@ -205,6 +204,7 @@ xdsIR: limit: requests: 10 unit: Hour + name: envoy-gateway/policy-for-gateway/rule/0 - cidrMatch: cidr: 192.168.0.0/16 distinct: false @@ -221,6 +221,7 @@ xdsIR: limit: requests: 10 unit: Minute + name: envoy-gateway/policy-for-gateway/rule/1 readyListener: address: 0.0.0.0 ipFamily: IPv4 diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-local-ratelimit-distinct-match-type.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-local-ratelimit-distinct-match-type.out.yaml index ad18d64714..bb901670be 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-local-ratelimit-distinct-match-type.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-local-ratelimit-distinct-match-type.out.yaml @@ -188,7 +188,6 @@ xdsIR: name: "" prefix: / traffic: - name: envoy-gateway/policy-for-gateway rateLimit: local: default: @@ -204,6 +203,7 @@ xdsIR: limit: requests: 10 unit: Hour + name: envoy-gateway/policy-for-gateway/rule/0 - cidrMatch: cidr: 192.168.0.0/16 distinct: false @@ -220,6 +220,7 @@ xdsIR: limit: requests: 10 unit: Minute + name: envoy-gateway/policy-for-gateway/rule/1 readyListener: address: 0.0.0.0 ipFamily: IPv4 diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-local-ratelimit.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-local-ratelimit.out.yaml index 1f70bc3c26..7509c84446 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-local-ratelimit.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-local-ratelimit.out.yaml @@ -191,7 +191,6 @@ xdsIR: name: "" prefix: / traffic: - name: envoy-gateway/policy-for-gateway rateLimit: local: default: @@ -208,6 +207,7 @@ xdsIR: limit: requests: 10 unit: Hour + name: envoy-gateway/policy-for-gateway/rule/1 - cidrMatch: cidr: 192.168.0.0/16 distinct: false @@ -224,6 +224,7 @@ xdsIR: limit: requests: 10 unit: Minute + name: envoy-gateway/policy-for-gateway/rule/2 readyListener: address: 0.0.0.0 ipFamily: IPv4 diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-panic-threshold.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-panic-threshold.out.yaml index 5054a68777..cb30f5b23c 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-panic-threshold.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-panic-threshold.out.yaml @@ -356,7 +356,6 @@ xdsIR: traffic: healthCheck: panicThreshold: 80 - name: envoy-gateway/policy-for-all-routes-in-gateway-1 readyListener: address: 0.0.0.0 ipFamily: IPv4 @@ -406,7 +405,6 @@ xdsIR: traffic: healthCheck: panicThreshold: 10 - name: default/policy-for-route-2 - destination: name: httproute/default/httproute-1/rule/0 settings: @@ -431,7 +429,6 @@ xdsIR: traffic: healthCheck: panicThreshold: 66 - name: default/policy-for-route-1 readyListener: address: 0.0.0.0 ipFamily: IPv4 diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-proxyprotocol.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-proxyprotocol.out.yaml index dfadd197e0..45e87169d4 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-proxyprotocol.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-proxyprotocol.out.yaml @@ -280,7 +280,6 @@ xdsIR: namespace: default name: grpcroute/default/grpcroute-1/rule/0/match/-1/* traffic: - name: envoy-gateway/policy-for-gateway proxyProtocol: version: V1 readyListener: @@ -330,7 +329,6 @@ xdsIR: name: "" prefix: / traffic: - name: default/policy-for-route proxyProtocol: version: V2 readyListener: diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-ratelimit.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-ratelimit.out.yaml index c0c8debf20..435096820c 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-ratelimit.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-ratelimit.out.yaml @@ -312,7 +312,6 @@ xdsIR: namespace: default name: grpcroute/default/grpcroute-1/rule/0/match/-1/* traffic: - name: envoy-gateway/policy-for-gateway rateLimit: global: rules: @@ -329,6 +328,7 @@ xdsIR: limit: requests: 10 unit: Hour + name: envoy-gateway/policy-for-gateway/rule/0 readyListener: address: 0.0.0.0 ipFamily: IPv4 @@ -376,7 +376,6 @@ xdsIR: name: "" prefix: / traffic: - name: default/policy-for-route rateLimit: global: rules: @@ -390,6 +389,7 @@ xdsIR: limit: requests: 20 unit: Hour + name: default/policy-for-route/rule/0 requestCost: number: 1 responseCost: diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-response-override.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-response-override.out.yaml index c7cc354afc..15c062a97b 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-response-override.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-response-override.out.yaml @@ -399,7 +399,6 @@ xdsIR: namespace: default name: grpcroute/default/grpcroute-1/rule/0/match/-1/* traffic: - name: default/policy-for-gateway responseOverride: name: backendtrafficpolicy/default/policy-for-gateway rules: @@ -470,7 +469,6 @@ xdsIR: name: "" prefix: / traffic: - name: default/policy-for-route-1 responseOverride: name: backendtrafficpolicy/default/policy-for-route-1 rules: @@ -516,7 +514,6 @@ xdsIR: name: "" prefix: / traffic: - name: default/policy-for-route-2 responseOverride: name: backendtrafficpolicy/default/policy-for-route-2 rules: diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-retries.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-retries.out.yaml index 5b1f96de97..5785fcf001 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-retries.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-retries.out.yaml @@ -436,7 +436,6 @@ xdsIR: namespace: default name: grpcroute/default/grpcroute-1/rule/0/match/-1/* traffic: - name: envoy-gateway/policy-for-gateway retry: perRetry: backOff: @@ -539,7 +538,6 @@ xdsIR: name: "" prefix: / traffic: - name: default/policy-for-route-1 retry: numRetries: 5 perRetry: diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-same-prefix-httproutes.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-same-prefix-httproutes.out.yaml index a449e2ea13..d2ae6de9a4 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-same-prefix-httproutes.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-same-prefix-httproutes.out.yaml @@ -211,7 +211,6 @@ xdsIR: maxConnections: 2048 maxParallelRequests: 4294967295 maxPendingRequests: 1 - name: default/policy-for-httproute-1 - destination: name: httproute/default/httproute-1-1/rule/0 settings: diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-shared-ratelimit.in.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-shared-ratelimit.in.yaml index 16513852d3..3386225ff7 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-shared-ratelimit.in.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-shared-ratelimit.in.yaml @@ -76,7 +76,6 @@ backendTrafficPolicies: rateLimit: type: Global global: - shared: true rules: - clientSelectors: - headers: @@ -90,6 +89,7 @@ backendTrafficPolicies: limit: requests: 10 unit: Hour + shared: true - apiVersion: gateway.envoyproxy.io/v1alpha1 kind: BackendTrafficPolicy metadata: @@ -103,7 +103,6 @@ backendTrafficPolicies: rateLimit: type: Global global: - shared: true rules: - clientSelectors: - sourceCIDR: @@ -112,6 +111,7 @@ backendTrafficPolicies: limit: requests: 20 unit: Hour + shared: true - clientSelectors: - sourceCIDR: type: "Distinct" @@ -119,3 +119,4 @@ backendTrafficPolicies: limit: requests: 20 unit: Hour + shared: true diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-shared-ratelimit.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-shared-ratelimit.out.yaml index 2ff5ac3922..90a365156f 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-shared-ratelimit.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-shared-ratelimit.out.yaml @@ -16,6 +16,7 @@ backendTrafficPolicies: limit: requests: 20 unit: Hour + shared: true - clientSelectors: - sourceCIDR: type: Distinct @@ -23,7 +24,7 @@ backendTrafficPolicies: limit: requests: 20 unit: Hour - shared: true + shared: true type: Global targetRef: group: gateway.networking.k8s.io @@ -66,7 +67,7 @@ backendTrafficPolicies: limit: requests: 10 unit: Hour - shared: true + shared: true type: Global targetRef: group: gateway.networking.k8s.io @@ -312,7 +313,6 @@ xdsIR: namespace: default name: grpcroute/default/grpcroute-1/rule/0/match/-1/* traffic: - name: envoy-gateway/policy-for-gateway rateLimit: global: rules: @@ -329,7 +329,8 @@ xdsIR: limit: requests: 10 unit: Hour - shared: true + name: envoy-gateway/policy-for-gateway/rule/0 + shared: true readyListener: address: 0.0.0.0 ipFamily: IPv4 @@ -377,7 +378,6 @@ xdsIR: name: "" prefix: / traffic: - name: default/policy-for-route rateLimit: global: rules: @@ -391,6 +391,8 @@ xdsIR: limit: requests: 20 unit: Hour + name: default/policy-for-route/rule/0 + shared: true - cidrMatch: cidr: ::/64 distinct: true @@ -401,7 +403,8 @@ xdsIR: limit: requests: 20 unit: Hour - shared: true + name: default/policy-for-route/rule/1 + shared: true readyListener: address: 0.0.0.0 ipFamily: IPv4 diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-tcpkeepalive.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-tcpkeepalive.out.yaml index f614c6eb96..7c9c2587eb 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-tcpkeepalive.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-tcpkeepalive.out.yaml @@ -284,7 +284,6 @@ xdsIR: namespace: default name: grpcroute/default/grpcroute-1/rule/0/match/-1/* traffic: - name: envoy-gateway/policy-for-gateway tcpKeepalive: idleTime: 1200 interval: 60 @@ -336,7 +335,6 @@ xdsIR: name: "" prefix: / traffic: - name: default/policy-for-route tcpKeepalive: idleTime: 10 interval: 1800 diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-timeout-targetrefs.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-timeout-targetrefs.out.yaml index eb123fd100..d8a27d8b07 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-timeout-targetrefs.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-timeout-targetrefs.out.yaml @@ -272,7 +272,6 @@ xdsIR: namespace: envoy-gateway name: grpcroute/envoy-gateway/grpcroute-1/rule/0/match/-1/* traffic: - name: envoy-gateway/policy-for-gateway timeout: http: connectionIdleTimeout: 16s @@ -326,7 +325,6 @@ xdsIR: name: "" prefix: / traffic: - name: envoy-gateway/policy-for-gateway timeout: http: connectionIdleTimeout: 16s diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-timeout.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-timeout.out.yaml index 40e234b1f6..f567963c6a 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-timeout.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-timeout.out.yaml @@ -292,7 +292,6 @@ xdsIR: namespace: default name: grpcroute/default/grpcroute-1/rule/0/match/-1/* traffic: - name: envoy-gateway/policy-for-gateway timeout: http: connectionIdleTimeout: 16s @@ -348,7 +347,6 @@ xdsIR: prefix: / timeout: 1s traffic: - name: default/policy-for-route timeout: http: connectionIdleTimeout: 21s diff --git a/internal/gatewayapi/testdata/httproute-and-backendtrafficpolicy-with-timeout.out.yaml b/internal/gatewayapi/testdata/httproute-and-backendtrafficpolicy-with-timeout.out.yaml index 5dffce1c94..3b14a31d9f 100644 --- a/internal/gatewayapi/testdata/httproute-and-backendtrafficpolicy-with-timeout.out.yaml +++ b/internal/gatewayapi/testdata/httproute-and-backendtrafficpolicy-with-timeout.out.yaml @@ -289,7 +289,6 @@ xdsIR: namespace: default name: grpcroute/default/grpcroute-1/rule/0/match/-1/* traffic: - name: envoy-gateway/policy-for-gateway timeout: http: connectionIdleTimeout: 16s @@ -344,7 +343,6 @@ xdsIR: prefix: / timeout: 1s traffic: - name: default/policy-for-route timeout: http: maxConnectionDuration: 22s diff --git a/internal/gatewayapi/testdata/httproute-retry.out.yaml b/internal/gatewayapi/testdata/httproute-retry.out.yaml index d252da5b59..2d9e06df3a 100644 --- a/internal/gatewayapi/testdata/httproute-retry.out.yaml +++ b/internal/gatewayapi/testdata/httproute-retry.out.yaml @@ -310,7 +310,6 @@ xdsIR: - 500 timeout: 3s traffic: - name: default/policy-for-route retry: numRetries: 5 perRetry: diff --git a/internal/gatewayapi/testdata/merge-with-isolated-policies-2.out.yaml b/internal/gatewayapi/testdata/merge-with-isolated-policies-2.out.yaml index 4d416250bc..4a93a03edb 100644 --- a/internal/gatewayapi/testdata/merge-with-isolated-policies-2.out.yaml +++ b/internal/gatewayapi/testdata/merge-with-isolated-policies-2.out.yaml @@ -555,7 +555,6 @@ xdsIR: - x-header-8 maxAge: 33m20s traffic: - name: default/policy-for-gateway tcpKeepalive: idleTime: 1200 interval: 60 @@ -616,7 +615,6 @@ xdsIR: - x-header-8 maxAge: 33m20s traffic: - name: default/policy-for-gateway tcpKeepalive: idleTime: 1200 interval: 60 diff --git a/internal/gatewayapi/testdata/merge-with-isolated-policies.out.yaml b/internal/gatewayapi/testdata/merge-with-isolated-policies.out.yaml index 501ed5795c..b56dfd2852 100644 --- a/internal/gatewayapi/testdata/merge-with-isolated-policies.out.yaml +++ b/internal/gatewayapi/testdata/merge-with-isolated-policies.out.yaml @@ -347,7 +347,6 @@ xdsIR: - x-header-8 maxAge: 33m20s traffic: - name: default/policy-for-gateway tcpKeepalive: idleTime: 1200 interval: 60 diff --git a/internal/ir/xds.go b/internal/ir/xds.go index 777e33eb9c..30bd1d1fcb 100644 --- a/internal/ir/xds.go +++ b/internal/ir/xds.go @@ -835,8 +835,6 @@ type Compression struct { // TrafficFeatures holds the information associated with the Backend Traffic Policy. // +k8s:deepcopy-gen=true type TrafficFeatures struct { - // Name of the backend traffic policy and namespace - Name string `json:"name,omitempty"` // RateLimit defines the more specific match conditions as well as limits for ratelimiting // the requests on this route. RateLimit *RateLimit `json:"rateLimit,omitempty" yaml:"rateLimit,omitempty"` @@ -2093,16 +2091,7 @@ type GlobalRateLimit struct { // TODO zhaohuabing: add default values for Global rate limiting. // Rules for rate limiting. - Rules []*RateLimitRule `json:"rules,omitempty" yaml:"rules,omitempty"` - - // Shared determines whether this rate limit rule applies globally across the gateway, or xRoute(s). - // If set to true, the rule is treated as a common bucket and is shared across all routes under the backend traffic policy. - // Must have targetRef set to Gateway or xRoute(s). - // Default: false. - // - // +optional - // +kubebuilder:default=false - Shared *bool `json:"shared,omitempty" yaml:"shared,omitempty"` + Rules []*RateLimitRule `json:"rules,omitempty" yaml:"rules,omitempty" patchStrategy:"merge" patchMergeKey:"name"` } // LocalRateLimit holds the local rate limiting configuration. @@ -2113,7 +2102,7 @@ type LocalRateLimit struct { Default RateLimitValue `json:"default,omitempty" yaml:"default,omitempty"` // Rules for rate limiting. - Rules []*RateLimitRule `json:"rules,omitempty" yaml:"rules,omitempty"` + Rules []*RateLimitRule `json:"rules,omitempty" yaml:"rules,omitempty" patchStrategy:"merge" patchMergeKey:"name"` } // RateLimitRule holds the match and limit configuration for ratelimiting. @@ -2129,6 +2118,15 @@ type RateLimitRule struct { RequestCost *RateLimitCost `json:"requestCost,omitempty" yaml:"requestCost,omitempty"` // ResponseCost specifies the cost of the response. ResponseCost *RateLimitCost `json:"responseCost,omitempty" yaml:"responseCost,omitempty"` + // Shared determines whether this rate limit rule applies globally across the gateway, or xRoute(s). + // If set to true, the rule is treated as a common bucket and is shared across all routes under the backend traffic policy. + // Must have targetRef set to Gateway or xRoute(s). + // Default: false. + // + // +optional + Shared *bool `json:"shared,omitempty" yaml:"shared,omitempty"` + // Name is a unique identifier for this rule, set as //rule/. + Name string `json:"name,omitempty" yaml:"name,omitempty"` } // RateLimitCost specifies the cost of the request or response. diff --git a/internal/ir/zz_generated.deepcopy.go b/internal/ir/zz_generated.deepcopy.go index 5b27f1e48f..eb5f63edb8 100644 --- a/internal/ir/zz_generated.deepcopy.go +++ b/internal/ir/zz_generated.deepcopy.go @@ -1306,11 +1306,6 @@ func (in *GlobalRateLimit) DeepCopyInto(out *GlobalRateLimit) { } } } - if in.Shared != nil { - in, out := &in.Shared, &out.Shared - *out = new(bool) - **out = **in - } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GlobalRateLimit. @@ -2753,6 +2748,11 @@ func (in *RateLimitRule) DeepCopyInto(out *RateLimitRule) { *out = new(RateLimitCost) (*in).DeepCopyInto(*out) } + if in.Shared != nil { + in, out := &in.Shared, &out.Shared + *out = new(bool) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RateLimitRule. diff --git a/internal/utils/merge.go b/internal/utils/merge.go index 337147ed01..2c02230906 100644 --- a/internal/utils/merge.go +++ b/internal/utils/merge.go @@ -14,6 +14,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" + "github.com/envoyproxy/gateway/internal/ir" ) func MergeWithPatch[T client.Object](original T, patch *egv1a1.KubernetesPatchSpec) (T, error) { @@ -77,3 +78,52 @@ func Merge[T client.Object](original, patch T, mergeType egv1a1.MergeType) (T, e } return mergeInternal(original, patchJSON, mergeType) } + +func mergeRLInternal[T *ir.RateLimit](original T, patchJSON []byte, mergeType egv1a1.MergeType) (T, error) { + var ( + patchedJSON []byte + originalJSON []byte + err error + empty T + ) + + originalJSON, err = json.Marshal(original) + if err != nil { + return empty, fmt.Errorf("error marshaling original service: %w", err) + } + switch mergeType { + case egv1a1.StrategicMerge: + patchedJSON, err = strategicpatch.StrategicMergePatch(originalJSON, patchJSON, empty) + if err != nil { + return empty, fmt.Errorf("error during strategic merge: %w", err) + } + case egv1a1.JSONMerge: + patchedJSON, err = jsonpatch.MergePatch(originalJSON, patchJSON) + if err != nil { + return empty, fmt.Errorf("error during JSON merge: %w", err) + } + default: + return empty, fmt.Errorf("unsupported merge type: %v", mergeType) + } + + res := new(T) + if err = json.Unmarshal(patchedJSON, res); err != nil { + return empty, fmt.Errorf("error unmarshaling patched service: %w", err) + } + + return *res, nil +} + +func MergeRL[T *ir.RateLimit](original, patch T, mergeType egv1a1.MergeType) (T, error) { + var ( + patchJSON []byte + err error + empty T + ) + + patchJSON, err = json.Marshal(patch) + if err != nil { + return empty, fmt.Errorf("error marshaling original service: %w", err) + } + return mergeRLInternal(original, patchJSON, mergeType) +} diff --git a/internal/utils/testdata/backendtrafficpolicy_ratelimitrule_merge.in.yaml b/internal/utils/testdata/backendtrafficpolicy_ratelimitrule_merge.in.yaml index 3dc8eee659..3bf35dc887 100644 --- a/internal/utils/testdata/backendtrafficpolicy_ratelimitrule_merge.in.yaml +++ b/internal/utils/testdata/backendtrafficpolicy_ratelimitrule_merge.in.yaml @@ -1,12 +1,12 @@ kind: BackendTrafficPolicy metadata: - name: original + name: original-1 spec: rateLimit: type: Global global: rules: - - name: one + - shared: true clientSelectors: - headers: - name: x-user-id diff --git a/internal/utils/testdata/backendtrafficpolicy_ratelimitrule_merge.jsonmerge.out.yaml b/internal/utils/testdata/backendtrafficpolicy_ratelimitrule_merge.jsonmerge.out.yaml index a679b04d1b..17f6d2cb0e 100644 --- a/internal/utils/testdata/backendtrafficpolicy_ratelimitrule_merge.jsonmerge.out.yaml +++ b/internal/utils/testdata/backendtrafficpolicy_ratelimitrule_merge.jsonmerge.out.yaml @@ -1,7 +1,7 @@ kind: BackendTrafficPolicy metadata: creationTimestamp: null - name: original + name: original-2 spec: rateLimit: global: @@ -23,7 +23,7 @@ spec: limit: requests: 21 unit: Hour - name: two + shared: false type: Global status: ancestors: null diff --git a/internal/utils/testdata/backendtrafficpolicy_ratelimitrule_merge.patch.yaml b/internal/utils/testdata/backendtrafficpolicy_ratelimitrule_merge.patch.yaml index 2d13bd03b0..40b6d91867 100644 --- a/internal/utils/testdata/backendtrafficpolicy_ratelimitrule_merge.patch.yaml +++ b/internal/utils/testdata/backendtrafficpolicy_ratelimitrule_merge.patch.yaml @@ -1,12 +1,13 @@ kind: BackendTrafficPolicy metadata: - name: original + name: original-2 spec: rateLimit: type: Global global: rules: - name: two + shared: false clientSelectors: - headers: - name: x-user-id diff --git a/internal/utils/testdata/backendtrafficpolicy_ratelimitrule_merge.strategicmerge.out.yaml b/internal/utils/testdata/backendtrafficpolicy_ratelimitrule_merge.strategicmerge.out.yaml index 1833716e3e..17f6d2cb0e 100644 --- a/internal/utils/testdata/backendtrafficpolicy_ratelimitrule_merge.strategicmerge.out.yaml +++ b/internal/utils/testdata/backendtrafficpolicy_ratelimitrule_merge.strategicmerge.out.yaml @@ -1,7 +1,7 @@ kind: BackendTrafficPolicy metadata: creationTimestamp: null - name: original + name: original-2 spec: rateLimit: global: @@ -23,25 +23,7 @@ spec: limit: requests: 21 unit: Hour - name: two - - clientSelectors: - - headers: - - name: x-user-id - type: Exact - value: one - cost: - request: - from: Number - number: 0 - response: - from: Metadata - metadata: - key: request_cost_set_by_ext_proc - namespace: io.envoyproxy.gateway.e2e - limit: - requests: 21 - unit: Hour - name: one + shared: false type: Global status: ancestors: null diff --git a/internal/xds/translator/ratelimit.go b/internal/xds/translator/ratelimit.go index 03b01298dd..e2d8ff8f77 100644 --- a/internal/xds/translator/ratelimit.go +++ b/internal/xds/translator/ratelimit.go @@ -63,15 +63,6 @@ func (t *Translator) patchHCMWithRateLimit(mgr *hcmv3.HttpConnectionManager, irL } } -// routeContainsGlobalRateLimit checks if a route has global rate limit configuration. -// Returns false if any required component is nil. -func routeContainsGlobalRateLimit(irRoute *ir.HTTPRoute) bool { - return irRoute != nil && - irRoute.Traffic != nil && - irRoute.Traffic.RateLimit != nil && - irRoute.Traffic.RateLimit.Global != nil -} - // isRateLimitPresent returns true if rate limit config exists for the listener. func (t *Translator) isRateLimitPresent(irListener *ir.HTTPListener) bool { // Return false if global ratelimiting is disabled. @@ -80,7 +71,7 @@ func (t *Translator) isRateLimitPresent(irListener *ir.HTTPListener) bool { } // Return true if rate limit config exists. for _, route := range irListener.Routes { - if routeContainsGlobalRateLimit(route) { + if isValidGlobalRateLimit(route) { return true } } @@ -88,42 +79,52 @@ func (t *Translator) isRateLimitPresent(irListener *ir.HTTPListener) bool { } // buildRateLimitFilter constructs a list of HTTP filters for rate limiting based on the provided HTTP listener configuration. -// It creates at most one filter per domain to avoid duplicates. +// It creates separate filters for shared and non-shared rate limits. func (t *Translator) buildRateLimitFilter(irListener *ir.HTTPListener) []*hcmv3.HttpFilter { if irListener == nil || irListener.Routes == nil { return nil } - var filters []*hcmv3.HttpFilter - // Map to track which domains we've already created filters for, prevents creating duplicate filters for the same domain - processedDomains := make(map[string]bool) + filters := []*hcmv3.HttpFilter{} + created := make(map[string]bool) - // Iterate over each route in the listener to create rate limit filters for shared rate limits. for _, route := range irListener.Routes { - if !routeContainsGlobalRateLimit(route) { + if !isValidGlobalRateLimit(route) { continue } - var domain string - filterName := getRateLimitFilterName(route) - if isSharedRateLimit(route) { - // For shared rate limits, use the domain derived from the traffic policy - domain = getDomainName(route) - } else { - // For non-shared rate limits, use the listener domain - domain = irListener.Name + hasShared, hasNonShared := false, false + var sharedRuleName string + + for _, rule := range route.Traffic.RateLimit.Global.Rules { + if isRuleShared(rule) { + hasShared = true + sharedRuleName = stripRuleIndexSuffix(rule.Name) + } else { + hasNonShared = true + } } - // Skip if we've already created a filter for this domain - if processedDomains[domain] { - continue + if hasShared { + sharedDomain := sharedRuleName + if !created[sharedDomain] { + filterName := fmt.Sprintf("%s/%s", egv1a1.EnvoyFilterRateLimit.String(), sharedRuleName) + if filter := createRateLimitFilter(t, irListener, sharedDomain, filterName); filter != nil { + filters = append(filters, filter) + } + created[sharedDomain] = true + } } - processedDomains[domain] = true - // Create a filter for this domain - filter := createRateLimitFilter(t, irListener, domain, filterName) - if filter != nil { - filters = append(filters, filter) + if hasNonShared { + nonSharedDomain := irListener.Name + if !created[nonSharedDomain] { + filterName := egv1a1.EnvoyFilterRateLimit.String() + if filter := createRateLimitFilter(t, irListener, nonSharedDomain, filterName); filter != nil { + filters = append(filters, filter) + } + created[nonSharedDomain] = true + } } } @@ -184,7 +185,7 @@ func createRateLimitFilter(t *Translator, irListener *ir.HTTPListener, domain, f func patchRouteWithRateLimit(route *routev3.Route, irRoute *ir.HTTPRoute) error { //nolint:unparam // Return early if no rate limit config exists. xdsRouteAction := route.GetRoute() - if !routeContainsGlobalRateLimit(irRoute) || xdsRouteAction == nil { + if !isValidGlobalRateLimit(irRoute) || xdsRouteAction == nil { return nil } rateLimits, costSpecified := buildRouteRateLimits(irRoute) @@ -223,42 +224,47 @@ func patchRouteWithRateLimitOnTypedFilterConfig(route *routev3.Route, rateLimits // buildRouteRateLimits constructs rate limit actions for a given route based on the global rate limit configuration. func buildRouteRateLimits(route *ir.HTTPRoute) (rateLimits []*routev3.RateLimit, costSpecified bool) { - descriptorPrefix := route.Name // Ensure route has rate limit config - if !routeContainsGlobalRateLimit(route) { + if !isValidGlobalRateLimit(route) { return nil, false } - // Determine if the rate limit is shared across multiple routes. + // Get the global rate limit configuration global := route.Traffic.RateLimit.Global - isShared := isSharedRateLimit(route) - - // Set the descriptor key based on whether the rate limit is shared. - var descriptorKey, descriptorValue string - if isShared { - // For shared rate limits, use BTP name as key and namespace as value - descriptorKey = route.Traffic.Name - descriptorValue = route.Traffic.Name - } else { - // For non-shared rate limits, include the route name in the descriptor - descriptorKey = getRouteDescriptor(descriptorPrefix) - descriptorValue = descriptorKey - } - - // Create a generic key action for the route descriptor. - routeDescriptor := &routev3.RateLimit_Action{ - ActionSpecifier: &routev3.RateLimit_Action_GenericKey_{ - GenericKey: &routev3.RateLimit_Action_GenericKey{ - DescriptorKey: descriptorKey, - DescriptorValue: descriptorValue, - }, - }, - } // Iterate over each rule in the global rate limit configuration. for rIdx, rule := range global.Rules { - // Initialize a list of rate limit actions for the current rule. - rlActions := []*routev3.RateLimit_Action{routeDescriptor} + // Create a list of rate limit actions for the current rule. + var rlActions []*routev3.RateLimit_Action + + // Create the route descriptor using the rule's shared attribute + var descriptorKey, descriptorValue string + if isRuleShared(rule) { + // For shared rule, use full rule name + descriptorKey = rule.Name + descriptorValue = rule.Name + } else { + // For non-shared rule, use route name in descriptor + descriptorKey = getRouteDescriptor(route.Name) + descriptorValue = descriptorKey + } + + // Create a generic key action for the route descriptor. + routeDescriptor := &routev3.RateLimit_Action{ + ActionSpecifier: &routev3.RateLimit_Action_GenericKey_{ + GenericKey: &routev3.RateLimit_Action_GenericKey{ + DescriptorKey: descriptorKey, + DescriptorValue: descriptorValue, + }, + }, + } + + // Add the generic key action + rlActions = append(rlActions, routeDescriptor) + + // Calculate the domain-specific rule index (0-based for each domain) + ruleIsShared := isRuleShared(rule) + domainRuleIdx := getDomainRuleIndex(global.Rules, rIdx, ruleIsShared) // Process each header match in the rule. for mIdx, match := range rule.HeaderMatches { @@ -266,7 +272,7 @@ func buildRouteRateLimits(route *ir.HTTPRoute) (rateLimits []*routev3.RateLimit, // Handle distinct matches by setting up request header actions. if match.Distinct { - descriptorKey := getRouteRuleDescriptor(rIdx, mIdx) + descriptorKey := getRouteRuleDescriptor(domainRuleIdx, mIdx) action = &routev3.RateLimit_Action{ ActionSpecifier: &routev3.RateLimit_Action_RequestHeaders_{ RequestHeaders: &routev3.RateLimit_Action_RequestHeaders{ @@ -277,8 +283,8 @@ func buildRouteRateLimits(route *ir.HTTPRoute) (rateLimits []*routev3.RateLimit, } } else { // Handle non-distinct matches by setting up header value match actions. - descriptorKey := getRouteRuleDescriptor(rIdx, mIdx) - descriptorVal := getRouteRuleDescriptor(rIdx, mIdx) + descriptorKey := getRouteRuleDescriptor(domainRuleIdx, mIdx) + descriptorVal := getRouteRuleDescriptor(domainRuleIdx, mIdx) headerMatcher := &routev3.HeaderMatcher{ Name: match.Name, HeaderMatchSpecifier: &routev3.HeaderMatcher_StringMatch{ @@ -358,8 +364,8 @@ func buildRouteRateLimits(route *ir.HTTPRoute) (rateLimits []*routev3.RateLimit, action := &routev3.RateLimit_Action{ ActionSpecifier: &routev3.RateLimit_Action_GenericKey_{ GenericKey: &routev3.RateLimit_Action_GenericKey{ - DescriptorKey: getRouteRuleDescriptor(rIdx, -1), - DescriptorValue: getRouteRuleDescriptor(rIdx, -1), + DescriptorKey: getRouteRuleDescriptor(domainRuleIdx, -1), + DescriptorValue: getRouteRuleDescriptor(domainRuleIdx, -1), }, }, } @@ -368,6 +374,7 @@ func buildRouteRateLimits(route *ir.HTTPRoute) (rateLimits []*routev3.RateLimit, // Create a rate limit object for the current rule. rateLimit := &routev3.RateLimit{Actions: rlActions} + if c := rule.RequestCost; c != nil { // Set the hits addend for the request cost if specified. rateLimit.HitsAddend = rateLimitCostToHitsAddend(c) @@ -423,43 +430,38 @@ func GetRateLimitServiceConfigStr(pbCfg *rlsconfv3.RateLimitConfig) (string, err // It returns a list of unique configurations, one for each domain needed across all listeners. // For shared rate limits, it ensures we only process each shared domain once to improve efficiency. func BuildRateLimitServiceConfig(irListeners []*ir.HTTPListener) []*rlsconfv3.RateLimitConfig { - // Map to store descriptors for each domain - domainDescriptors := make(map[string][]*rlsconfv3.RateLimitDescriptor) - // Map to track which domains we've already created filters for, prevents creating duplicate filters for the same domain - processedSharedDomains := make(map[string]bool) + // Map to store rate limit descriptors by domain name + domainDesc := make(map[string][]*rlsconfv3.RateLimitDescriptor) // Process each listener for _, irListener := range irListeners { - // Process each route to build descriptors + // Process each route in the listener for _, route := range irListener.Routes { - if !routeContainsGlobalRateLimit(route) { + // Skip routes without valid global rate limit configuration + if !isValidGlobalRateLimit(route) { continue } - domain := irListener.Name - if isSharedRateLimit(route) { - domain = getDomainName(route) - - // Skip if we've already processed this shared domain - if processedSharedDomains[domain] { - continue - } - processedSharedDomains[domain] = true - } + // Build all descriptors for this route in a single pass to maintain consistent indices + descriptors := buildRateLimitServiceDescriptors(route) - // Get route rule descriptors within each route - serviceDescriptors := buildRateLimitServiceDescriptors(route) - if len(serviceDescriptors) == 0 { + // Skip if no descriptors were created + if len(descriptors) == 0 { continue } - // Add the rate limit descriptor (handles both shared and non-shared) - addRateLimitDescriptor(route, serviceDescriptors, domain, domainDescriptors) + // Process shared rules - add to traffic policy domain + sharedDomain := getDomainSharedName(route) + addRateLimitDescriptor(route, descriptors, sharedDomain, domainDesc, true) + + // Process non-shared rules - add to listener-specific domain + listenerDomain := irListener.Name + addRateLimitDescriptor(route, descriptors, listenerDomain, domainDesc, false) } } - configs := createRateLimitConfigs(domainDescriptors) - return configs + // Convert domain descriptor map to list of rate limit configurations + return createRateLimitConfigs(domainDesc) } // createRateLimitConfigs creates rate limit configs from the domain descriptor map @@ -479,6 +481,25 @@ func createRateLimitConfigs( return configs } +// Helper to recursively compare two RateLimitDescriptors for equality +func descriptorsEqual(a, b *rlsconfv3.RateLimitDescriptor) bool { + if a == nil || b == nil { + return a == b + } + if a.Key != b.Key || a.Value != b.Value { + return false + } + if len(a.Descriptors) != len(b.Descriptors) { + return false + } + for i := range a.Descriptors { + if !descriptorsEqual(a.Descriptors[i], b.Descriptors[i]) { + return false + } + } + return true +} + // addRateLimitDescriptor adds rate limit descriptors to the domain descriptor map. // Handles both shared and route-specific rate limits. // @@ -495,31 +516,107 @@ func addRateLimitDescriptor( serviceDescriptors []*rlsconfv3.RateLimitDescriptor, domain string, domainDescriptors map[string][]*rlsconfv3.RateLimitDescriptor, + includeShared bool, ) { - var key, value string - - if isSharedRateLimit(route) { - // For shared rate limits, use traffic policy name key/value - key = route.Traffic.Name - value = route.Traffic.Name - } else { - // For non-shared rate limits, use route descriptor key/value - key = getRouteDescriptor(route.Name) - value = getRouteDescriptor(route.Name) + if !isValidGlobalRateLimit(route) || len(serviceDescriptors) == 0 { + return } - descriptor := &rlsconfv3.RateLimitDescriptor{ - Key: key, - Value: value, - Descriptors: serviceDescriptors, + for i, rule := range route.Traffic.RateLimit.Global.Rules { + if i >= len(serviceDescriptors) || (includeShared != isRuleShared(rule)) { + continue + } + + var descriptorKey string + if isRuleShared(rule) { + descriptorKey = rule.Name + } else { + descriptorKey = getRouteDescriptor(route.Name) + } + + // Find or create descriptor in domainDescriptors[domain] + var descriptorRule *rlsconfv3.RateLimitDescriptor + found := false + for _, d := range domainDescriptors[domain] { + if d.Key == descriptorKey { + descriptorRule = d + found = true + break + } + } + if !found { + descriptorRule = &rlsconfv3.RateLimitDescriptor{Key: descriptorKey, Value: descriptorKey} + domainDescriptors[domain] = append(domainDescriptors[domain], descriptorRule) + } + + // Ensure no duplicate descriptors + alreadyExists := false + for _, existing := range descriptorRule.Descriptors { + if descriptorsEqual(existing, serviceDescriptors[i]) { + alreadyExists = true + break + } + } + if !alreadyExists { + descriptorRule.Descriptors = append(descriptorRule.Descriptors, serviceDescriptors[i]) + } } - domainDescriptors[domain] = append(domainDescriptors[domain], descriptor) } -// Helper function to check if a route has a shared rate limit +// isSharedRateLimit checks if a route has at least one shared rate limit rule. +// It returns true if any rule in the global rate limit configuration is marked as shared. +// If no rules are shared or there's no global rate limit configuration, it returns false. func isSharedRateLimit(route *ir.HTTPRoute) bool { + if !isValidGlobalRateLimit(route) { + return false + } + global := route.Traffic.RateLimit.Global - return global != nil && global.Shared != nil && *global.Shared && len(global.Rules) > 0 + if len(global.Rules) == 0 { + return false + } + + // Check if any rule has shared=true + for _, rule := range global.Rules { + if isRuleShared(rule) { + return true + } + } + + return false +} + +// Helper function to check if a specific rule is shared +func isRuleShared(rule *ir.RateLimitRule) bool { + return rule != nil && rule.Shared != nil && *rule.Shared +} + +// Helper function to check if a specific rule in a route is shared +func isRuleAtIndexShared(route *ir.HTTPRoute, ruleIndex int) bool { + if route == nil || route.Traffic == nil || route.Traffic.RateLimit == nil || + route.Traffic.RateLimit.Global == nil || len(route.Traffic.RateLimit.Global.Rules) <= ruleIndex || ruleIndex < 0 { + return false + } + + return isRuleShared(route.Traffic.RateLimit.Global.Rules[ruleIndex]) +} + +// Helper function to map a global rule index to a domain-specific rule index +// This ensures that both shared and non-shared rules have indices starting from 0 in their own domains. +func getDomainRuleIndex(rules []*ir.RateLimitRule, globalRuleIdx int, ruleIsShared bool) int { + if globalRuleIdx < 0 || globalRuleIdx >= len(rules) { + return 0 + } + + // Count how many rules of the same "shared" status came before this one + count := 0 + for i := 0; i < globalRuleIdx; i++ { + // If we're looking for shared rules, count shared ones; otherwise count non-shared ones + if (ruleIsShared && isRuleShared(rules[i])) || (!ruleIsShared && !isRuleShared(rules[i])) { + count++ + } + } + return count } // buildRateLimitServiceDescriptors creates the rate limit service pb descriptors based on the global rate limit IR config. @@ -531,7 +628,6 @@ func buildRateLimitServiceDescriptors(route *ir.HTTPRoute) []*rlsconfv3.RateLimi global := route.Traffic.RateLimit.Global pbDescriptors := make([]*rlsconfv3.RateLimitDescriptor, 0, len(global.Rules)) - usedSharedKey := false // Track if the shared key has been used // The order in which matching descriptors are built is consistent with // the order in which ratelimit actions are built: @@ -549,17 +645,21 @@ func buildRateLimitServiceDescriptors(route *ir.HTTPRoute) []*rlsconfv3.RateLimi // We use a chain structure to describe the matching descriptors for one rule. var head, cur *rlsconfv3.RateLimitDescriptor + // Calculate the domain-specific rule index (0-based for each domain) + ruleIsShared := isRuleShared(rule) + domainRuleIdx := getDomainRuleIndex(global.Rules, rIdx, ruleIsShared) + // 1) Header Matches for mIdx, match := range rule.HeaderMatches { pbDesc := new(rlsconfv3.RateLimitDescriptor) // Distinct vs HeaderValueMatch if match.Distinct { // RequestHeader case - pbDesc.Key = getRouteRuleDescriptor(rIdx, mIdx) + pbDesc.Key = getRouteRuleDescriptor(domainRuleIdx, mIdx) } else { // HeaderValueMatch case - pbDesc.Key = getRouteRuleDescriptor(rIdx, mIdx) - pbDesc.Value = getRouteRuleDescriptor(rIdx, mIdx) + pbDesc.Key = getRouteRuleDescriptor(domainRuleIdx, mIdx) + pbDesc.Value = getRouteRuleDescriptor(domainRuleIdx, mIdx) } if mIdx == 0 { @@ -584,14 +684,14 @@ func buildRateLimitServiceDescriptors(route *ir.HTTPRoute) []*rlsconfv3.RateLimi // // An example of rate limit server configuration looks like this: // - // descriptors: - // - key: masked_remote_address //catch all the source IPs inside a CIDR - // value: 192.168.0.0/16 - // descriptors: - // - key: remote_address //set limit for individual IP - // rate_limit: - // unit: second - // requests_per_unit: 100 + // descriptors: + // - key: masked_remote_address //catch all the source IPs inside a CIDR + // value: 192.168.0.0/16 + // descriptors: + // - key: remote_address //set limit for individual IP + // rate_limit: + // unit: second + // requests_per_unit: 100 // // Please refer to [Rate Limit Service Descriptor list definition](https://github.com/envoyproxy/ratelimit#descriptor-list-definition) for details. // 2) CIDR Match @@ -617,22 +717,21 @@ func buildRateLimitServiceDescriptors(route *ir.HTTPRoute) []*rlsconfv3.RateLimi cur = pbDesc } } - // Case when both header and cidr match are not set and the ratelimit // will be applied to all traffic. // 3) No Match (apply to all traffic) if !rule.IsMatchSet() { pbDesc := new(rlsconfv3.RateLimitDescriptor) - // Determine if we should use the shared rate limit key (BTP-based) or a generic route key - if isSharedRateLimit(route) && !usedSharedKey { - // For shared rate limits, use BTP name and namespace - pbDesc.Key = route.Traffic.Name - pbDesc.Value = route.Traffic.Name - usedSharedKey = true + // Determine if we should use the shared rate limit key (rule-based) or a generic route key + if isRuleAtIndexShared(route, rIdx) { + // For shared rate limits, use rule name + pbDesc.Key = rule.Name + pbDesc.Value = rule.Name } else { - // Use generic key for non-shared rate limits - pbDesc.Key = getRouteRuleDescriptor(rIdx, -1) + // Use generic key for non-shared rate limits, with prefix for uniqueness + descriptorKey := getRouteRuleDescriptor(domainRuleIdx, -1) + pbDesc.Key = descriptorKey pbDesc.Value = pbDesc.Key } @@ -715,8 +814,9 @@ func (t *Translator) createRateLimitServiceCluster(tCtx *types.ResourceVersionTa }) } -func getDomainName(route *ir.HTTPRoute) string { - return strings.Replace(route.Traffic.Name, "/", "-", 1) +// getDomainSharedName returns the shared domain (stripped policy name), stripRuleIndexSuffix is used to remove the rule index suffix. +func getDomainSharedName(route *ir.HTTPRoute) string { + return stripRuleIndexSuffix(route.Traffic.RateLimit.Global.Rules[0].Name) } func getRouteRuleDescriptor(ruleIndex, matchIndex int) string { @@ -743,12 +843,37 @@ func (t *Translator) getRateLimitServiceGrpcHostPort() (string, uint32) { return u.Hostname(), uint32(p) } -// For shared rate limits, it appends the traffic policy name to the base filter name. +// getRateLimitFilterName gets the filter name for rate limits. +// If any rule in the route is shared, it appends the rule name to the base filter name. // For non-shared rate limits, it returns just the base filter name. +// Note: This function is primarily used for route-level filter configuration, not for HTTP filters at the listener level. func getRateLimitFilterName(route *ir.HTTPRoute) string { filterName := egv1a1.EnvoyFilterRateLimit.String() + // If any rule is shared, include the rule name in the filter name if isSharedRateLimit(route) { - filterName = fmt.Sprintf("%s/%s", filterName, route.Traffic.Name) + // Find the first shared rule to use its name + for _, rule := range route.Traffic.RateLimit.Global.Rules { + if isRuleShared(rule) { + filterName = fmt.Sprintf("%s/%s", filterName, stripRuleIndexSuffix(rule.Name)) + break + } + } } return filterName } + +// Helper to strip /rule/ from a rule name in order to use shared http filter +func stripRuleIndexSuffix(name string) string { + if i := strings.LastIndex(name, "/rule/"); i != -1 { + return name[:i] + } + return strings.Replace(name, "/", "-", 1) +} + +// Helper to check if a route has a valid global rate limit config +func isValidGlobalRateLimit(route *ir.HTTPRoute) bool { + return route != nil && + route.Traffic != nil && + route.Traffic.RateLimit != nil && + route.Traffic.RateLimit.Global != nil +} diff --git a/internal/xds/translator/testdata/in/ratelimit-config/distinct-match.yaml b/internal/xds/translator/testdata/in/ratelimit-config/distinct-match.yaml index 238ad7a6b1..7a9a6b0d18 100644 --- a/internal/xds/translator/testdata/in/ratelimit-config/distinct-match.yaml +++ b/internal/xds/translator/testdata/in/ratelimit-config/distinct-match.yaml @@ -10,11 +10,11 @@ http: routes: - name: "first-route" traffic: - name: "test-policy-1/test-namespace" rateLimit: global: rules: - - headerMatches: + - name: "test-namespace/test-policy-1/rule/0" + headerMatches: - name: "x-user-id" distinct: true limit: diff --git a/internal/xds/translator/testdata/in/ratelimit-config/distinct-remote-address-match.yaml b/internal/xds/translator/testdata/in/ratelimit-config/distinct-remote-address-match.yaml index 59b50eadb5..1ab9355d9f 100644 --- a/internal/xds/translator/testdata/in/ratelimit-config/distinct-remote-address-match.yaml +++ b/internal/xds/translator/testdata/in/ratelimit-config/distinct-remote-address-match.yaml @@ -10,11 +10,11 @@ http: routes: - name: "first-route" traffic: - name: "test-policy-1/test-namespace" rateLimit: global: rules: - - cidrMatch: + - name: "test-namespace/test-policy-1/rule/0" + cidrMatch: cidr: "192.168.0.0/16" ipv6: false maskLen: 16 diff --git a/internal/xds/translator/testdata/in/ratelimit-config/empty-header-matches.yaml b/internal/xds/translator/testdata/in/ratelimit-config/empty-header-matches.yaml index 55375b28c9..390285f565 100644 --- a/internal/xds/translator/testdata/in/ratelimit-config/empty-header-matches.yaml +++ b/internal/xds/translator/testdata/in/ratelimit-config/empty-header-matches.yaml @@ -10,11 +10,11 @@ http: routes: - name: "first-route" traffic: - name: "test-policy-1/test-namespace" rateLimit: global: rules: - - limit: + - name: "test-namespace/test-policy-1/rule/0" + limit: requests: 5 unit: second pathMatch: diff --git a/internal/xds/translator/testdata/in/ratelimit-config/global-shared-distinct-match.yaml b/internal/xds/translator/testdata/in/ratelimit-config/global-shared-distinct-match.yaml index 84a159a0f7..50929cd847 100644 --- a/internal/xds/translator/testdata/in/ratelimit-config/global-shared-distinct-match.yaml +++ b/internal/xds/translator/testdata/in/ratelimit-config/global-shared-distinct-match.yaml @@ -10,17 +10,17 @@ http: routes: - name: "first-route" traffic: - name: "test-policy-1/test-namespace" rateLimit: global: - shared: true rules: - - headerMatches: + - name: "test-namespace/test-policy-1/rule/0" + headerMatches: - name: "x-user-id" distinct: true limit: requests: 5 unit: second + shared: true pathMatch: exact: "foo/bar" destination: diff --git a/internal/xds/translator/testdata/in/ratelimit-config/global-shared-multiple-shared-policies.yaml b/internal/xds/translator/testdata/in/ratelimit-config/global-shared-multiple-shared-policies.yaml index a586d563df..a3271bb665 100644 --- a/internal/xds/translator/testdata/in/ratelimit-config/global-shared-multiple-shared-policies.yaml +++ b/internal/xds/translator/testdata/in/ratelimit-config/global-shared-multiple-shared-policies.yaml @@ -10,58 +10,58 @@ http: routes: - name: "first-route" traffic: - name: "test-policy-1/test-namespace" rateLimit: global: - shared: true rules: - - headerMatches: + - name: "test-namespace/test-policy-1/rule/0" + headerMatches: - name: "x-user-id" exact: "one" limit: requests: 5 unit: second + shared: true - name: "second-route" traffic: - name: "test-policy-1/test-namespace" rateLimit: global: - shared: true rules: - - headerMatches: + - name: "test-namespace/test-policy-1/rule/0" + headerMatches: - name: "x-user-id" exact: "one" limit: requests: 5 unit: second + shared: true pathMatch: exact: "foo/baz" - name: "third-route" traffic: - name: "test-policy-2/test-namespace" rateLimit: global: - shared: true rules: - - headerMatches: + - name: "test-namespace/test-policy-2/rule/0" + headerMatches: - name: "x-user-id" exact: "two" limit: requests: 10 unit: second + shared: true - name: "fourth-route" traffic: - name: "test-policy-3/test-namespace" rateLimit: global: - shared: false rules: - - headerMatches: + - name: "test-namespace/test-policy-3/rule/0" + headerMatches: - name: "x-user-id" exact: "three" limit: requests: 10 unit: second + shared: false pathMatch: exact: "foo/bar" destination: diff --git a/internal/xds/translator/testdata/in/ratelimit-config/header-and-cidr-matches.yaml b/internal/xds/translator/testdata/in/ratelimit-config/header-and-cidr-matches.yaml index 8849d5b9d9..65df64720d 100644 --- a/internal/xds/translator/testdata/in/ratelimit-config/header-and-cidr-matches.yaml +++ b/internal/xds/translator/testdata/in/ratelimit-config/header-and-cidr-matches.yaml @@ -10,11 +10,11 @@ http: routes: - name: "first-route" traffic: - name: "test-policy-1/test-namespace" rateLimit: global: rules: - - headerMatches: + - name: "test-namespace/test-policy-1/rule/0" + headerMatches: - name: "x-user-id" exact: "one" - name: "x-user-id" diff --git a/internal/xds/translator/testdata/in/ratelimit-config/masked-remote-address-match.yaml b/internal/xds/translator/testdata/in/ratelimit-config/masked-remote-address-match.yaml index 9756be4795..501c3b49d2 100644 --- a/internal/xds/translator/testdata/in/ratelimit-config/masked-remote-address-match.yaml +++ b/internal/xds/translator/testdata/in/ratelimit-config/masked-remote-address-match.yaml @@ -10,11 +10,11 @@ http: routes: - name: "first-route" traffic: - name: "test-policy-1/test-namespace" rateLimit: global: rules: - - cidrMatch: + - name: "test-namespace/test-policy-1/rule/0" + cidrMatch: cidr: "192.168.0.0/16" ipv6: false maskLen: 16 diff --git a/internal/xds/translator/testdata/in/ratelimit-config/multiple-domains.yaml b/internal/xds/translator/testdata/in/ratelimit-config/multiple-domains.yaml index 8321c3cbe0..87f60a62bb 100644 --- a/internal/xds/translator/testdata/in/ratelimit-config/multiple-domains.yaml +++ b/internal/xds/translator/testdata/in/ratelimit-config/multiple-domains.yaml @@ -10,27 +10,27 @@ http: routes: - name: "first-route" traffic: - name: "test-policy-1/test-namespace" rateLimit: global: - shared: true rules: - - headerMatches: + - name: "test-namespace/test-policy-1/rule/0" + headerMatches: - name: "x-user-id" exact: "one" limit: requests: 5 unit: second + shared: true - name: "second-route" traffic: - name: "test-policy-2/test-namespace" rateLimit: global: - shared: false rules: - - headerMatches: + - name: "test-namespace/test-policy-2/rule/0" + headerMatches: - name: "x-user-id" exact: "one" limit: requests: 5 unit: second + shared: false diff --git a/internal/xds/translator/testdata/in/ratelimit-config/multiple-global-shared-distinct-match.yaml b/internal/xds/translator/testdata/in/ratelimit-config/multiple-global-shared-distinct-match.yaml new file mode 100644 index 0000000000..54e33e635a --- /dev/null +++ b/internal/xds/translator/testdata/in/ratelimit-config/multiple-global-shared-distinct-match.yaml @@ -0,0 +1,47 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "first-route" + traffic: + rateLimit: + global: + rules: + - name: "test-namespace/test-policy-1/rule/0" + headerMatches: + - name: "x-user-id" + distinct: true + limit: + requests: 5 + unit: second + shared: true + - name: "test-namespace/test-policy-1/rule/1" + headerMatches: + - name: "x-user-id" + distinct: true + limit: + requests: 5 + unit: second + shared: true + - name: "test-namespace/test-policy-1/rule/2" + headerMatches: + - name: "x-user-id" + distinct: true + limit: + requests: 5 + unit: second + shared: true + pathMatch: + exact: "foo/bar" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/ratelimit-config/multiple-listeners-distinct-match.yaml b/internal/xds/translator/testdata/in/ratelimit-config/multiple-listeners-distinct-match.yaml index 55dc7c79e0..ccc36f04aa 100644 --- a/internal/xds/translator/testdata/in/ratelimit-config/multiple-listeners-distinct-match.yaml +++ b/internal/xds/translator/testdata/in/ratelimit-config/multiple-listeners-distinct-match.yaml @@ -10,11 +10,11 @@ http: routes: - name: "first-route" traffic: - name: "test-policy-1/test-namespace" rateLimit: global: rules: - - headerMatches: + - name: "test-namespace/test-policy-1/rule/0" + headerMatches: - name: "x-user-id" distinct: true limit: @@ -39,11 +39,11 @@ http: routes: - name: "first-route" traffic: - name: "test-policy-2/test-namespace" rateLimit: global: rules: - - headerMatches: + - name: "test-namespace/test-policy-2/rule/0" + headerMatches: - name: "x-user-id" distinct: true limit: diff --git a/internal/xds/translator/testdata/in/ratelimit-config/multiple-listeners-same-shared-distinct-match.yaml b/internal/xds/translator/testdata/in/ratelimit-config/multiple-listeners-same-shared-distinct-match.yaml index c694042ab3..b37bc3220a 100644 --- a/internal/xds/translator/testdata/in/ratelimit-config/multiple-listeners-same-shared-distinct-match.yaml +++ b/internal/xds/translator/testdata/in/ratelimit-config/multiple-listeners-same-shared-distinct-match.yaml @@ -10,17 +10,17 @@ http: routes: - name: "first-route" traffic: - name: "test-policy-1/test-namespace" rateLimit: global: - shared: true rules: - - headerMatches: + - name: "test-namespace/test-policy-1/rule/0" + headerMatches: - name: "x-user-id" distinct: true limit: requests: 5 unit: second + shared: true pathMatch: exact: "foo/bar" destination: @@ -40,17 +40,17 @@ http: routes: - name: "first-route" traffic: - name: "test-policy-1/test-namespace" rateLimit: global: - shared: true rules: - - headerMatches: + - name: "test-namespace/test-policy-1/rule/0" + headerMatches: - name: "x-user-id" distinct: true limit: requests: 5 unit: second + shared: true pathMatch: exact: "foo/bar" destination: diff --git a/internal/xds/translator/testdata/in/ratelimit-config/multiple-listeners-shared-distinct-match.yaml b/internal/xds/translator/testdata/in/ratelimit-config/multiple-listeners-shared-distinct-match.yaml index b6d93f8b2d..9d45930773 100644 --- a/internal/xds/translator/testdata/in/ratelimit-config/multiple-listeners-shared-distinct-match.yaml +++ b/internal/xds/translator/testdata/in/ratelimit-config/multiple-listeners-shared-distinct-match.yaml @@ -10,11 +10,12 @@ http: routes: - name: "first-route" traffic: - name: "test-policy-1/test-namespace" + name: "test-namespace/test-policy-1" rateLimit: global: rules: - - headerMatches: + - name: "test-policy-1/rule/0" + headerMatches: - name: "x-user-id" distinct: true limit: @@ -39,17 +40,17 @@ http: routes: - name: "first-route" traffic: - name: "test-policy-2/test-namespace" rateLimit: global: - shared: true rules: - - headerMatches: + - name: "test-namespace/test-policy-2/rule/0" + headerMatches: - name: "x-user-id" distinct: true limit: requests: 5 unit: second + shared: true pathMatch: exact: "foo/bar" destination: diff --git a/internal/xds/translator/testdata/in/ratelimit-config/multiple-masked-remote-address-match-with-same-cidr.yaml b/internal/xds/translator/testdata/in/ratelimit-config/multiple-masked-remote-address-match-with-same-cidr.yaml index d988353203..fae7230914 100644 --- a/internal/xds/translator/testdata/in/ratelimit-config/multiple-masked-remote-address-match-with-same-cidr.yaml +++ b/internal/xds/translator/testdata/in/ratelimit-config/multiple-masked-remote-address-match-with-same-cidr.yaml @@ -10,18 +10,18 @@ http: routes: - name: "first-route" traffic: - name: "test-policy-1/test-namespace" rateLimit: global: - shared: false rules: - - cidrMatch: + - name: "test-namespace/test-policy-1/rule/0" + cidrMatch: cidr: "192.168.0.10/32" ipv6: false maskLen: 32 limit: requests: 15 unit: Hour + shared: false pathMatch: exact: "foo/bar" destination: @@ -32,18 +32,18 @@ http: port: 50000 - name: "second-route" traffic: - name: "test-policy-2/test-namespace" rateLimit: global: - shared: false rules: - - cidrMatch: + - name: "test-namespace/test-policy-2/rule/0" + cidrMatch: cidr: "192.168.0.10/32" ipv6: false maskLen: 32 limit: requests: 300 unit: Hour + shared: false pathMatch: exact: "foo/bar" destination: diff --git a/internal/xds/translator/testdata/in/ratelimit-config/multiple-matches.yaml b/internal/xds/translator/testdata/in/ratelimit-config/multiple-matches.yaml index 15a90087a6..2da0e13a50 100644 --- a/internal/xds/translator/testdata/in/ratelimit-config/multiple-matches.yaml +++ b/internal/xds/translator/testdata/in/ratelimit-config/multiple-matches.yaml @@ -10,12 +10,11 @@ http: routes: - name: "first-route" traffic: - name: "test-policy-1/test-namespace" rateLimit: global: - shared: false rules: - - headerMatches: + - name: "test-namespace/test-policy-1/rule/0" + headerMatches: - name: "x-user-id" exact: "one" - name: "x-user-id" @@ -23,6 +22,7 @@ http: limit: requests: 5 unit: second + shared: false pathMatch: exact: "foo/bar" destination: diff --git a/internal/xds/translator/testdata/in/ratelimit-config/multiple-routes.yaml b/internal/xds/translator/testdata/in/ratelimit-config/multiple-routes.yaml index 0a6a885c75..909b2388ef 100644 --- a/internal/xds/translator/testdata/in/ratelimit-config/multiple-routes.yaml +++ b/internal/xds/translator/testdata/in/ratelimit-config/multiple-routes.yaml @@ -10,12 +10,12 @@ http: routes: - name: "first-route" traffic: - name: "test-policy-1/test-namespace" rateLimit: global: shared: false rules: - - headerMatches: + - name: "test-namespace/test-policy-1/rule/0" + headerMatches: - name: "x-user-id" exact: "one" limit: @@ -25,12 +25,12 @@ http: exact: "foo/baz" - name: "second-route" traffic: - name: "test-policy-2/test-namespace" rateLimit: global: shared: false rules: - - headerMatches: + - name: "test-namespace/test-policy-2/rule/0" + headerMatches: - name: "x-user-id" exact: "two" limit: diff --git a/internal/xds/translator/testdata/in/ratelimit-config/multiple-rules.yaml b/internal/xds/translator/testdata/in/ratelimit-config/multiple-rules.yaml index 92817339d6..351dcf2fae 100644 --- a/internal/xds/translator/testdata/in/ratelimit-config/multiple-rules.yaml +++ b/internal/xds/translator/testdata/in/ratelimit-config/multiple-rules.yaml @@ -10,23 +10,25 @@ http: routes: - name: "first-route" traffic: - name: "test-policy-1/test-namespace" rateLimit: global: - shared: false rules: - - headerMatches: + - name: "test-namespace/test-policy-1/rule/0" + headerMatches: - name: "x-user-id" exact: "one" limit: requests: 5 unit: second - - headerMatches: + shared: false + - name: "test-namespace/test-policy-1/rule/1" + headerMatches: - name: "x-user-id" exact: "two" limit: requests: 10 unit: second + shared: false pathMatch: exact: "foo/bar" destination: diff --git a/internal/xds/translator/testdata/in/ratelimit-config/multiple-shared-and-unshared.yaml b/internal/xds/translator/testdata/in/ratelimit-config/multiple-shared-and-unshared.yaml new file mode 100644 index 0000000000..e70a5ecd8d --- /dev/null +++ b/internal/xds/translator/testdata/in/ratelimit-config/multiple-shared-and-unshared.yaml @@ -0,0 +1,47 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "first-route" + traffic: + rateLimit: + global: + rules: + - name: "test-namespace/test-policy-1/rule/0" + headerMatches: + - name: "x-user-id" + exact: "one" + limit: + requests: 5 + unit: second + shared: true + - name: "test-namespace/test-policy-1/rule/1" + headerMatches: + - name: "x-user-id" + exact: "two" + limit: + requests: 5 + unit: second + shared: false + - name: "test-namespace/test-policy-1/rule/2" + headerMatches: + - name: "x-user-id" + exact: "three" + limit: + requests: 5 + unit: second + shared: true + - name: "test-namespace/test-policy-1/rule/3" + headerMatches: + - name: "x-user-id" + exact: "four" + limit: + requests: 5 + unit: second + shared: false diff --git a/internal/xds/translator/testdata/in/ratelimit-config/multiple-shared-ratelimit-rules.yaml b/internal/xds/translator/testdata/in/ratelimit-config/multiple-shared-ratelimit-rules.yaml new file mode 100644 index 0000000000..77fc67e7a3 --- /dev/null +++ b/internal/xds/translator/testdata/in/ratelimit-config/multiple-shared-ratelimit-rules.yaml @@ -0,0 +1,47 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "first-route" + traffic: + rateLimit: + global: + rules: + - name: "test-namespace/test-policy-1/rule/0" + headerMatches: + - name: "x-user-id" + exact: "one" + limit: + requests: 1 + unit: second + shared: true + - name: "test-namespace/test-policy-1/rule/1" + headerMatches: + - name: "x-user-id" + exact: "two" + limit: + requests: 2 + unit: second + shared: true + - name: "test-namespace/test-policy-1/rule/2" + headerMatches: + - name: "x-user-id" + exact: "three" + limit: + requests: 3 + unit: second + shared: false + pathMatch: + exact: "foo/bar" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/ratelimit-config/value-match.yaml b/internal/xds/translator/testdata/in/ratelimit-config/value-match.yaml index 2f27cd072f..2e0dcf9664 100644 --- a/internal/xds/translator/testdata/in/ratelimit-config/value-match.yaml +++ b/internal/xds/translator/testdata/in/ratelimit-config/value-match.yaml @@ -10,17 +10,17 @@ http: routes: - name: "first-route" traffic: - name: "test-policy-1/test-namespace" rateLimit: global: - shared: false rules: - - headerMatches: + - name: "test-namespace/test-policy-1/rule/0" + headerMatches: - name: "x-user-id" exact: "one" limit: requests: 5 unit: second + shared: false pathMatch: exact: "foo/bar" destination: diff --git a/internal/xds/translator/testdata/in/xds-ir/ratelimit-global-shared.yaml b/internal/xds/translator/testdata/in/xds-ir/ratelimit-global-shared.yaml index 8d59e27bc8..2b36d593e7 100644 --- a/internal/xds/translator/testdata/in/xds-ir/ratelimit-global-shared.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/ratelimit-global-shared.yaml @@ -13,17 +13,18 @@ http: - name: "first-route" hostname: "*" traffic: - name: "test-policy-1/test-namespace" + name: "test-namespace/test-policy-1" rateLimit: global: - shared: true rules: - - headerMatches: + - name: "test-namespace/test-policy-1/rule/0" + headerMatches: - name: "x-user-id" exact: "one" limit: requests: 5 unit: second + shared: true pathMatch: exact: "foo/bar" destination: @@ -37,17 +38,18 @@ http: - name: "second-route" hostname: "*" traffic: - name: "test-policy-1/test-namespace" + name: "test-namespace/test-policy-1" rateLimit: global: - shared: true rules: - - headerMatches: + - name: "test-namespace/test-policy-1/rule/0" + headerMatches: - name: "x-user-id" exact: "one" limit: requests: 5 unit: second + shared: true pathMatch: exact: "foo/baz" destination: @@ -61,17 +63,18 @@ http: - name: "third-route" hostname: "*" traffic: - name: "test-policy-2/test-namespace" + name: "test-namespace/test-policy-2" rateLimit: global: - shared: true rules: - - headerMatches: + - name: "test-namespace/test-policy-2/rule/0" + headerMatches: - name: "x-user-id" exact: "two" limit: requests: 10 unit: second + shared: true pathMatch: exact: "test" destination: @@ -85,17 +88,18 @@ http: - name: "fourth-route" hostname: "*" traffic: - name: "test-policy-3/test-namespace" + name: "test-namespace/test-policy-3" rateLimit: global: - shared: false rules: - - headerMatches: + - name: "test-namespace/test-policy-3/rule/0" + headerMatches: - name: "x-user-id" exact: "two" limit: requests: 10 unit: second + shared: false pathMatch: exact: "foo/bar" destination: diff --git a/internal/xds/translator/testdata/in/xds-ir/ratelimit-multi-global-shared.yaml b/internal/xds/translator/testdata/in/xds-ir/ratelimit-multi-global-shared.yaml new file mode 100644 index 0000000000..ebd57e1ad4 --- /dev/null +++ b/internal/xds/translator/testdata/in/xds-ir/ratelimit-multi-global-shared.yaml @@ -0,0 +1,94 @@ +metrics: + enablePerEndpointStats: true +http: + - name: "first-listener" + address: "::" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "first-route" + hostname: "*" + traffic: + name: "test-namespace/test-policy-1" + rateLimit: + global: + rules: + - name: "test-namespace/test-policy-1/rule/0" + headerMatches: + - name: "x-user-id" + exact: "one" + limit: + requests: 5 + unit: second + shared: true + pathMatch: + exact: "foo/bar" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + name: "first-route-dest/backend/0" + + - name: "second-route" + hostname: "*" + traffic: + name: "test-namespace/test-policy-1" + rateLimit: + global: + rules: + - name: "test-namespace/test-policy-1/rule/0" + headerMatches: + - name: "x-user-id" + exact: "one" + limit: + requests: 5 + unit: second + shared: true + pathMatch: + exact: "foo/baz" + destination: + name: "second-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + name: "second-route-dest/backend/0" + + - name: "third-route" + hostname: "*" + traffic: + name: "test-namespace/test-policy-2" + rateLimit: + global: + rules: + - name: "test-namespace/test-policy-2/rule/0" + headerMatches: + - name: "x-user-id" + exact: "two" + limit: + requests: 10 + unit: second + shared: true + - name: "test-namespace/test-policy-2/rule/1" + headerMatches: + - name: "x-user-id" + exact: "two" + limit: + requests: 10 + unit: second + shared: true + pathMatch: + exact: "test" + destination: + name: "third-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + name: "third-route-dest/backend/0" diff --git a/internal/xds/translator/testdata/out/ratelimit-config/global-shared-distinct-match.yaml b/internal/xds/translator/testdata/out/ratelimit-config/global-shared-distinct-match.yaml index 62a2ad78f0..936dc97f4e 100644 --- a/internal/xds/translator/testdata/out/ratelimit-config/global-shared-distinct-match.yaml +++ b/internal/xds/translator/testdata/out/ratelimit-config/global-shared-distinct-match.yaml @@ -1,8 +1,8 @@ -name: test-policy-1-test-namespace -domain: test-policy-1-test-namespace +name: test-namespace/test-policy-1 +domain: test-namespace/test-policy-1 descriptors: - - key: test-policy-1/test-namespace - value: test-policy-1/test-namespace + - key: test-namespace/test-policy-1/rule/0 + value: test-namespace/test-policy-1/rule/0 rate_limit: null descriptors: - key: rule-0-match-0 diff --git a/internal/xds/translator/testdata/out/ratelimit-config/global-shared-multiple-shared-policies.yaml b/internal/xds/translator/testdata/out/ratelimit-config/global-shared-multiple-shared-policies.yaml index 83ebd9a9b9..5325d2fc06 100644 --- a/internal/xds/translator/testdata/out/ratelimit-config/global-shared-multiple-shared-policies.yaml +++ b/internal/xds/translator/testdata/out/ratelimit-config/global-shared-multiple-shared-policies.yaml @@ -19,11 +19,11 @@ descriptors: shadow_mode: false detailed_metric: false --- -name: test-policy-1-test-namespace -domain: test-policy-1-test-namespace +name: test-namespace/test-policy-1 +domain: test-namespace/test-policy-1 descriptors: - - key: test-policy-1/test-namespace - value: test-policy-1/test-namespace + - key: test-namespace/test-policy-1/rule/0 + value: test-namespace/test-policy-1/rule/0 rate_limit: null descriptors: - key: rule-0-match-0 @@ -40,11 +40,11 @@ descriptors: shadow_mode: false detailed_metric: false --- -name: test-policy-2-test-namespace -domain: test-policy-2-test-namespace +name: test-namespace/test-policy-2 +domain: test-namespace/test-policy-2 descriptors: - - key: test-policy-2/test-namespace - value: test-policy-2/test-namespace + - key: test-namespace/test-policy-2/rule/0 + value: test-namespace/test-policy-2/rule/0 rate_limit: null descriptors: - key: rule-0-match-0 diff --git a/internal/xds/translator/testdata/out/ratelimit-config/multiple-domains.yaml b/internal/xds/translator/testdata/out/ratelimit-config/multiple-domains.yaml index 0562531e11..af737a0a21 100644 --- a/internal/xds/translator/testdata/out/ratelimit-config/multiple-domains.yaml +++ b/internal/xds/translator/testdata/out/ratelimit-config/multiple-domains.yaml @@ -19,11 +19,11 @@ descriptors: shadow_mode: false detailed_metric: false --- -name: test-policy-1-test-namespace -domain: test-policy-1-test-namespace +name: test-namespace/test-policy-1 +domain: test-namespace/test-policy-1 descriptors: - - key: test-policy-1/test-namespace - value: test-policy-1/test-namespace + - key: test-namespace/test-policy-1/rule/0 + value: test-namespace/test-policy-1/rule/0 rate_limit: null descriptors: - key: rule-0-match-0 diff --git a/internal/xds/translator/testdata/out/ratelimit-config/multiple-global-shared-distinct-match.yaml b/internal/xds/translator/testdata/out/ratelimit-config/multiple-global-shared-distinct-match.yaml new file mode 100644 index 0000000000..82ec4f7081 --- /dev/null +++ b/internal/xds/translator/testdata/out/ratelimit-config/multiple-global-shared-distinct-match.yaml @@ -0,0 +1,54 @@ +name: test-namespace/test-policy-1 +domain: test-namespace/test-policy-1 +descriptors: + - key: test-namespace/test-policy-1/rule/0 + value: test-namespace/test-policy-1/rule/0 + rate_limit: null + descriptors: + - key: rule-0-match-0 + value: "" + rate_limit: + requests_per_unit: 5 + unit: SECOND + unlimited: false + name: "" + replaces: [] + descriptors: [] + shadow_mode: false + detailed_metric: false + shadow_mode: false + detailed_metric: false + - key: test-namespace/test-policy-1/rule/1 + value: test-namespace/test-policy-1/rule/1 + rate_limit: null + descriptors: + - key: rule-1-match-0 + value: "" + rate_limit: + requests_per_unit: 5 + unit: SECOND + unlimited: false + name: "" + replaces: [] + descriptors: [] + shadow_mode: false + detailed_metric: false + shadow_mode: false + detailed_metric: false + - key: test-namespace/test-policy-1/rule/2 + value: test-namespace/test-policy-1/rule/2 + rate_limit: null + descriptors: + - key: rule-2-match-0 + value: "" + rate_limit: + requests_per_unit: 5 + unit: SECOND + unlimited: false + name: "" + replaces: [] + descriptors: [] + shadow_mode: false + detailed_metric: false + shadow_mode: false + detailed_metric: false diff --git a/internal/xds/translator/testdata/out/ratelimit-config/multiple-listeners-same-shared-distinct-match.yaml b/internal/xds/translator/testdata/out/ratelimit-config/multiple-listeners-same-shared-distinct-match.yaml index 62a2ad78f0..936dc97f4e 100644 --- a/internal/xds/translator/testdata/out/ratelimit-config/multiple-listeners-same-shared-distinct-match.yaml +++ b/internal/xds/translator/testdata/out/ratelimit-config/multiple-listeners-same-shared-distinct-match.yaml @@ -1,8 +1,8 @@ -name: test-policy-1-test-namespace -domain: test-policy-1-test-namespace +name: test-namespace/test-policy-1 +domain: test-namespace/test-policy-1 descriptors: - - key: test-policy-1/test-namespace - value: test-policy-1/test-namespace + - key: test-namespace/test-policy-1/rule/0 + value: test-namespace/test-policy-1/rule/0 rate_limit: null descriptors: - key: rule-0-match-0 diff --git a/internal/xds/translator/testdata/out/ratelimit-config/multiple-listeners-shared-distinct-match.yaml b/internal/xds/translator/testdata/out/ratelimit-config/multiple-listeners-shared-distinct-match.yaml index 50b14f478a..e7379e4af6 100644 --- a/internal/xds/translator/testdata/out/ratelimit-config/multiple-listeners-shared-distinct-match.yaml +++ b/internal/xds/translator/testdata/out/ratelimit-config/multiple-listeners-shared-distinct-match.yaml @@ -19,11 +19,11 @@ descriptors: shadow_mode: false detailed_metric: false --- -name: test-policy-2-test-namespace -domain: test-policy-2-test-namespace +name: test-namespace/test-policy-2 +domain: test-namespace/test-policy-2 descriptors: - - key: test-policy-2/test-namespace - value: test-policy-2/test-namespace + - key: test-namespace/test-policy-2/rule/0 + value: test-namespace/test-policy-2/rule/0 rate_limit: null descriptors: - key: rule-0-match-0 diff --git a/internal/xds/translator/testdata/out/ratelimit-config/multiple-shared-and-unshared.yaml b/internal/xds/translator/testdata/out/ratelimit-config/multiple-shared-and-unshared.yaml new file mode 100644 index 0000000000..c02c4f20f8 --- /dev/null +++ b/internal/xds/translator/testdata/out/ratelimit-config/multiple-shared-and-unshared.yaml @@ -0,0 +1,69 @@ +name: first-listener +domain: first-listener +descriptors: + - key: first-route + value: first-route + rate_limit: null + descriptors: + - key: rule-0-match-0 + value: rule-0-match-0 + rate_limit: + requests_per_unit: 5 + unit: SECOND + unlimited: false + name: "" + replaces: [] + descriptors: [] + shadow_mode: false + detailed_metric: false + - key: rule-1-match-0 + value: rule-1-match-0 + rate_limit: + requests_per_unit: 5 + unit: SECOND + unlimited: false + name: "" + replaces: [] + descriptors: [] + shadow_mode: false + detailed_metric: false + shadow_mode: false + detailed_metric: false +--- +name: test-namespace/test-policy-1 +domain: test-namespace/test-policy-1 +descriptors: + - key: test-namespace/test-policy-1/rule/0 + value: test-namespace/test-policy-1/rule/0 + rate_limit: null + descriptors: + - key: rule-0-match-0 + value: rule-0-match-0 + rate_limit: + requests_per_unit: 5 + unit: SECOND + unlimited: false + name: "" + replaces: [] + descriptors: [] + shadow_mode: false + detailed_metric: false + shadow_mode: false + detailed_metric: false + - key: test-namespace/test-policy-1/rule/2 + value: test-namespace/test-policy-1/rule/2 + rate_limit: null + descriptors: + - key: rule-1-match-0 + value: rule-1-match-0 + rate_limit: + requests_per_unit: 5 + unit: SECOND + unlimited: false + name: "" + replaces: [] + descriptors: [] + shadow_mode: false + detailed_metric: false + shadow_mode: false + detailed_metric: false diff --git a/internal/xds/translator/testdata/out/ratelimit-config/multiple-shared-ratelimit-rules.yaml b/internal/xds/translator/testdata/out/ratelimit-config/multiple-shared-ratelimit-rules.yaml new file mode 100644 index 0000000000..ecb7e8ca89 --- /dev/null +++ b/internal/xds/translator/testdata/out/ratelimit-config/multiple-shared-ratelimit-rules.yaml @@ -0,0 +1,58 @@ +name: first-listener +domain: first-listener +descriptors: + - key: first-route + value: first-route + rate_limit: null + descriptors: + - key: rule-0-match-0 + value: rule-0-match-0 + rate_limit: + requests_per_unit: 3 + unit: SECOND + unlimited: false + name: "" + replaces: [] + descriptors: [] + shadow_mode: false + detailed_metric: false + shadow_mode: false + detailed_metric: false +--- +name: test-namespace/test-policy-1 +domain: test-namespace/test-policy-1 +descriptors: + - key: test-namespace/test-policy-1/rule/0 + value: test-namespace/test-policy-1/rule/0 + rate_limit: null + descriptors: + - key: rule-0-match-0 + value: rule-0-match-0 + rate_limit: + requests_per_unit: 1 + unit: SECOND + unlimited: false + name: "" + replaces: [] + descriptors: [] + shadow_mode: false + detailed_metric: false + shadow_mode: false + detailed_metric: false + - key: test-namespace/test-policy-1/rule/1 + value: test-namespace/test-policy-1/rule/1 + rate_limit: null + descriptors: + - key: rule-1-match-0 + value: rule-1-match-0 + rate_limit: + requests_per_unit: 2 + unit: SECOND + unlimited: false + name: "" + replaces: [] + descriptors: [] + shadow_mode: false + detailed_metric: false + shadow_mode: false + detailed_metric: false diff --git a/internal/xds/translator/testdata/out/xds-ir/ratelimit-global-shared.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/ratelimit-global-shared.listeners.yaml index 36c9cd5439..fd03d88dca 100644 --- a/internal/xds/translator/testdata/out/xds-ir/ratelimit-global-shared.listeners.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/ratelimit-global-shared.listeners.yaml @@ -14,20 +14,20 @@ initialStreamWindowSize: 65536 maxConcurrentStreams: 100 httpFilters: - - name: envoy.filters.http.ratelimit/test-policy-1/test-namespace + - name: envoy.filters.http.ratelimit/test-namespace/test-policy-1 typedConfig: '@type': type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit - domain: test-policy-1-test-namespace + domain: test-namespace/test-policy-1 enableXRatelimitHeaders: DRAFT_VERSION_03 rateLimitService: grpcService: envoyGrpc: clusterName: ratelimit_cluster transportApiVersion: V3 - - name: envoy.filters.http.ratelimit/test-policy-2/test-namespace + - name: envoy.filters.http.ratelimit/test-namespace/test-policy-2 typedConfig: '@type': type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit - domain: test-policy-2-test-namespace + domain: test-namespace/test-policy-2 enableXRatelimitHeaders: DRAFT_VERSION_03 rateLimitService: grpcService: diff --git a/internal/xds/translator/testdata/out/xds-ir/ratelimit-global-shared.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/ratelimit-global-shared.routes.yaml index 6eeb9bee6e..935fb21d8c 100644 --- a/internal/xds/translator/testdata/out/xds-ir/ratelimit-global-shared.routes.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/ratelimit-global-shared.routes.yaml @@ -13,8 +13,8 @@ rateLimits: - actions: - genericKey: - descriptorKey: test-policy-1/test-namespace - descriptorValue: test-policy-1/test-namespace + descriptorKey: test-namespace/test-policy-1/rule/0 + descriptorValue: test-namespace/test-policy-1/rule/0 - headerValueMatch: descriptorKey: rule-0-match-0 descriptorValue: rule-0-match-0 @@ -33,8 +33,8 @@ rateLimits: - actions: - genericKey: - descriptorKey: test-policy-1/test-namespace - descriptorValue: test-policy-1/test-namespace + descriptorKey: test-namespace/test-policy-1/rule/0 + descriptorValue: test-namespace/test-policy-1/rule/0 - headerValueMatch: descriptorKey: rule-0-match-0 descriptorValue: rule-0-match-0 @@ -53,8 +53,8 @@ rateLimits: - actions: - genericKey: - descriptorKey: test-policy-2/test-namespace - descriptorValue: test-policy-2/test-namespace + descriptorKey: test-namespace/test-policy-2/rule/0 + descriptorValue: test-namespace/test-policy-2/rule/0 - headerValueMatch: descriptorKey: rule-0-match-0 descriptorValue: rule-0-match-0 diff --git a/internal/xds/translator/testdata/out/xds-ir/ratelimit-multi-global-shared.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/ratelimit-multi-global-shared.clusters.yaml new file mode 100644 index 0000000000..87afaadd1c --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/ratelimit-multi-global-shared.clusters.yaml @@ -0,0 +1,105 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_PREFERRED + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + ignoreHealthOnHostRemoval: true + lbPolicy: LEAST_REQUEST + name: first-route-dest + perConnectionBufferLimitBytes: 32768 + trackClusterStats: + perEndpointStats: true + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_PREFERRED + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: second-route-dest + ignoreHealthOnHostRemoval: true + lbPolicy: LEAST_REQUEST + name: second-route-dest + perConnectionBufferLimitBytes: 32768 + trackClusterStats: + perEndpointStats: true + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_PREFERRED + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: third-route-dest + ignoreHealthOnHostRemoval: true + lbPolicy: LEAST_REQUEST + name: third-route-dest + perConnectionBufferLimitBytes: 32768 + trackClusterStats: + perEndpointStats: true + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_PREFERRED + dnsRefreshRate: 30s + lbPolicy: LEAST_REQUEST + loadAssignment: + clusterName: ratelimit_cluster + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: envoy-ratelimit.envoy-gateway-system.svc.cluster.local + portValue: 8081 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: ratelimit_cluster/backend/-1 + name: ratelimit_cluster + perConnectionBufferLimitBytes: 32768 + respectDnsTtl: true + trackClusterStats: + perEndpointStats: true + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + commonTlsContext: + tlsCertificates: + - certificateChain: + filename: /certs/tls.crt + privateKey: + filename: /certs/tls.key + validationContext: + trustedCa: + filename: /certs/ca.crt + type: STRICT_DNS + typedExtensionProtocolOptions: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicitHttpConfig: + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 diff --git a/internal/xds/translator/testdata/out/xds-ir/ratelimit-multi-global-shared.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/ratelimit-multi-global-shared.endpoints.yaml new file mode 100644 index 0000000000..475b89a087 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/ratelimit-multi-global-shared.endpoints.yaml @@ -0,0 +1,36 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 +- clusterName: second-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: second-route-dest/backend/0 +- clusterName: third-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: third-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/ratelimit-multi-global-shared.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/ratelimit-multi-global-shared.listeners.yaml new file mode 100644 index 0000000000..0e76130891 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/ratelimit-multi-global-shared.listeners.yaml @@ -0,0 +1,54 @@ +- address: + socketAddress: + address: '::' + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.ratelimit/test-namespace/test-policy-1 + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit + domain: test-namespace/test-policy-1 + enableXRatelimitHeaders: DRAFT_VERSION_03 + rateLimitService: + grpcService: + envoyGrpc: + clusterName: ratelimit_cluster + transportApiVersion: V3 + - name: envoy.filters.http.ratelimit/test-namespace/test-policy-2 + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit + domain: test-namespace/test-policy-2 + enableXRatelimitHeaders: DRAFT_VERSION_03 + rateLimitService: + grpcService: + envoyGrpc: + clusterName: ratelimit_cluster + transportApiVersion: V3 + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http-10080 + useRemoteAddress: true + name: first-listener + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/ratelimit-multi-global-shared.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/ratelimit-multi-global-shared.routes.yaml new file mode 100644 index 0000000000..9ebf1dd631 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/ratelimit-multi-global-shared.routes.yaml @@ -0,0 +1,79 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + path: foo/bar + name: first-route + route: + cluster: first-route-dest + rateLimits: + - actions: + - genericKey: + descriptorKey: test-namespace/test-policy-1/rule/0 + descriptorValue: test-namespace/test-policy-1/rule/0 + - headerValueMatch: + descriptorKey: rule-0-match-0 + descriptorValue: rule-0-match-0 + expectMatch: true + headers: + - name: x-user-id + stringMatch: + exact: one + upgradeConfigs: + - upgradeType: websocket + - match: + path: foo/baz + name: second-route + route: + cluster: second-route-dest + rateLimits: + - actions: + - genericKey: + descriptorKey: test-namespace/test-policy-1/rule/0 + descriptorValue: test-namespace/test-policy-1/rule/0 + - headerValueMatch: + descriptorKey: rule-0-match-0 + descriptorValue: rule-0-match-0 + expectMatch: true + headers: + - name: x-user-id + stringMatch: + exact: one + upgradeConfigs: + - upgradeType: websocket + - match: + path: test + name: third-route + route: + cluster: third-route-dest + rateLimits: + - actions: + - genericKey: + descriptorKey: test-namespace/test-policy-2/rule/0 + descriptorValue: test-namespace/test-policy-2/rule/0 + - headerValueMatch: + descriptorKey: rule-0-match-0 + descriptorValue: rule-0-match-0 + expectMatch: true + headers: + - name: x-user-id + stringMatch: + exact: two + - actions: + - genericKey: + descriptorKey: test-namespace/test-policy-2/rule/1 + descriptorValue: test-namespace/test-policy-2/rule/1 + - headerValueMatch: + descriptorKey: rule-1-match-0 + descriptorValue: rule-1-match-0 + expectMatch: true + headers: + - name: x-user-id + stringMatch: + exact: two + upgradeConfigs: + - upgradeType: websocket diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md index 580c34083c..8e7da32400 100644 --- a/site/content/en/latest/api/extension_types.md +++ b/site/content/en/latest/api/extension_types.md @@ -3870,10 +3870,10 @@ _Appears in:_ | Field | Type | Required | Default | Description | | --- | --- | --- | --- | --- | -| `name` | _string_ | false | | Name is the name of the rule. This is used to identify the rule
in the Envoy configuration and as a unique identifier for merging. | | `clientSelectors` | _[RateLimitSelectCondition](#ratelimitselectcondition) array_ | false | | ClientSelectors holds the list of select conditions to select
specific clients using attributes from the traffic flow.
All individual select conditions must hold True for this rule
and its limit to be applied.
If no client selectors are specified, the rule applies to all traffic of
the targeted Route.
If the policy targets a Gateway, the rule applies to each Route of the Gateway.
Please note that each Route has its own rate limit counters. For example,
if a Gateway has two Routes, and the policy has a rule with limit 10rps,
each Route will have its own 10rps limit. | | `limit` | _[RateLimitValue](#ratelimitvalue)_ | true | | Limit holds the rate limit values.
This limit is applied for traffic flows when the selectors
compute to True, causing the request to be counted towards the limit.
The limit is enforced and the request is ratelimited, i.e. a response with
429 HTTP status code is sent back to the client when
the selected requests have reached the limit. | | `cost` | _[RateLimitCost](#ratelimitcost)_ | false | | Cost specifies the cost of requests and responses for the rule.
This is optional and if not specified, the default behavior is to reduce the rate limit counters by 1 on
the request path and do not reduce the rate limit counters on the response path. | +| `shared` | _boolean_ | false | | Shared determines whether this rate limit rule applies across all the policy targets.
If set to true, the rule is treated as a common bucket and is shared across all policy targets (xRoutes).
Default: false. | #### RateLimitSelectCondition diff --git a/test/e2e/testdata/ratelimit-global-shared-and-unshared-header-match.yaml b/test/e2e/testdata/ratelimit-global-shared-and-unshared-header-match.yaml new file mode 100644 index 0000000000..639bbd7257 --- /dev/null +++ b/test/e2e/testdata/ratelimit-global-shared-and-unshared-header-match.yaml @@ -0,0 +1,111 @@ +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: eg-rate-limit + namespace: gateway-conformance-infra +spec: + gatewayClassName: "{GATEWAY_CLASS_NAME}" + listeners: + - name: http-8080 + protocol: HTTP + port: 8080 +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: header-ratelimit-1 + namespace: gateway-conformance-infra +spec: + parentRefs: + - name: eg-rate-limit + rules: + - matches: + - path: + type: PathPrefix + value: /foo + backendRefs: + - name: infra-backend-v1 + port: 8080 +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: header-ratelimit-2 + namespace: gateway-conformance-infra +spec: + parentRefs: + - name: eg-rate-limit + rules: + - matches: + - path: + type: PathPrefix + value: /bar + backendRefs: + - name: infra-backend-v2 + port: 8080 +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: BackendTrafficPolicy +metadata: + name: ratelimit-headers-route-policy + namespace: gateway-conformance-infra +spec: + targetRefs: + - group: gateway.networking.k8s.io + kind: HTTPRoute + name: header-ratelimit-1 + - group: gateway.networking.k8s.io + kind: HTTPRoute + name: header-ratelimit-2 + mergeType: StrategicMerge + rateLimit: + type: Global + global: + rules: + - clientSelectors: + - headers: + - name: x-user-id + value: one + limit: + requests: 3 + unit: Hour + shared: true + - clientSelectors: + - headers: + - name: x-user-id + value: two + limit: + requests: 3 + unit: Hour + shared: false +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: BackendTrafficPolicy +metadata: + name: ratelimit-headers-gateway-policy + namespace: gateway-conformance-infra +spec: + targetRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: eg-rate-limit + rateLimit: + type: Global + global: + rules: + - clientSelectors: + - headers: + - name: x-user-id + value: three + limit: + requests: 3 + unit: Hour + shared: true + - clientSelectors: + - headers: + - name: x-user-id + value: four + limit: + requests: 3 + unit: Hour + shared: false diff --git a/test/e2e/testdata/ratelimit-global-shared-cidr-match.yaml b/test/e2e/testdata/ratelimit-global-shared-cidr-match.yaml index 68f620df8a..17871a2524 100644 --- a/test/e2e/testdata/ratelimit-global-shared-cidr-match.yaml +++ b/test/e2e/testdata/ratelimit-global-shared-cidr-match.yaml @@ -14,7 +14,6 @@ spec: rateLimit: type: Global global: - shared: true rules: - clientSelectors: - sourceCIDR: @@ -23,6 +22,7 @@ spec: limit: requests: 3 unit: Hour + shared: true --- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute diff --git a/test/e2e/testdata/ratelimit-global-shared-gateway-header-match.yaml b/test/e2e/testdata/ratelimit-global-shared-gateway-header-match.yaml index 04ff67b4b5..95fd22ad79 100644 --- a/test/e2e/testdata/ratelimit-global-shared-gateway-header-match.yaml +++ b/test/e2e/testdata/ratelimit-global-shared-gateway-header-match.yaml @@ -57,7 +57,6 @@ spec: rateLimit: type: Global global: - shared: true rules: - clientSelectors: - headers: @@ -67,3 +66,4 @@ spec: limit: requests: 3 unit: Hour + shared: true diff --git a/test/e2e/tests/ratelimit.go b/test/e2e/tests/ratelimit.go index da8cff9fdc..b3522af687 100644 --- a/test/e2e/tests/ratelimit.go +++ b/test/e2e/tests/ratelimit.go @@ -41,6 +41,7 @@ func init() { ConformanceTests = append(ConformanceTests, UsageRateLimitTest) ConformanceTests = append(ConformanceTests, RateLimitGlobalSharedCidrMatchTest) ConformanceTests = append(ConformanceTests, RateLimitGlobalSharedGatewayHeaderMatchTest) + ConformanceTests = append(ConformanceTests, RateLimitGlobalMergeTest) } var RateLimitCIDRMatchTest = suite.ConformanceTest{ @@ -946,6 +947,89 @@ var RateLimitGlobalSharedGatewayHeaderMatchTest = suite.ConformanceTest{ }, } +var RateLimitGlobalMergeTest = suite.ConformanceTest{ + ShortName: "RateLimitGlobalMergeTest", + Description: "Limit requests with matching headers across multiple routes, verifying both shared and unshared rate limit behaviors", + Manifests: []string{"testdata/ratelimit-global-shared-and-unshared-header-match.yaml"}, + Test: func(t *testing.T, suite *suite.ConformanceTestSuite) { + ns := "gateway-conformance-infra" + route1NN := types.NamespacedName{Name: "header-ratelimit-1", Namespace: ns} + route2NN := types.NamespacedName{Name: "header-ratelimit-2", Namespace: ns} + gwNN := types.NamespacedName{Name: "eg-rate-limit", Namespace: ns} + + gwAddr1 := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), route1NN) + gwAddr2 := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), route2NN) + + t.Run("shared_route_policy_x-user-id=one", func(t *testing.T) { + headers := map[string]string{"x-user-id": "one"} + + expectOk1 := http.ExpectedResponse{Request: http.Request{Path: "/bar", Headers: headers}, Response: http.Response{StatusCode: 200}, Namespace: ns} + expectOk2 := http.ExpectedResponse{Request: http.Request{Path: "/foo", Headers: headers}, Response: http.Response{StatusCode: 200}, Namespace: ns} + expectOk3 := http.ExpectedResponse{Request: http.Request{Path: "/bar", Headers: headers}, Response: http.Response{StatusCode: 200}, Namespace: ns} + expectLimit := http.ExpectedResponse{Request: http.Request{Path: "/foo", Headers: headers}, Response: http.Response{StatusCode: 429}, Namespace: ns} + + http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr2, expectOk1) + + _ = GotExactExpectedResponse(t, 1, suite.RoundTripper, http.MakeRequest(t, &expectOk1, gwAddr2, "HTTP", "http"), expectOk1) + _ = GotExactExpectedResponse(t, 1, suite.RoundTripper, http.MakeRequest(t, &expectOk2, gwAddr1, "HTTP", "http"), expectOk2) + _ = GotExactExpectedResponse(t, 1, suite.RoundTripper, http.MakeRequest(t, &expectOk3, gwAddr2, "HTTP", "http"), expectOk3) + _ = GotExactExpectedResponse(t, 1, suite.RoundTripper, http.MakeRequest(t, &expectLimit, gwAddr1, "HTTP", "http"), expectLimit) + }) + + t.Run("unshared_route_policy_x-user-id=two", func(t *testing.T) { + headers := map[string]string{"x-user-id": "two"} + + // Route 1 (/foo) + ok1 := http.ExpectedResponse{Request: http.Request{Path: "/foo", Headers: headers}, Response: http.Response{StatusCode: 200}, Namespace: ns} + limit1 := http.ExpectedResponse{Request: http.Request{Path: "/foo", Headers: headers}, Response: http.Response{StatusCode: 429}, Namespace: ns} + // Route 2 (/bar) + ok2 := http.ExpectedResponse{Request: http.Request{Path: "/bar", Headers: headers}, Response: http.Response{StatusCode: 200}, Namespace: ns} + limit2 := http.ExpectedResponse{Request: http.Request{Path: "/bar", Headers: headers}, Response: http.Response{StatusCode: 429}, Namespace: ns} + + http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr1, ok1) + + _ = GotExactExpectedResponse(t, 3, suite.RoundTripper, http.MakeRequest(t, &ok1, gwAddr1, "HTTP", "http"), ok1) + _ = GotExactExpectedResponse(t, 1, suite.RoundTripper, http.MakeRequest(t, &limit1, gwAddr1, "HTTP", "http"), limit1) + + _ = GotExactExpectedResponse(t, 3, suite.RoundTripper, http.MakeRequest(t, &ok2, gwAddr2, "HTTP", "http"), ok2) + _ = GotExactExpectedResponse(t, 1, suite.RoundTripper, http.MakeRequest(t, &limit2, gwAddr2, "HTTP", "http"), limit2) + }) + + t.Run("shared_gateway_policy_x-user-id=three", func(t *testing.T) { + headers := map[string]string{"x-user-id": "three"} + + ok1 := http.ExpectedResponse{Request: http.Request{Path: "/bar", Headers: headers}, Response: http.Response{StatusCode: 200}, Namespace: ns} + ok2 := http.ExpectedResponse{Request: http.Request{Path: "/foo", Headers: headers}, Response: http.Response{StatusCode: 200}, Namespace: ns} + ok3 := http.ExpectedResponse{Request: http.Request{Path: "/bar", Headers: headers}, Response: http.Response{StatusCode: 200}, Namespace: ns} + limit := http.ExpectedResponse{Request: http.Request{Path: "/foo", Headers: headers}, Response: http.Response{StatusCode: 429}, Namespace: ns} + + http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr2, ok1) + + _ = GotExactExpectedResponse(t, 1, suite.RoundTripper, http.MakeRequest(t, &ok1, gwAddr2, "HTTP", "http"), ok1) + _ = GotExactExpectedResponse(t, 1, suite.RoundTripper, http.MakeRequest(t, &ok2, gwAddr1, "HTTP", "http"), ok2) + _ = GotExactExpectedResponse(t, 1, suite.RoundTripper, http.MakeRequest(t, &ok3, gwAddr2, "HTTP", "http"), ok3) + _ = GotExactExpectedResponse(t, 1, suite.RoundTripper, http.MakeRequest(t, &limit, gwAddr1, "HTTP", "http"), limit) + }) + + t.Run("unshared_gateway_policy__x-user-id=four", func(t *testing.T) { + headers := map[string]string{"x-user-id": "four"} + + ok1 := http.ExpectedResponse{Request: http.Request{Path: "/foo", Headers: headers}, Response: http.Response{StatusCode: 200}, Namespace: ns} + limit1 := http.ExpectedResponse{Request: http.Request{Path: "/foo", Headers: headers}, Response: http.Response{StatusCode: 429}, Namespace: ns} + ok2 := http.ExpectedResponse{Request: http.Request{Path: "/bar", Headers: headers}, Response: http.Response{StatusCode: 200}, Namespace: ns} + limit2 := http.ExpectedResponse{Request: http.Request{Path: "/bar", Headers: headers}, Response: http.Response{StatusCode: 429}, Namespace: ns} + + http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr1, ok1) + + _ = GotExactExpectedResponse(t, 3, suite.RoundTripper, http.MakeRequest(t, &ok1, gwAddr1, "HTTP", "http"), ok1) + _ = GotExactExpectedResponse(t, 1, suite.RoundTripper, http.MakeRequest(t, &limit1, gwAddr1, "HTTP", "http"), limit1) + + _ = GotExactExpectedResponse(t, 3, suite.RoundTripper, http.MakeRequest(t, &ok2, gwAddr2, "HTTP", "http"), ok2) + _ = GotExactExpectedResponse(t, 1, suite.RoundTripper, http.MakeRequest(t, &limit2, gwAddr2, "HTTP", "http"), limit2) + }) + }, +} + func GotExactExpectedResponse(t *testing.T, n int, r roundtripper.RoundTripper, req roundtripper.Request, resp http.ExpectedResponse) error { for i := 0; i < n; i++ { cReq, cRes, err := r.CaptureRoundTrip(req) diff --git a/test/helm/gateway-crds-helm/all.out.yaml b/test/helm/gateway-crds-helm/all.out.yaml index be47c7f6c9..2612431a81 100644 --- a/test/helm/gateway-crds-helm/all.out.yaml +++ b/test/helm/gateway-crds-helm/all.out.yaml @@ -18570,23 +18570,17 @@ spec: - requests - unit type: object - name: + shared: description: |- - Name is the name of the rule. This is used to identify the rule - in the Envoy configuration and as a unique identifier for merging. - type: string + Shared determines whether this rate limit rule applies across all the policy targets. + If set to true, the rule is treated as a common bucket and is shared across all policy targets (xRoutes). + Default: false. + type: boolean required: - limit type: object maxItems: 64 type: array - shared: - default: false - description: |- - Shared determines whether the rate limit rules apply across all the policy targets. - If set to true, the rule is treated as a common bucket and is shared across all policy targets (xRoutes). - Default: false. - type: boolean required: - rules type: object @@ -18825,11 +18819,12 @@ spec: - requests - unit type: object - name: + shared: description: |- - Name is the name of the rule. This is used to identify the rule - in the Envoy configuration and as a unique identifier for merging. - type: string + Shared determines whether this rate limit rule applies across all the policy targets. + If set to true, the rule is treated as a common bucket and is shared across all policy targets (xRoutes). + Default: false. + type: boolean required: - limit type: object 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 a0e7257100..9dc657ce74 100644 --- a/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml +++ b/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml @@ -1258,23 +1258,17 @@ spec: - requests - unit type: object - name: + shared: description: |- - Name is the name of the rule. This is used to identify the rule - in the Envoy configuration and as a unique identifier for merging. - type: string + Shared determines whether this rate limit rule applies across all the policy targets. + If set to true, the rule is treated as a common bucket and is shared across all policy targets (xRoutes). + Default: false. + type: boolean required: - limit type: object maxItems: 64 type: array - shared: - default: false - description: |- - Shared determines whether the rate limit rules apply across all the policy targets. - If set to true, the rule is treated as a common bucket and is shared across all policy targets (xRoutes). - Default: false. - type: boolean required: - rules type: object @@ -1513,11 +1507,12 @@ spec: - requests - unit type: object - name: + shared: description: |- - Name is the name of the rule. This is used to identify the rule - in the Envoy configuration and as a unique identifier for merging. - type: string + Shared determines whether this rate limit rule applies across all the policy targets. + If set to true, the rule is treated as a common bucket and is shared across all policy targets (xRoutes). + Default: false. + type: boolean required: - limit type: object