Skip to content

Commit e14c2b3

Browse files
authored
Merge pull request #93 from kgateway-dev/issue_59_12
agentgateway emitter: map body-size annotations to frontend HTTP buffer
2 parents 158809c + 1ea2174 commit e14c2b3

6 files changed

Lines changed: 206 additions & 9 deletions

File tree

pkg/i2gw/emitters/agentgateway/README.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,13 +398,30 @@ This is projected into a **Service-targeted** `AgentgatewayPolicy` by setting:
398398
- The provider currently maps only gRPC-family values into policy IR, so the agentgateway emitter currently emits
399399
only `HTTP2` for this feature.
400400

401+
#### Request/Body Buffer Size
402+
403+
The agentgateway emitter supports projecting ingress-nginx request/body size controls via:
404+
405+
- `nginx.ingress.kubernetes.io/proxy-body-size`
406+
- `nginx.ingress.kubernetes.io/client-body-buffer-size`
407+
408+
These are projected into an `AgentgatewayPolicy` by setting:
409+
410+
- `AgentgatewayPolicy.spec.frontend.http.maxBufferSize`
411+
412+
**Semantics:**
413+
414+
- `proxy-body-size` is preferred when both annotations are present.
415+
- `client-body-buffer-size` is used as a fallback when `proxy-body-size` is unset.
416+
- The selected quantity is resolved to bytes for `maxBufferSize`.
417+
401418
## AgentgatewayPolicy Projection
402419

403420
Rate limit, timeout, CORS, rewrite target, access log, etc. annotations are converted into AgentgatewayPolicy resources.
404421
The agentgateway emitter emits AgentgatewayPolicy resources in two shapes:
405422

406423
- HTTPRoute-scoped policies for traffic-level behavior (rate limit, request timeouts, CORS, rewrite target,
407-
basic auth, ext auth, access log).
424+
basic auth, ext auth, access log, request/body buffer size).
408425
- Service-scoped policies for backend connection behavior (backend TLS, proxy connect timeout, backend protocol).
409426

410427
### Naming
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
Copyright 2025 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package agentgateway
18+
19+
import (
20+
"fmt"
21+
"math"
22+
23+
emitterir "github.com/kgateway-dev/ingress2gateway/pkg/i2gw/emitter_intermediate"
24+
agentgatewayv1alpha1 "github.com/kgateway-dev/kgateway/v2/api/v1alpha1/agentgateway"
25+
"k8s.io/apimachinery/pkg/util/validation/field"
26+
)
27+
28+
// applyBufferPolicy projects ingress-nginx body-size annotations into
29+
// AgentgatewayPolicy.spec.frontend.http.maxBufferSize.
30+
//
31+
// Semantics:
32+
// - Prefer proxy-body-size when set.
33+
// - Otherwise fall back to client-body-buffer-size.
34+
func applyBufferPolicy(
35+
pol emitterir.Policy,
36+
ingressName, namespace string,
37+
ap map[string]*agentgatewayv1alpha1.AgentgatewayPolicy,
38+
) (bool, *field.Error) {
39+
if pol.ProxyBodySize == nil && pol.ClientBodyBufferSize == nil {
40+
return false, nil
41+
}
42+
43+
size := pol.ProxyBodySize
44+
if size == nil {
45+
size = pol.ClientBodyBufferSize
46+
}
47+
if size == nil {
48+
return false, nil
49+
}
50+
51+
sizeBytes := size.Value()
52+
if sizeBytes <= 0 {
53+
return false, field.Invalid(
54+
field.NewPath("emitter", "agentgateway", "AgentgatewayPolicy", "frontend", "http", "maxBufferSize"),
55+
size.String(),
56+
"resolved maxBufferSize must be greater than 0",
57+
)
58+
}
59+
if sizeBytes > math.MaxInt32 {
60+
return false, field.Invalid(
61+
field.NewPath("emitter", "agentgateway", "AgentgatewayPolicy", "frontend", "http", "maxBufferSize"),
62+
size.String(),
63+
fmt.Sprintf("resolved maxBufferSize exceeds int32 limit (%d)", int64(math.MaxInt32)),
64+
)
65+
}
66+
67+
maxBufferSize := int32(sizeBytes)
68+
agp := ensureAgentgatewayPolicy(ap, ingressName, namespace)
69+
if agp.Spec.Frontend == nil {
70+
agp.Spec.Frontend = &agentgatewayv1alpha1.Frontend{}
71+
}
72+
if agp.Spec.Frontend.HTTP == nil {
73+
agp.Spec.Frontend.HTTP = &agentgatewayv1alpha1.FrontendHTTP{}
74+
}
75+
agp.Spec.Frontend.HTTP.MaxBufferSize = &maxBufferSize
76+
return true, nil
77+
}

pkg/i2gw/emitters/agentgateway/emitter.go

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -176,14 +176,16 @@ func (e *Emitter) Emit(ir emitterir.EmitterIR) (i2gw.GatewayResources, field.Err
176176
)
177177
}
178178

