Skip to content

Commit e405149

Browse files
Add extproc translation in the agentgateway traffic policy plugin
Signed-off-by: Sibasish Behera <[email protected]> add tests for agentgateway extproc Signed-off-by: Sibasish Behera <[email protected]> bump agentgateway version Signed-off-by: Sibasish Behera <[email protected]> fix from review Signed-off-by: Sibasish Behera <[email protected]> revert unwanted auto generated changes Signed-off-by: Sibasish Behera <[email protected]>
1 parent 6341471 commit e405149

28 files changed

+1125
-16
lines changed

api/v1alpha1/ext_proc_types.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,24 +17,32 @@ type ExtProcProvider struct {
1717
FailOpen bool `json:"failOpen,omitempty"`
1818

1919
// ProcessingMode defines how the filter should interact with the request/response streams.
20+
// Envoy: Supported
21+
// Agentgateway: Not Supported (ignored)
2022
// +optional
2123
ProcessingMode *ProcessingMode `json:"processingMode,omitempty"`
2224

2325
// MessageTimeout is the timeout for each message sent to the external processing server.
26+
// Envoy: Supported
27+
// Agentgateway: Not Supported (ignored)
2428
// +optional
2529
// +kubebuilder:validation:XValidation:rule="matches(self, '^([0-9]{1,5}(h|m|s|ms)){1,4}$')",message="invalid timeout value"
2630
// +kubebuilder:validation:XValidation:rule="duration(self) >= duration('1ms')",message="timeout must be at least 1ms."
2731
MessageTimeout *metav1.Duration `json:"messageTimeout,omitempty"`
2832

2933
// MaxMessageTimeout specifies the upper bound of override_message_timeout that may be sent from the external processing server.
3034
// The default value 0, which effectively disables the override_message_timeout API.
35+
// Envoy: Supported
36+
// Agentgateway: Not Supported (ignored)
3137
// +optional
3238
// +kubebuilder:validation:XValidation:rule="matches(self, '^([0-9]{1,5}(h|m|s|ms)){1,4}$')",message="invalid timeout value"
3339
// +kubebuilder:validation:XValidation:rule="duration(self) >= duration('1ms')",message="timeout must be at least 1ms."
3440
MaxMessageTimeout *metav1.Duration `json:"maxMessageTimeout,omitempty"`
3541

3642
// StatPrefix is an optional prefix to include when emitting stats from the extproc filter,
3743
// enabling different instances of the filter to have unique stats.
44+
// Envoy: Supported
45+
// Agentgateway: Not Supported (ignored)
3846
// +optional
3947
// +kubebuilder:validation:MinLength=1
4048
StatPrefix *string `json:"statPrefix,omitempty"`
@@ -43,13 +51,17 @@ type ExtProcProvider struct {
4351
// external processor response is received in response to request headers.
4452
// The default behavior is "FromResponse" which will only clear the route cache when
4553
// an external processing response has the clear_route_cache field set.
54+
// Envoy: Supported
55+
// Agentgateway: Not Supported (ignored)
4656
// +optional
4757
// +kubebuilder:validation:Enum=FromResponse;Clear;Retain
4858
// +kubebuilder:default=FromResponse
4959
RouteCacheAction ExtProcRouteCacheAction `json:"routeCacheAction,omitempty"`
5060

5161
// MetadataOptions allows configuring metadata namespaces to forwarded or received from the external
5262
// processing server.
63+
// Envoy: Supported
64+
// Agentgateway: Not Supported (ignored)
5365
// +optional
5466
MetadataOptions *MetadataOptions `json:"metadataOptions,omitempty"`
5567
}
@@ -77,11 +89,15 @@ type ExtProcPolicy struct {
7789
ExtensionRef *NamespacedObjectReference `json:"extensionRef,omitempty"`
7890

7991
// ProcessingMode defines how the filter should interact with the request/response streams
92+
// Envoy: Supported
93+
// Agentgateway: Not Supported (ignored)
8094
// +optional
8195
ProcessingMode *ProcessingMode `json:"processingMode,omitempty"`
8296

8397
// Disable all external processing filters.
8498
// Can be used to disable external processing policies applied at a higher level in the config hierarchy.
99+
// Envoy: Supported
100+
// Agentgateway: Not Supported (ignored)
85101
// +optional
86102
Disable *PolicyDisable `json:"disable,omitempty"`
87103
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ go 1.25.3
44

55
require (
66
// Also update AgentgatewayDefaultTag in pkg/deployer/wellknown.go and test/deployer/testdata/*
7-
github.com/agentgateway/agentgateway v0.10.3
7+
github.com/agentgateway/agentgateway v0.10.4
88
github.com/avast/retry-go/v4 v4.3.3
99
github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443
1010
github.com/envoyproxy/go-control-plane v0.13.5-0.20251015221300-4138018a492b

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,8 +194,8 @@ github.com/ProtonMail/gopenpgp/v2 v2.7.1 h1:Awsg7MPc2gD3I7IFac2qE3Gdls0lZW8SzrFZ
194194
github.com/ProtonMail/gopenpgp/v2 v2.7.1/go.mod h1:/BU5gfAVwqyd8EfC3Eu7zmuhwYQpKs+cGD8M//iiaxs=
195195
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
196196
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
197-
github.com/agentgateway/agentgateway v0.10.3 h1:z5HcuNuiz+NTZR87sU91xsjxvBD9bRP2xOhJwGbgW8Y=
198-
github.com/agentgateway/agentgateway v0.10.3/go.mod h1:geHd31xH5d8OrCRuRyvnDxBeqSZ9+O9VhL8VJAfcA+E=
197+
github.com/agentgateway/agentgateway v0.10.4 h1:9zROecngjHJcdWwqoow2N0Ngs+c+yCgAwEptdqYz6n8=
198+
github.com/agentgateway/agentgateway v0.10.4/go.mod h1:geHd31xH5d8OrCRuRyvnDxBeqSZ9+O9VhL8VJAfcA+E=
199199
github.com/agnivade/levenshtein v1.2.1 h1:EHBY3UOn1gwdy/VbFwgo4cxecRznFk7fKWN1KOX7eoM=
200200
github.com/agnivade/levenshtein v1.2.1/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU=
201201
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=

hack/utils/oss_compliance/osa_provided.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Name|Version|License
22
---|---|---
33
[cel.dev/expr](https://cel.dev/expr)|v0.24.0|Apache License 2.0
4-
[agentgateway/agentgateway](https://github.com/agentgateway/agentgateway)|v0.10.3|Apache License 2.0
4+
[agentgateway/agentgateway](https://github.com/agentgateway/agentgateway)|v0.10.4|Apache License 2.0
55
[anthropics/anthropic-sdk-go](https://github.com/anthropics/anthropic-sdk-go)|v1.13.0|MIT License
66
[retry-go/v4](https://github.com/avast/retry-go)|v4.3.3|MIT License
77
[xds/go](https://github.com/cncf/xds)|v0.0.0-20250501225837-2ac532fd4443|Apache License 2.0

install/helm/kgateway-crds/templates/gateway.kgateway.dev_gatewayextensions.yaml

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -439,15 +439,19 @@ spec:
439439
description: |-
440440
MaxMessageTimeout specifies the upper bound of override_message_timeout that may be sent from the external processing server.
441441
The default value 0, which effectively disables the override_message_timeout API.
442+
Envoy: Supported
443+
Agentgateway: Not Supported (ignored)
442444
type: string
443445
x-kubernetes-validations:
444446
- message: invalid timeout value
445447
rule: matches(self, '^([0-9]{1,5}(h|m|s|ms)){1,4}$')
446448
- message: timeout must be at least 1ms.
447449
rule: duration(self) >= duration('1ms')
448450
messageTimeout:
449-
description: MessageTimeout is the timeout for each message sent
450-
to the external processing server.
451+
description: |-
452+
MessageTimeout is the timeout for each message sent to the external processing server.
453+
Envoy: Supported
454+
Agentgateway: Not Supported (ignored)
451455
type: string
452456
x-kubernetes-validations:
453457
- message: invalid timeout value
@@ -458,6 +462,8 @@ spec:
458462
description: |-
459463
MetadataOptions allows configuring metadata namespaces to forwarded or received from the external
460464
processing server.
465+
Envoy: Supported
466+
Agentgateway: Not Supported (ignored)
461467
properties:
462468
forwarding:
463469
description: Forwarding defines the typed or untyped dynamic
@@ -477,8 +483,10 @@ spec:
477483
type: object
478484
type: object
479485
processingMode:
480-
description: ProcessingMode defines how the filter should interact
481-
with the request/response streams.
486+
description: |-
487+
ProcessingMode defines how the filter should interact with the request/response streams.
488+
Envoy: Supported
489+
Agentgateway: Not Supported (ignored)
482490
properties:
483491
requestBodyMode:
484492
default: NONE
@@ -546,6 +554,8 @@ spec:
546554
external processor response is received in response to request headers.
547555
The default behavior is "FromResponse" which will only clear the route cache when
548556
an external processing response has the clear_route_cache field set.
557+
Envoy: Supported
558+
Agentgateway: Not Supported (ignored)
549559
enum:
550560
- FromResponse
551561
- Clear
@@ -555,6 +565,8 @@ spec:
555565
description: |-
556566
StatPrefix is an optional prefix to include when emitting stats from the extproc filter,
557567
enabling different instances of the filter to have unique stats.
568+
Envoy: Supported
569+
Agentgateway: Not Supported (ignored)
558570
minLength: 1
559571
type: string
560572
required:

install/helm/kgateway-crds/templates/gateway.kgateway.dev_trafficpolicies.yaml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1033,6 +1033,8 @@ spec:
10331033
description: |-
10341034
Disable all external processing filters.
10351035
Can be used to disable external processing policies applied at a higher level in the config hierarchy.
1036+
Envoy: Supported
1037+
Agentgateway: Not Supported (ignored)
10361038
type: object
10371039
extensionRef:
10381040
description: ExtensionRef references the GatewayExtension that
@@ -1055,8 +1057,10 @@ spec:
10551057
- name
10561058
type: object
10571059
processingMode:
1058-
description: ProcessingMode defines how the filter should interact
1059-
with the request/response streams
1060+
description: |-
1061+
ProcessingMode defines how the filter should interact with the request/response streams
1062+
Envoy: Supported
1063+
Agentgateway: Not Supported (ignored)
10601064
properties:
10611065
requestBodyMode:
10621066
default: NONE

pkg/agentgateway/plugins/traffic_plugin.go

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ const (
4242
globalRateLimitPolicySuffix = ":rl-global"
4343
transformationPolicySuffix = ":transformation"
4444
csrfPolicySuffix = ":csrf"
45+
extprocPolicySuffix = ":ext-proc"
4546
)
4647

4748
var logger = logging.New("agentgateway/plugins")
@@ -398,6 +399,16 @@ func translateTrafficPolicyToAgw(
398399
agwPolicies = append(agwPolicies, csrfPolicies...)
399400
}
400401

402+
// Process extproc policies if present
403+
if trafficPolicy.Spec.ExtProc != nil {
404+
extProcPolicies, err := processExtProcPolicy(ctx, gatewayExtensions, trafficPolicy, policyName, policyTarget)
405+
if err != nil {
406+
logger.Error("error processing extproc policy", "error", err)
407+
errs = append(errs, err)
408+
}
409+
agwPolicies = append(agwPolicies, extProcPolicies...)
410+
}
411+
401412
return agwPolicies, errors.Join(errs...)
402413
}
403414

@@ -864,6 +875,114 @@ func processRateLimitPolicy(ctx krt.HandlerContext, gatewayExtensions krt.Collec
864875
return agwPolicies, errors.Join(errs...)
865876
}
866877

878+
func processExtProcPolicy(ctx krt.HandlerContext, gatewayExtensions krt.Collection[*v1alpha1.GatewayExtension], trafficPolicy *v1alpha1.TrafficPolicy, policyName string, policyTarget *api.PolicyTarget) ([]AgwPolicy, error) {
879+
var errs []error
880+
881+
// validate that unsupported ExtProcPolicy fields are not set
882+
if err := validateExtProcPolicy(trafficPolicy.Spec.ExtProc); err != nil {
883+
errs = append(errs, err)
884+
}
885+
886+
gwExt, err := lookupGatewayExtension(ctx, gatewayExtensions, *trafficPolicy.Spec.ExtProc.ExtensionRef, trafficPolicy.Namespace, v1alpha1.GatewayExtensionTypeExtProc)
887+
if err != nil {
888+
return nil, err
889+
}
890+
891+
extProc := (*gwExt).Spec.ExtProc
892+
if extProc == nil {
893+
return nil, fmt.Errorf("extproc provider is missing from gateway extension %s/%s", gwExt.Namespace, gwExt.Namespace)
894+
}
895+
896+
// validate that unsupported ExtProcProvider fields are not set
897+
if err := validateExtProcProvider(extProc); err != nil {
898+
errs = append(errs, err)
899+
}
900+
901+
var extProcSvcTarget *api.BackendReference
902+
if extProc.GrpcService != nil && extProc.GrpcService.BackendRef != nil {
903+
var err error
904+
extProcSvcTarget, err = buildAGWServiceRef(extProc.GrpcService.BackendRef, trafficPolicy.Namespace)
905+
if err != nil {
906+
return nil, fmt.Errorf("failed to build extproc service reference: %w", err)
907+
}
908+
}
909+
910+
if extProcSvcTarget == nil {
911+
return nil, fmt.Errorf("extproc policy %s/%s missing backendRef in gateway extension %s/%s",
912+
trafficPolicy.Namespace, trafficPolicy.Name, (*gwExt).Namespace, (*gwExt).Name)
913+
}
914+
915+
failureMode := api.PolicySpec_ExtProc_FAIL_CLOSED
916+
if extProc.FailOpen {
917+
failureMode = api.PolicySpec_ExtProc_FAIL_OPEN
918+
}
919+
920+
extProcPolicy := &api.Policy{
921+
Name: policyName + extprocPolicySuffix + attachmentName(policyTarget),
922+
Target: policyTarget,
923+
Spec: &api.PolicySpec{
924+
Kind: &api.PolicySpec_ExtProc_{
925+
ExtProc: &api.PolicySpec_ExtProc{
926+
Target: extProcSvcTarget,
927+
FailureMode: failureMode,
928+
},
929+
},
930+
},
931+
}
932+
933+
logger.Debug("generated ExtProc policy",
934+
"policy", trafficPolicy.Name,
935+
"agentgateway_policy", extProcPolicy.Name,
936+
"target", extProcSvcTarget)
937+
938+
return []AgwPolicy{{Policy: extProcPolicy}}, errors.Join(errs...)
939+
}
940+
941+
// validateExtProcPolicy validates that unsupported ExtProcPolicy fields are not set.
942+
func validateExtProcPolicy(policy *v1alpha1.ExtProcPolicy) error {
943+
var errs []error
944+
945+
if policy.ProcessingMode != nil {
946+
errs = append(errs, fmt.Errorf("processingMode field is not supported for agentgateway"))
947+
}
948+
949+
if policy.Disable != nil {
950+
errs = append(errs, fmt.Errorf("disable field is not supported for agentgateway"))
951+
}
952+
953+
return errors.Join(errs...)
954+
}
955+
956+
// validateExtProcProvider validates that unsupported ExtProcProvider fields are not set.
957+
func validateExtProcProvider(provider *v1alpha1.ExtProcProvider) error {
958+
var errs []error
959+
960+
if provider.ProcessingMode != nil {
961+
errs = append(errs, fmt.Errorf("processingMode field in ExtProcProvider is not supported for agentgateway"))
962+
}
963+
964+
if provider.MessageTimeout != nil {
965+
errs = append(errs, fmt.Errorf("messageTimeout field in ExtProcProvider is not supported for agentgateway"))
966+
}
967+
968+
if provider.MaxMessageTimeout != nil {
969+
errs = append(errs, fmt.Errorf("maxMessageTimeout field in ExtProcProvider is not supported for agentgateway"))
970+
}
971+
972+
if provider.StatPrefix != nil {
973+
errs = append(errs, fmt.Errorf("statPrefix field in ExtProcProvider is not supported for agentgateway"))
974+
}
975+
976+
// TODO: RouteCacheAction has a kubebuilder default so we don't validate it here
977+
// to avoid false positives when the default is automatically applied.
978+
979+
if provider.MetadataOptions != nil {
980+
errs = append(errs, fmt.Errorf("metadataOptions field in ExtProcProvider is not supported for agentgateway"))
981+
}
982+
983+
return errors.Join(errs...)
984+
}
985+
867986
// processLocalRateLimitPolicy processes local rate limiting configuration
868987
func processLocalRateLimitPolicy(trafficPolicy *v1alpha1.TrafficPolicy, policyName string, policyTarget *api.PolicyTarget) (*AgwPolicy, error) {
869988
if trafficPolicy.Spec.RateLimit.Local.TokenBucket == nil {

pkg/deployer/wellknown.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const (
1717
AgentgatewayRegistry = "ghcr.io/agentgateway"
1818
// AgentgatewayDefaultTag is the default agentgateway image tag
1919
// Note: should be in sync with version in go.mod and test/deployer/testdata/*
20-
AgentgatewayDefaultTag = "0.10.3"
20+
AgentgatewayDefaultTag = "0.10.4"
2121
// SdsImage is the image of the sds container.
2222
SdsImage = "sds"
2323
// SdsContainerName is the name of the container in the proxy deployment for the SDS integration.

test/deployer/testdata/agentgateway-infrastructure-out.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ spec:
124124
value: "0"
125125
containers:
126126
- name: agent-gateway
127-
image: "ghcr.io/agentgateway/agentgateway:0.10.3"
127+
image: "ghcr.io/agentgateway/agentgateway:0.10.4"
128128
securityContext:
129129
allowPrivilegeEscalation: false
130130
capabilities:

test/deployer/testdata/agentgateway-omitdefaultsecuritycontext-out.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ spec:
9696
serviceAccountName: gw
9797
containers:
9898
- name: agent-gateway
99-
image: "ghcr.io/agentgateway/agentgateway:0.10.3"
99+
image: "ghcr.io/agentgateway/agentgateway:0.10.4"
100100
startupProbe:
101101
failureThreshold: 60
102102
httpGet:

0 commit comments

Comments
 (0)