From 5b496519d9f40073a71329436fa7ca20862635a2 Mon Sep 17 00:00:00 2001 From: Sergey Matov Date: Tue, 9 Jun 2026 16:47:41 +0400 Subject: [PATCH] chore(gateway): Add logs RateLimiter for Gateway A part of "BURST:REPLENISHMENTS_PER_SECOND" defines rate limiter config for Token/Bucket throttler in the Gatewy. Signed-off-by: Sergey Matov --- api/gateway/v1alpha1/gateway_types.go | 22 ++++++++++++++++++ api/gateway/v1alpha1/gateway_types_test.go | 23 +++++++++++++++++++ api/gateway/v1alpha1/zz_generated.deepcopy.go | 20 ++++++++++++++++ .../gateway.githedgehog.com_gateways.yaml | 17 ++++++++++++++ .../gwint.githedgehog.com_gatewayagents.yaml | 17 ++++++++++++++ docs/api.md | 19 +++++++++++++++ 6 files changed, 118 insertions(+) diff --git a/api/gateway/v1alpha1/gateway_types.go b/api/gateway/v1alpha1/gateway_types.go index 188e4c3f..fbf7dd4e 100644 --- a/api/gateway/v1alpha1/gateway_types.go +++ b/api/gateway/v1alpha1/gateway_types.go @@ -78,6 +78,19 @@ type GatewayBGPNeighbor struct { type GatewayLogs struct { Default GatewayLogLevel `json:"default,omitempty"` Tags map[string]GatewayLogLevel `json:"tags,omitempty"` + // RateLimit optionally throttles repeated log messages using a token + // bucket. When unset, log output is not rate limited. + RateLimit *GatewayLogRateLimit `json:"rateLimit,omitempty"` +} + +// GatewayLogRateLimit configures the token-bucket rate limiter applied to log +// output. Both fields must be greater than zero when the limiter is set. +type GatewayLogRateLimit struct { + // Burst is the maximum number of log messages allowed in a burst, i.e. the + // token bucket capacity + Burst uint32 `json:"burst,omitempty"` + // ReplenishPerSecond is the number of tokens (messages) replenished per second + ReplenishPerSecond uint32 `json:"replenishPerSecond,omitempty"` } type GatewayLogLevel string @@ -185,6 +198,15 @@ func (gw *Gateway) Validate(ctx context.Context, kube kclient.Reader, fabricCfg return fmt.Errorf("workers should be between 1 and 64: %w", ErrInvalidGW) } + if rl := gw.Spec.Logs.RateLimit; rl != nil { + if rl.Burst == 0 { + return fmt.Errorf("log rate limit burst must be greater than 0: %w", ErrInvalidGW) + } + if rl.ReplenishPerSecond == 0 { + return fmt.Errorf("log rate limit replenishPerSecond must be greater than 0: %w", ErrInvalidGW) + } + } + protoIP, err := netip.ParsePrefix(gw.Spec.ProtocolIP) if err != nil { return fmt.Errorf("invalid ProtocolIP %s: %w", gw.Spec.ProtocolIP, errors.Join(err, ErrInvalidGW)) diff --git a/api/gateway/v1alpha1/gateway_types_test.go b/api/gateway/v1alpha1/gateway_types_test.go index 2ed727ba..56593f74 100644 --- a/api/gateway/v1alpha1/gateway_types_test.go +++ b/api/gateway/v1alpha1/gateway_types_test.go @@ -277,6 +277,29 @@ func TestGatewayValidate(t *testing.T) { objs: base, err: v1alpha1.ErrInvalidGW, }, + { + name: "test-log-rate-limit-valid", + gw: *gwa("gw-1", func(gw *v1alpha1.Gateway) { + gw.Spec.Logs.RateLimit = &v1alpha1.GatewayLogRateLimit{Burst: 50, ReplenishPerSecond: 5} + }), + objs: base, + }, + { + name: "test-log-rate-limit-zero-burst", + gw: *gwa("gw-1", func(gw *v1alpha1.Gateway) { + gw.Spec.Logs.RateLimit = &v1alpha1.GatewayLogRateLimit{Burst: 0, ReplenishPerSecond: 5} + }), + objs: base, + err: v1alpha1.ErrInvalidGW, + }, + { + name: "test-log-rate-limit-zero-replenish", + gw: *gwa("gw-1", func(gw *v1alpha1.Gateway) { + gw.Spec.Logs.RateLimit = &v1alpha1.GatewayLogRateLimit{Burst: 50, ReplenishPerSecond: 0} + }), + objs: base, + err: v1alpha1.ErrInvalidGW, + }, } scheme := runtime.NewScheme() diff --git a/api/gateway/v1alpha1/zz_generated.deepcopy.go b/api/gateway/v1alpha1/zz_generated.deepcopy.go index 9c03cc1a..42407a61 100644 --- a/api/gateway/v1alpha1/zz_generated.deepcopy.go +++ b/api/gateway/v1alpha1/zz_generated.deepcopy.go @@ -209,6 +209,21 @@ func (in *GatewayList) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GatewayLogRateLimit) DeepCopyInto(out *GatewayLogRateLimit) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GatewayLogRateLimit. +func (in *GatewayLogRateLimit) DeepCopy() *GatewayLogRateLimit { + if in == nil { + return nil + } + out := new(GatewayLogRateLimit) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GatewayLogs) DeepCopyInto(out *GatewayLogs) { *out = *in @@ -219,6 +234,11 @@ func (in *GatewayLogs) DeepCopyInto(out *GatewayLogs) { (*out)[key] = val } } + if in.RateLimit != nil { + in, out := &in.RateLimit, &out.RateLimit + *out = new(GatewayLogRateLimit) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GatewayLogs. diff --git a/config/crd/bases/gateway.githedgehog.com_gateways.yaml b/config/crd/bases/gateway.githedgehog.com_gateways.yaml index e20926e2..9190fdf9 100644 --- a/config/crd/bases/gateway.githedgehog.com_gateways.yaml +++ b/config/crd/bases/gateway.githedgehog.com_gateways.yaml @@ -113,6 +113,23 @@ spec: properties: default: type: string + rateLimit: + description: |- + RateLimit optionally throttles repeated log messages using a token + bucket. When unset, log output is not rate limited. + properties: + burst: + description: |- + Burst is the maximum number of log messages allowed in a burst, i.e. the + token bucket capacity + format: int32 + type: integer + replenishPerSecond: + description: ReplenishPerSecond is the number of tokens (messages) + replenished per second + format: int32 + type: integer + type: object tags: additionalProperties: type: string diff --git a/config/crd/bases/gwint.githedgehog.com_gatewayagents.yaml b/config/crd/bases/gwint.githedgehog.com_gatewayagents.yaml index 2f17fe62..67796ff2 100644 --- a/config/crd/bases/gwint.githedgehog.com_gatewayagents.yaml +++ b/config/crd/bases/gwint.githedgehog.com_gatewayagents.yaml @@ -141,6 +141,23 @@ spec: properties: default: type: string + rateLimit: + description: |- + RateLimit optionally throttles repeated log messages using a token + bucket. When unset, log output is not rate limited. + properties: + burst: + description: |- + Burst is the maximum number of log messages allowed in a burst, i.e. the + token bucket capacity + format: int32 + type: integer + replenishPerSecond: + description: ReplenishPerSecond is the number of tokens + (messages) replenished per second + format: int32 + type: integer + type: object tags: additionalProperties: type: string diff --git a/docs/api.md b/docs/api.md index 0cb3f149..eca38423 100644 --- a/docs/api.md +++ b/docs/api.md @@ -991,6 +991,24 @@ _Appears in:_ | `trace` | | +#### GatewayLogRateLimit + + + +GatewayLogRateLimit configures the token-bucket rate limiter applied to log +output. Both fields must be greater than zero when the limiter is set. + + + +_Appears in:_ +- [GatewayLogs](#gatewaylogs) + +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `burst` _integer_ | Burst is the maximum number of log messages allowed in a burst, i.e. the
token bucket capacity | | | +| `replenishPerSecond` _integer_ | ReplenishPerSecond is the number of tokens (messages) replenished per second | | | + + #### GatewayLogs @@ -1006,6 +1024,7 @@ _Appears in:_ | --- | --- | --- | --- | | `default` _[GatewayLogLevel](#gatewayloglevel)_ | | | | | `tags` _object (keys:string, values:[GatewayLogLevel](#gatewayloglevel))_ | | | | +| `rateLimit` _[GatewayLogRateLimit](#gatewaylogratelimit)_ | RateLimit optionally throttles repeated log messages using a token
bucket. When unset, log output is not rate limited. | | | #### GatewayPeering