179-
// Buffer policy currently has no equivalent in AgentgatewayPolicy (see README for limitations).
180-
if pol.ProxyBodySize != nil || pol.ClientBodyBufferSize != nil {
181-
errs = append(errs, field.Invalid(
182-
field.NewPath("emitter", "agentgateway", "AgentgatewayPolicy"),
183-
polSourceIngressName,
184-
"buffer policy (nginx.ingress.kubernetes.io/proxy-body-size/nginx.ingress.kubernetes.io/client-body-buffer-size) "+
185-
"is not supported by the agentgateway emitter",
186-
))
179+
// Buffer policy maps to AgentgatewayPolicy.spec.frontend.http.maxBufferSize.
180+
if bufferTouched, bufferErr := applyBufferPolicy(
181+
pol,
182+
polSourceIngressName,
183+
httpRouteKey.Namespace,
184+
agentgatewayPolicies,
185+
); bufferErr != nil {
186+
errs = append(errs, bufferErr)
187+
} else if bufferTouched {
188+
touched = true
187189
}
188190

189191
// Attach the resulting AgentgatewayPolicy to the HTTPRoute, but only if the policy fully covers the route.

pkg/i2gw/emitters/agentgateway/emitter_integration_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,32 @@ func TestAgentgatewayIngressNginxIntegration_AccessLog(t *testing.T) {
441441
}
442442
}
443443

444+
func TestAgentgatewayIngressNginxIntegration_BufferBodySize(t *testing.T) {
445+
t.Helper()
446+
447+
tests := []struct {
448+
name string
449+
inputRel string
450+
goldenRel string
451+
}{
452+
{
453+
name: "buffer_body_size",
454+
inputRel: filepath.Join(
455+
"pkg", "i2gw", "emitters", "agentgateway", "testing", "testdata", "input", "buffer_body_size.yaml",
456+
),
457+
goldenRel: filepath.Join(
458+
"pkg", "i2gw", "emitters", "agentgateway", "testing", "testdata", "output", "buffer_body_size.yaml",
459+
),
460+
},
461+
}
462+
463+
for _, tt := range tests {
464+
t.Run(tt.name, func(t *testing.T) {
465+
runGoldenTest(t, tt.inputRel, tt.goldenRel)
466+
})
467+
}
468+
}
469+
444470
func TestAgentgatewayIngressNginxIntegration_SSLRedirect(t *testing.T) {
445471
t.Helper()
446472

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
apiVersion: networking.k8s.io/v1
2+
kind: Ingress
3+
metadata:
4+
annotations:
5+
nginx.ingress.kubernetes.io/client-body-buffer-size: "1Mi"
6+
nginx.ingress.kubernetes.io/proxy-body-size: "2Mi"
7+
name: ingress-buffer-body-size
8+
namespace: default
9+
spec:
10+
ingressClassName: nginx
11+
rules:
12+
- host: buffer-body-size.example.org
13+
http:
14+
paths:
15+
- backend:
16+
service:
17+
name: myservice
18+
port:
19+
number: 80
20+
path: /
21+
pathType: Prefix
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
apiVersion: agentgateway.dev/v1alpha1
2+
kind: AgentgatewayPolicy
3+
metadata:
4+
name: ingress-buffer-body-size
5+
namespace: default
6+
spec:
7+
frontend:
8+
http:
9+
maxBufferSize: 2097152
10+
targetRefs:
11+
- group: gateway.networking.k8s.io
12+
kind: HTTPRoute
13+
name: ingress-buffer-body-size-buffer-body-size-example-org
14+
status:
15+
ancestors: null
16+
---
17+
apiVersion: gateway.networking.k8s.io/v1
18+
kind: Gateway
19+
metadata:
20+
annotations:
21+
gateway.networking.k8s.io/generator: ingress2gateway-dev
22+
name: nginx
23+
namespace: default
24+
spec:
25+
gatewayClassName: agentgateway
26+
listeners:
27+
- hostname: buffer-body-size.example.org
28+
name: buffer-body-size-example-org-http
29+
port: 80
30+
protocol: HTTP
31+
status: {}
32+
---
33+
apiVersion: gateway.networking.k8s.io/v1
34+
kind: HTTPRoute
35+
metadata:
36+
annotations:
37+
gateway.networking.k8s.io/generator: ingress2gateway-dev
38+
name: ingress-buffer-body-size-buffer-body-size-example-org
39+
namespace: default
40+
spec:
41+
hostnames:
42+
- buffer-body-size.example.org
43+
parentRefs:
44+
- name: nginx
45+
rules:
46+
- backendRefs:
47+
- name: myservice
48+
port: 80
49+
matches:
50+
- path:
51+
type: PathPrefix
52+
value: /
53+
status:
54+
parents: []

0 commit comments

Comments
 (0)