From 25e862e70c043f1452a79994891fb0afae3db5ca Mon Sep 17 00:00:00 2001 From: zirain Date: Sun, 4 May 2025 11:03:14 +0800 Subject: [PATCH 1/2] feat: allow merge rate limit rule in BTP Signed-off-by: zirain --- api/v1alpha1/ratelimit_types.go | 14 +++++- ....envoyproxy.io_backendtrafficpolicies.yaml | 10 ++++ ....envoyproxy.io_backendtrafficpolicies.yaml | 10 ++++ ...dtrafficpolicy_ratelimitrule_merge.in.yaml | 27 +++++++++++ ...icy_ratelimitrule_merge.jsonmerge.out.yaml | 29 ++++++++++++ ...afficpolicy_ratelimitrule_merge.patch.yaml | 27 +++++++++++ ...atelimitrule_merge.strategicmerge.out.yaml | 47 +++++++++++++++++++ site/content/en/latest/api/extension_types.md | 1 + test/helm/gateway-crds-helm/all.out.yaml | 6 +++ .../envoy-gateway-crds.out.yaml | 6 +++ 10 files changed, 175 insertions(+), 2 deletions(-) create mode 100644 internal/utils/testdata/backendtrafficpolicy_ratelimitrule_merge.in.yaml create mode 100644 internal/utils/testdata/backendtrafficpolicy_ratelimitrule_merge.jsonmerge.out.yaml create mode 100644 internal/utils/testdata/backendtrafficpolicy_ratelimitrule_merge.patch.yaml create mode 100644 internal/utils/testdata/backendtrafficpolicy_ratelimitrule_merge.strategicmerge.out.yaml diff --git a/api/v1alpha1/ratelimit_types.go b/api/v1alpha1/ratelimit_types.go index 7ecca9e904..21727bb057 100644 --- a/api/v1alpha1/ratelimit_types.go +++ b/api/v1alpha1/ratelimit_types.go @@ -49,8 +49,10 @@ 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"` + 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). @@ -69,15 +71,23 @@ 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"` + Rules []RateLimitRule `json:"rules" patchMergeKey:"name" patchStrategy:"merge"` } // 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 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 b58c751757..66c01520e6 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,6 +948,11 @@ spec: - requests - unit type: object + name: + 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 required: - limit type: object @@ -1198,6 +1203,11 @@ spec: - requests - unit type: object + name: + 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 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 fb186d33d6..af436290d9 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml @@ -947,6 +947,11 @@ spec: - requests - unit type: object + name: + 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 required: - limit type: object @@ -1197,6 +1202,11 @@ spec: - requests - unit type: object + name: + 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 required: - limit type: object diff --git a/internal/utils/testdata/backendtrafficpolicy_ratelimitrule_merge.in.yaml b/internal/utils/testdata/backendtrafficpolicy_ratelimitrule_merge.in.yaml new file mode 100644 index 0000000000..3dc8eee659 --- /dev/null +++ b/internal/utils/testdata/backendtrafficpolicy_ratelimitrule_merge.in.yaml @@ -0,0 +1,27 @@ +kind: BackendTrafficPolicy +metadata: + name: original +spec: + rateLimit: + type: Global + global: + rules: + - name: one + clientSelectors: + - headers: + - name: x-user-id + type: Exact + value: one + limit: + # 21 instead of 20 to test the zero request cost. + requests: 21 + unit: Hour + cost: + request: + from: Number + number: 0 + response: + from: Metadata + metadata: + namespace: io.envoyproxy.gateway.e2e + key: request_cost_set_by_ext_proc diff --git a/internal/utils/testdata/backendtrafficpolicy_ratelimitrule_merge.jsonmerge.out.yaml b/internal/utils/testdata/backendtrafficpolicy_ratelimitrule_merge.jsonmerge.out.yaml new file mode 100644 index 0000000000..a679b04d1b --- /dev/null +++ b/internal/utils/testdata/backendtrafficpolicy_ratelimitrule_merge.jsonmerge.out.yaml @@ -0,0 +1,29 @@ +kind: BackendTrafficPolicy +metadata: + creationTimestamp: null + name: original +spec: + rateLimit: + global: + rules: + - clientSelectors: + - headers: + - name: x-user-id + type: Exact + value: two + 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: two + 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 new file mode 100644 index 0000000000..2d13bd03b0 --- /dev/null +++ b/internal/utils/testdata/backendtrafficpolicy_ratelimitrule_merge.patch.yaml @@ -0,0 +1,27 @@ +kind: BackendTrafficPolicy +metadata: + name: original +spec: + rateLimit: + type: Global + global: + rules: + - name: two + clientSelectors: + - headers: + - name: x-user-id + type: Exact + value: two + limit: + # 21 instead of 20 to test the zero request cost. + requests: 21 + unit: Hour + cost: + request: + from: Number + number: 0 + response: + from: Metadata + metadata: + namespace: io.envoyproxy.gateway.e2e + key: request_cost_set_by_ext_proc diff --git a/internal/utils/testdata/backendtrafficpolicy_ratelimitrule_merge.strategicmerge.out.yaml b/internal/utils/testdata/backendtrafficpolicy_ratelimitrule_merge.strategicmerge.out.yaml new file mode 100644 index 0000000000..1833716e3e --- /dev/null +++ b/internal/utils/testdata/backendtrafficpolicy_ratelimitrule_merge.strategicmerge.out.yaml @@ -0,0 +1,47 @@ +kind: BackendTrafficPolicy +metadata: + creationTimestamp: null + name: original +spec: + rateLimit: + global: + rules: + - clientSelectors: + - headers: + - name: x-user-id + type: Exact + value: two + 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: 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 + type: Global +status: + ancestors: null diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md index 2c7a476643..bab62ebc7b 100644 --- a/site/content/en/latest/api/extension_types.md +++ b/site/content/en/latest/api/extension_types.md @@ -3869,6 +3869,7 @@ _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. | diff --git a/test/helm/gateway-crds-helm/all.out.yaml b/test/helm/gateway-crds-helm/all.out.yaml index 68bac7a103..641df2259f 100644 --- a/test/helm/gateway-crds-helm/all.out.yaml +++ b/test/helm/gateway-crds-helm/all.out.yaml @@ -18559,8 +18559,11 @@ spec: - requests - unit type: object + name: + type: string required: - limit + - name type: object maxItems: 64 type: array @@ -18809,8 +18812,11 @@ spec: - requests - unit type: object + name: + type: string required: - limit + - name type: object maxItems: 16 type: array 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 c478588002..84b2b89f94 100644 --- a/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml +++ b/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml @@ -1247,8 +1247,11 @@ spec: - requests - unit type: object + name: + type: string required: - limit + - name type: object maxItems: 64 type: array @@ -1497,8 +1500,11 @@ spec: - requests - unit type: object + name: + type: string required: - limit + - name type: object maxItems: 16 type: array From 4bc6e831a88997cb8098aeb1c1c64043625b40e6 Mon Sep 17 00:00:00 2001 From: zirain Date: Sun, 4 May 2025 11:42:04 +0800 Subject: [PATCH 2/2] fix gen Signed-off-by: zirain --- test/helm/gateway-crds-helm/all.out.yaml | 8 ++++++-- test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/test/helm/gateway-crds-helm/all.out.yaml b/test/helm/gateway-crds-helm/all.out.yaml index 641df2259f..e88959ed64 100644 --- a/test/helm/gateway-crds-helm/all.out.yaml +++ b/test/helm/gateway-crds-helm/all.out.yaml @@ -18560,10 +18560,12 @@ spec: - unit type: object name: + 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 required: - limit - - name type: object maxItems: 64 type: array @@ -18813,10 +18815,12 @@ spec: - unit type: object name: + 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 required: - limit - - name type: object maxItems: 16 type: array 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 84b2b89f94..addf7bddf1 100644 --- a/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml +++ b/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml @@ -1248,10 +1248,12 @@ spec: - unit type: object name: + 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 required: - limit - - name type: object maxItems: 64 type: array @@ -1501,10 +1503,12 @@ spec: - unit type: object name: + 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 required: - limit - - name type: object maxItems: 16 type: array