diff --git a/api/v1alpha1/ratelimit_types.go b/api/v1alpha1/ratelimit_types.go
index de195ffd760..cc9b742fa65 100644
--- a/api/v1alpha1/ratelimit_types.go
+++ b/api/v1alpha1/ratelimit_types.go
@@ -98,7 +98,6 @@ type RateLimitRule struct {
// the request path and do not reduce the rate limit counters on the response path.
//
// +optional
- // +notImplementedHide
Cost *RateLimitCost `json:"cost,omitempty"`
}
@@ -112,7 +111,6 @@ type RateLimitCost struct {
// enough capacity, the request is rate limited.
//
// +optional
- // +notImplementedHide
Request *RateLimitCostSpecifier `json:"request,omitempty"`
// Response specifies the number to reduce the rate limit counters
// after the response is sent back to the client or the request stream is closed.
@@ -127,7 +125,6 @@ type RateLimitCost struct {
// Currently, this is only supported for HTTP Global Rate Limits.
//
// +optional
- // +notImplementedHide
Response *RateLimitCostSpecifier `json:"response,omitempty"`
}
@@ -143,12 +140,10 @@ type RateLimitCostSpecifier struct {
// Using zero can be used to only check the rate limit counters without reducing them.
//
// +optional
- // +notImplementedHide
Number *uint64 `json:"number,omitempty"`
// Metadata specifies the per-request metadata to retrieve the usage number from.
//
// +optional
- // +notImplementedHide
Metadata *RateLimitCostMetadata `json:"metadata,omitempty"`
}
diff --git a/examples/grpc-ext-proc/main.go b/examples/grpc-ext-proc/main.go
index 0aab517f298..fa333f7d3f5 100644
--- a/examples/grpc-ext-proc/main.go
+++ b/examples/grpc-ext-proc/main.go
@@ -20,7 +20,6 @@ import (
envoy_api_v3_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
envoy_service_proc_v3 "github.com/envoyproxy/go-control-plane/envoy/service/ext_proc/v3"
-
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials"
@@ -329,10 +328,26 @@ func (s *extProcServer) Process(srv envoy_service_proc_v3.ExternalProcessor_Proc
},
},
}
+
resp = &envoy_service_proc_v3.ProcessingResponse{
Response: &envoy_service_proc_v3.ProcessingResponse_ResponseHeaders{
ResponseHeaders: rhq,
},
+ DynamicMetadata: &structpb.Struct{
+ Fields: map[string]*structpb.Value{
+ "io.envoyproxy.gateway.e2e": {
+ Kind: &structpb.Value_StructValue{
+ StructValue: &structpb.Struct{
+ Fields: map[string]*structpb.Value{
+ "request_cost_set_by_ext_proc": {
+ Kind: &structpb.Value_NumberValue{NumberValue: float64(10)},
+ },
+ },
+ },
+ },
+ },
+ },
+ },
}
break
default:
diff --git a/internal/gatewayapi/backendtrafficpolicy.go b/internal/gatewayapi/backendtrafficpolicy.go
index 2d07982cfef..0246c4115bc 100644
--- a/internal/gatewayapi/backendtrafficpolicy.go
+++ b/internal/gatewayapi/backendtrafficpolicy.go
@@ -791,9 +791,30 @@ func buildRateLimitRule(rule egv1a1.RateLimitRule) (*ir.RateLimitRule, error) {
irRule.CIDRMatch = cidrMatch
}
}
+
+ if cost := rule.Cost; cost != nil {
+ if cost.Request != nil {
+ irRule.RequestCost = translateRateLimitCost(cost.Request)
+ }
+ if cost.Response != nil {
+ irRule.ResponseCost = translateRateLimitCost(cost.Response)
+ }
+ }
return irRule, nil
}
+func translateRateLimitCost(cost *egv1a1.RateLimitCostSpecifier) *ir.RateLimitCost {
+ ret := &ir.RateLimitCost{}
+ if cost.Number != nil {
+ ret.Number = cost.Number
+ }
+ if cost.Metadata != nil {
+ ret.Format = ptr.To(fmt.Sprintf("%%DYNAMIC_METADATA(%s:%s)%%",
+ cost.Metadata.Namespace, cost.Metadata.Key))
+ }
+ return ret
+}
+
func int64ToUint32(in int64) (uint32, bool) {
if in >= 0 && in <= math.MaxUint32 {
return uint32(in), true
diff --git a/internal/gatewayapi/backendtrafficpolicy_test.go b/internal/gatewayapi/backendtrafficpolicy_test.go
index ebf721fb07d..e104f3b0bae 100644
--- a/internal/gatewayapi/backendtrafficpolicy_test.go
+++ b/internal/gatewayapi/backendtrafficpolicy_test.go
@@ -11,6 +11,7 @@ import (
"testing"
"github.com/stretchr/testify/require"
+ "k8s.io/utils/ptr"
egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1"
"github.com/envoyproxy/gateway/internal/ir"
@@ -107,3 +108,27 @@ func TestMakeIrTriggerSet(t *testing.T) {
})
}
}
+
+func Test_translateRateLimitCost(t *testing.T) {
+ for _, tc := range []struct {
+ name string
+ cost *egv1a1.RateLimitCostSpecifier
+ exp *ir.RateLimitCost
+ }{
+ {
+ name: "number",
+ cost: &egv1a1.RateLimitCostSpecifier{Number: ptr.To[uint64](1)},
+ exp: &ir.RateLimitCost{Number: ptr.To[uint64](1)},
+ },
+ {
+ name: "metadata",
+ cost: &egv1a1.RateLimitCostSpecifier{Metadata: &egv1a1.RateLimitCostMetadata{Namespace: "something.com", Key: "name"}},
+ exp: &ir.RateLimitCost{Format: ptr.To(`%DYNAMIC_METADATA(something.com:name)%`)},
+ },
+ } {
+ t.Run(tc.name, func(t *testing.T) {
+ act := translateRateLimitCost(tc.cost)
+ require.Equal(t, tc.exp, act)
+ })
+ }
+}
diff --git a/internal/gatewayapi/clienttrafficpolicy.go b/internal/gatewayapi/clienttrafficpolicy.go
index 1c8d0f8af4a..162f1698061 100644
--- a/internal/gatewayapi/clienttrafficpolicy.go
+++ b/internal/gatewayapi/clienttrafficpolicy.go
@@ -484,6 +484,12 @@ func (t *Translator) translateClientTrafficPolicyForListener(policy *egv1a1.Clie
// Early return if got any errors
if errs != nil {
+ for _, route := range httpIR.Routes {
+ // Return a 500 direct response
+ route.DirectResponse = &ir.CustomResponse{
+ StatusCode: ptr.To(uint32(500)),
+ }
+ }
return errs
}
@@ -504,6 +510,9 @@ func (t *Translator) translateClientTrafficPolicyForListener(policy *egv1a1.Clie
// Early return if got any errors
if errs != nil {
+ // Remove all TCP routes if there are any errors
+ // The listener will still be created, but any client traffic will be forwarded to the default empty cluster
+ tcpIR.Routes = nil
return errs
}
diff --git a/internal/gatewayapi/ext_service.go b/internal/gatewayapi/ext_service.go
index b1111ffa0f4..26452399ed0 100644
--- a/internal/gatewayapi/ext_service.go
+++ b/internal/gatewayapi/ext_service.go
@@ -95,8 +95,7 @@ func (t *Translator) processExtServiceDestination(
if !t.BackendEnabled {
return nil, fmt.Errorf("resource %s of type Backend cannot be used since Backend is disabled in Envoy Gateway configuration", string(backendRef.Name))
}
- ds = t.processBackendDestinationSetting(backendRef.BackendObjectReference, backendNamespace, resources)
- ds.Protocol = protocol
+ ds = t.processBackendDestinationSetting(backendRef.BackendObjectReference, backendNamespace, protocol, resources)
}
if ds == nil {
diff --git a/internal/gatewayapi/route.go b/internal/gatewayapi/route.go
index 56466649d80..8145c49f773 100644
--- a/internal/gatewayapi/route.go
+++ b/internal/gatewayapi/route.go
@@ -1359,7 +1359,7 @@ func (t *Translator) processDestination(backendRefContext BackendRefContext,
}
ds.IPFamily = getServiceIPFamily(resources.GetService(backendNamespace, string(backendRef.Name)))
case egv1a1.KindBackend:
- ds = t.processBackendDestinationSetting(backendRef.BackendObjectReference, backendNamespace, resources)
+ ds = t.processBackendDestinationSetting(backendRef.BackendObjectReference, backendNamespace, protocol, resources)
ds.TLS, err = t.applyBackendTLSSetting(
backendRef.BackendObjectReference,
backendNamespace,
@@ -1725,11 +1725,10 @@ func getTargetBackendReference(backendRef gwapiv1a2.BackendObjectReference, back
return ref
}
-func (t *Translator) processBackendDestinationSetting(backendRef gwapiv1.BackendObjectReference, backendNamespace string, resources *resource.Resources) *ir.DestinationSetting {
+func (t *Translator) processBackendDestinationSetting(backendRef gwapiv1.BackendObjectReference, backendNamespace string, protocol ir.AppProtocol, resources *resource.Resources) *ir.DestinationSetting {
var (
dstEndpoints []*ir.DestinationEndpoint
dstAddrType *ir.DestinationAddressType
- dstProtocol ir.AppProtocol
)
addrTypeMap := make(map[ir.DestinationAddressType]int)
@@ -1775,14 +1774,16 @@ func (t *Translator) processBackendDestinationSetting(backendRef gwapiv1.Backend
}
for _, ap := range backend.Spec.AppProtocols {
- if ap == egv1a1.AppProtocolTypeH2C {
- dstProtocol = ir.HTTP2
- break
+ switch ap {
+ case egv1a1.AppProtocolTypeH2C:
+ protocol = ir.HTTP2
+ case "grpc":
+ protocol = ir.GRPC
}
}
ds := &ir.DestinationSetting{
- Protocol: dstProtocol,
+ Protocol: protocol,
Endpoints: dstEndpoints,
AddressType: dstAddrType,
}
diff --git a/internal/gatewayapi/testdata/backend-with-fallback.out.yaml b/internal/gatewayapi/testdata/backend-with-fallback.out.yaml
index 74bd61795fe..d5f7e87642d 100644
--- a/internal/gatewayapi/testdata/backend-with-fallback.out.yaml
+++ b/internal/gatewayapi/testdata/backend-with-fallback.out.yaml
@@ -160,12 +160,14 @@ xdsIR:
endpoints:
- host: 1.1.1.1
port: 3001
+ protocol: HTTP
weight: 1
- addressType: IP
endpoints:
- host: 2.2.2.2
port: 3001
priority: 1
+ protocol: HTTP
weight: 1
hostname: '*'
isHTTP2: false
diff --git a/internal/gatewayapi/testdata/backendtlspolicy-default-ns-targetrefs.out.yaml b/internal/gatewayapi/testdata/backendtlspolicy-default-ns-targetrefs.out.yaml
index bbea6c79f5f..a6cf1223961 100644
--- a/internal/gatewayapi/testdata/backendtlspolicy-default-ns-targetrefs.out.yaml
+++ b/internal/gatewayapi/testdata/backendtlspolicy-default-ns-targetrefs.out.yaml
@@ -300,6 +300,7 @@ xdsIR:
endpoints:
- host: 2.2.2.2
port: 3443
+ protocol: HTTP
tls:
alpnProtocols: null
caCertificate:
@@ -357,6 +358,7 @@ xdsIR:
endpoints:
- host: 2.2.2.2
port: 3443
+ protocol: HTTP
tls:
alpnProtocols: null
caCertificate:
diff --git a/internal/gatewayapi/testdata/backendtlspolicy-default-ns.out.yaml b/internal/gatewayapi/testdata/backendtlspolicy-default-ns.out.yaml
index 0fbf1d8d411..5dab3f46b19 100644
--- a/internal/gatewayapi/testdata/backendtlspolicy-default-ns.out.yaml
+++ b/internal/gatewayapi/testdata/backendtlspolicy-default-ns.out.yaml
@@ -261,6 +261,7 @@ xdsIR:
endpoints:
- host: 2.2.2.2
port: 3443
+ protocol: HTTP
tls:
alpnProtocols: null
caCertificate:
@@ -272,6 +273,7 @@ xdsIR:
endpoints:
- host: 3.3.3.3
port: 3443
+ protocol: HTTP
weight: 1
hostname: '*'
isHTTP2: false
diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-ratelimit.in.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-ratelimit.in.yaml
index f536d9a20bc..67874070632 100644
--- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-ratelimit.in.yaml
+++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-ratelimit.in.yaml
@@ -110,3 +110,12 @@ backendTrafficPolicies:
limit:
requests: 20
unit: Hour
+ cost:
+ request:
+ from: Number
+ number: 1
+ response:
+ from: Metadata
+ metadata:
+ namespace: something.com
+ key: some_cost_set_by_foo
diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-ratelimit.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-ratelimit.out.yaml
index 07fa997e109..a7eb921e023 100644
--- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-ratelimit.out.yaml
+++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-ratelimit.out.yaml
@@ -13,6 +13,15 @@ backendTrafficPolicies:
- sourceCIDR:
type: Distinct
value: 192.168.0.0/16
+ cost:
+ request:
+ from: Number
+ number: 1
+ response:
+ from: Metadata
+ metadata:
+ key: some_cost_set_by_foo
+ namespace: something.com
limit:
requests: 20
unit: Hour
@@ -370,3 +379,7 @@ xdsIR:
limit:
requests: 20
unit: Hour
+ requestCost:
+ number: 1
+ responseCost:
+ format: '%DYNAMIC_METADATA(something.com:some_cost_set_by_foo)%'
diff --git a/internal/gatewayapi/testdata/clienttrafficpolicy-invalid-settings.in.yaml b/internal/gatewayapi/testdata/clienttrafficpolicy-invalid-settings.in.yaml
new file mode 100644
index 00000000000..d205efd678f
--- /dev/null
+++ b/internal/gatewayapi/testdata/clienttrafficpolicy-invalid-settings.in.yaml
@@ -0,0 +1,147 @@
+clientTrafficPolicies:
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: ClientTrafficPolicy
+ metadata:
+ namespace: default
+ name: target-gateway-1
+ spec:
+ targetRef:
+ group: gateway.networking.k8s.io
+ kind: Gateway
+ name: gateway-1
+ headers:
+ xForwardedClientCert:
+ mode: Sanitize
+ certDetailsToAdd:
+ - Cert
+ tls:
+ clientValidation:
+ caCertificateRefs:
+ - name: tls-secret-1
+ namespace: default
+gateways:
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: Gateway
+ metadata:
+ namespace: default
+ name: gateway-1
+ spec:
+ gatewayClassName: envoy-gateway-class
+ listeners:
+ - name: http-1
+ protocol: HTTPS
+ port: 443
+ allowedRoutes:
+ namespaces:
+ from: Same
+ tls:
+ mode: Terminate
+ certificateRefs:
+ - name: tls-secret-1
+ namespace: default
+ - name: http-2
+ protocol: HTTP
+ port: 8080
+ allowedRoutes:
+ namespaces:
+ from: Same
+ - name: tcp-1
+ protocol: TLS
+ port: 8443
+ allowedRoutes:
+ namespaces:
+ from: Same
+ tls:
+ mode: Terminate
+ certificateRefs:
+ - name: tls-secret-1
+ namespace: default
+ - name: tcp-2
+ protocol: TCP
+ port: 5000
+ allowedRoutes:
+ namespaces:
+ from: Same
+httpRoutes:
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ namespace: default
+ name: httproute-1
+ spec:
+ parentRefs:
+ - namespace: default
+ name: gateway-1
+ sectionName: http-1
+ rules:
+ - backendRefs:
+ - name: service-1
+ port: 8080
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ namespace: default
+ name: httproute-2
+ spec:
+ parentRefs:
+ - namespace: default
+ name: gateway-1
+ sectionName: http-1
+ rules:
+ - backendRefs:
+ - name: service-1
+ port: 8080
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ namespace: default
+ name: httproute-3
+ spec:
+ parentRefs:
+ - namespace: default
+ name: gateway-1
+ sectionName: http-2
+ rules:
+ - backendRefs:
+ - name: service-1
+ port: 8080
+tcpRoutes:
+- apiVersion: gateway.networking.k8s.io/v1alpha2
+ kind: TCPRoute
+ metadata:
+ name: tcp-route-1
+ namespace: default
+ spec:
+ parentRefs:
+ - namespace: default
+ name: gateway-1
+ sectionName: tcp-1
+ rules:
+ - backendRefs:
+ - name: service-1
+ port: 8080
+- apiVersion: gateway.networking.k8s.io/v1alpha2
+ kind: TCPRoute
+ metadata:
+ name: tcp-route-1
+ namespace: default
+ spec:
+ parentRefs:
+ - namespace: default
+ name: gateway-1
+ sectionName: tcp-2
+ rules:
+ - backendRefs:
+ - name: service-1
+ port: 8080
+secrets:
+- apiVersion: v1
+ kind: Secret
+ metadata:
+ namespace: default
+ name: tls-secret-1
+ type: kubernetes.io/tls
+ data:
+ invalid-ca.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURPekNDQWlPZ0F3SUJBZ0lVYzQxa3BFOXdLK05IZ1JHdkJJZ3c4U0Nhei84d0RRWUpLb1pJaHZjTkFRRUwKQlFBd0xURVZNQk1HQTFVRUNnd01aWGhoYlhCc1pTQkpibU11TVJRd0VnWURWUVFEREF0bGVHRnRjR3hsTG1OdgpiVEFlRncweU5EQXhNall5TXpFMU16RmFGdzB5TlRBeE1qVXlNekUxTXpGYU1DMHhGVEFUQmdOVkJBb01ER1Y0CllXMXdiR1VnU1c1akxqRVVNQklHQTFVRUF3d0xaWGhoYlhCc1pTNWpiMjB3Z2dFaU1BMEdDU3FHU0liM0RRRUIKQVFVQUE0SUJEd0F3Z2dFS0FvSUJBUURDTGhaNURuQ1ZFNUpKOTd5T29jcFJ3Y2xibDBVd1gzY0krMVpaTmx0bApXNmpSZ3kxR3VONlZyN0NCbUkvbVB0Z0dzOVQ3RE5STWw1Z0pKa05IU1pvbUk2R2p1UDFLVWh1dmxmYlpQV05vCnA0NVQyMzVaODJHZzhPUkpIVDVtbjFRUksrYno5cnVKZnlsZE1NbGljVUp2L1lmdDZ6TlVSeFE3QlU5Y2lHZTEKdE0rVU1TeGtvcDNkb3ZWcHRFTG5rVERKU3d0NWRuK25qNmovR3I5NXo5MC9lMmdqZlZUdG1BckFHM3hoLzJCMQovRDZOWGh3UE16WXJwbG5NM2xPcHh6ZmxPVmdqTVVsb0wxb0k3c202YysyQTE0TmVCclcvb2ZCOVJEN0RXQkhkCjc2aitoY0FXRnN4WW1zSG81T3gvdEZlVGs3R1Jka0hFRUxMV0ZCdllHMEJUQWdNQkFBR2pVekJSTUIwR0ExVWQKRGdRV0JCU3JMYmNRUHBFeCtEdCtoWUUveXJqdDZyT1Y2VEFmQmdOVkhTTUVHREFXZ0JTckxiY1FQcEV4K0R0KwpoWUUveXJqdDZyT1Y2VEFQQmdOVkhSTUJBZjhFQlRBREFRSC9NQTBHQ1NxR1NJYjNEUUVCQ3dVQUE0SUJBUUNGCjRqbHRxeFZhS1phVk1MN0hOUVN3ZWd5K2daMXhhbHltTU5vN0lwYzh6T3lVVUk0N3dlRWYvcCtua3E4b3hpL20KbUxab2VNU2RYZytnZWJZTU1jVVJndGw5UWZ1RjBhWUNCM0FsQ3hscGRINExrM3VYTlRMUVhKaUxRUlROc0J1LwpMSjZVWXZMRktQd29kdlJLTDhLV0tFZ0xWSm1yVGUzZzhpTDNTU253MDBoV2lldUNkU3N4TmwvNDdUaGdZWHJnCnUxUFJCVXQ1ZytYb1dwVVNPQ01PRldsQkpxd0pZS2ZSQTNFNmZmNDRJVUpzYjdxVUhIQWUxd2ExWURmdUQrVDUKQXQ5L20rTTdHeVc5b0ViU1FzUFRHZllxUDU5UUUrMWllaTZxaUcrN2tuNGlSeEpxaGdtNU41bzg2UVNrME1hegpDejRqVEVLZE52WFlWRmZoNlpxcgotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
+ tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM0RENDQWNnQ0FRQXdEUVlKS29aSWh2Y05BUUVMQlFBd0xURVZNQk1HQTFVRUNnd01aWGhoYlhCc1pTQkoKYm1NdU1SUXdFZ1lEVlFRRERBdGxlR0Z0Y0d4bExtTnZiVEFlRncweU5EQXhNall5TXpFMU16RmFGdzB5TlRBeApNalV5TXpFMU16RmFNRDh4R1RBWEJnTlZCQU1NRUdWa1oyVXVaWGhoYlhCc1pTNWpiMjB4SWpBZ0JnTlZCQW9NCkdXVmtaMlVnWlhoaGJYQnNaU0J2Y21kaGJtbDZZWFJwYjI0d2dnRWlNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUIKRHdBd2dnRUtBb0lCQVFDNzdodkFQQWVGUm5xL3RwR1ZkSk5lY2Fhaks2a1F5Q2pZNXIvcFh4TkJhOXZXVlFIVQpuQ1dWT3lscEVkaDZPZlltR2dvSmF0TVRUWUFYK1ZpdkxTOVhwSDhuUENZYVpvQmRpMlA0MWRrbmsyUnpGWm1sCi9YUjVIWnREWmplRE8zd3ZCSm9ubStNeFA3Qms1TGdlOWhGSFJ3akViTGNZN3crN3pxOEJEQXlJSHY3T0ozYTcKeDkvalgyUlpGdTdPNXI1eWZFUTZGc0tjelREb29DeGRVa05JZ3RwVkNvRExLdmJMNjFuZE51bGUzL21EbS92MgpTeVRIdWQxUzVkcXNwOGtKZHU4WFVSZmMyWUVsRktXdkJ0bmpoL2pyTE1YRmNhaFYvb3hKckNIdXQvUENMYkJUCkFqVGV1U0RhdVM3T0hiSlJES3dtSDdvVnZ5SUNhSXlhWWNaTkFnTUJBQUV3RFFZSktvWklodmNOQVFFTEJRQUQKZ2dFQkFHeW5yNGNPMWFZbjRNQk90aVJ2WHFJdllHNnpxZXNrNGpQbU96TjdiUTdyRzdNUngzSVQ2SW4zVFI4RApHbFAxVE54TTg5cXZRcXp4VERsdER3bXluTlV1SEdEUW4yV1Z1OFEyK0RqRnFoc3B1WHp0NnhVK2RoVVBxUnV1Ckt6c1l4TDNpMVlWZ2pDQWtBUmp4SGhMWHYwdkFUWUVRMlJ6Uko5c2ZGcWVCMHVxSk5WL0lHamJFSzQ2eTQ5QU0KNzU4TUY4T0R6cVR2Q3hMRjJYd3BScjdjSDFuZ2J4eUJ6cEdlbkpsVTI2Q2hJT1BMZUV1NTUyUVJYVGwrU2JlQQpXUzNpS01Pb3F5NGV0b0ExNWFueW43Zm01YnpINEcyZ3Yxd1pWYlBkT1dNQWRZU2I5NDIvR09CSWUzSnIyVHo3CjRJdDRROWFERnF1aG9iOTVQMUhHQkxSQ2Y5QT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
+ tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQzc3aHZBUEFlRlJucS8KdHBHVmRKTmVjYWFqSzZrUXlDalk1ci9wWHhOQmE5dldWUUhVbkNXVk95bHBFZGg2T2ZZbUdnb0phdE1UVFlBWAorVml2TFM5WHBIOG5QQ1lhWm9CZGkyUDQxZGtuazJSekZabWwvWFI1SFp0RFpqZURPM3d2Qkpvbm0rTXhQN0JrCjVMZ2U5aEZIUndqRWJMY1k3dys3enE4QkRBeUlIdjdPSjNhN3g5L2pYMlJaRnU3TzVyNXlmRVE2RnNLY3pURG8Kb0N4ZFVrTklndHBWQ29ETEt2Ykw2MW5kTnVsZTMvbURtL3YyU3lUSHVkMVM1ZHFzcDhrSmR1OFhVUmZjMllFbApGS1d2QnRuamgvanJMTVhGY2FoVi9veEpyQ0h1dC9QQ0xiQlRBalRldVNEYXVTN09IYkpSREt3bUg3b1Z2eUlDCmFJeWFZY1pOQWdNQkFBRUNnZ0VBSG1McVd4NHZCbk9ybHFLMGVNLzM5c1lLOEVpTTlra0c5eHRJWGVSTGxCWnIKM2dTeUNSTStXRzk2ZGZkaFkxSDFPa1ZDUGpJOFNEQzRkMzA2Ymw0Ris2RW93TXFrUytjcTlrcDYzYTg3aE5TbQpOMGdxSnl3TGV5YzRXdll2ZFA2c25scnd6MXE3Vk5QbXpQUXJ6b1hIQVc2N2tpeHA1cFF3OG1oVzVQcHlidkp5Clo2TERCZGRSZkVma2ZXVnZUUk5YWUVDUEllUStST05jR3JvVzZ5RXRrbk1BWUJjdjRlNUhCQkkrcHdyYmsrOVMKY2FQYUVjdm4vS0lyT3NpVW1FT2wwb3JXVnhkbjRmMy9MNmlFZFgyZHhIdXlwYkFiL0Qwak1MSzBwb3kyaXYyTApyOGI5VUQrRVZFNmFTVnp0MFRHbVpJYUdRVVZDQnVDTDhodlYwSU9PV1FLQmdRRGplL3JXdmk4Rndia3BRNDA0CnFQcitBaEFwaG1pV3l1a1B1VmJLN2Q5ZkdURzRHOW9Bd2wzYlFoRGVUNHhjMzd0cjlkcCtpamJuWnpKWHczL1cKcm5xTDlGWkZsVXZCYXN6c05VK1lRNmJVOE9zTXl6cURSdGJaaytVWEowUEx6QzZKWHFkNTFZdVVDM3NwL2lmNwpqWEZrME55aHcrdkY3VU51N0ZFSzVuWEUwd0tCZ1FEVGZOT0RLYmZyalNkZEhkV05iOHhkN2pGMlZSY3hTTnRUCit0L0FmbkRjZG8zK1NBUnJaRi9TM0hZWUxxL0l4dmZ5ZHdIblUxdC9INkxDZjBnQ2RXS2NXL1hway93ZUo1QXYKWmdaZjBPTXZsOXF0THJhTU44OG1HblV4K2IxdHZLWm4xQVcySFNuYXd2Z0kvMWVjSldNRUJiYkREbkx4cUpMegowTHJhT2pYVVh3S0JnRGlBbE44OXdjUTJSOTFkNy9mQTBRYkNVRzFmK3g1cEs5WkIvTExPdm9xS1lYVVBSZWltClhsV1ZaVWN5anZTS2hhemRGZllVTW1ycmtPK0htWHNqUDBELzRXWExIVlBmU1NMcVl1aTQ5UGt6RmM3SnM3RGoKcVgzRlpFT0o5eWJwZ2kyUW14eUIwL2RqbXFYbGdOelVWdlBwaE1PUlBFQ2ZHLzZ6SjdZRFpBRU5Bb0dBSElVcQo2UGRKVEVTKzJEbmJ3TFVnOUZIWTdjSlAzRitjNUZoaXNFemMzMzVGYTlNK2RWVVY3eE80QVU3YWVkTUxRUEYzCm1rQ05pRGsxODlEQ1gwS0JSK0RHNnZiLyt2a080clY1aXBaYTdPSW5wVTgxWXZkcndoR3pXRWY3bWI3bEdmOW4KdmNWMURZRlpmYTBoblhjVlFVZWIrL1lJM2pvRGgwblF5UGtzcFRVQ2dZRUF0NERNajdZbStRS2J2bTJXaWNlcAo1Q2s3YWFMSUxuVHZqbGRLMkdjM2loOGVGRlE2Vy9pcUc1UUEzeHMwem8xVnhlUkhPWGkrK01xWjVWTVZMZFRWCjMxWXZOeUdPbVByTitZemVINmlTYXd5VXo2dW1UN1ZkMXRuUEJ1SmdPMFM3RnRlb01BckE3TGtDcUVhMDc4bS8KRXNxNzZjYW1WdW5kRXFTRWhGMllYNkU9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K
diff --git a/internal/gatewayapi/testdata/clienttrafficpolicy-invalid-settings.out.yaml b/internal/gatewayapi/testdata/clienttrafficpolicy-invalid-settings.out.yaml
new file mode 100644
index 00000000000..37f77d731a1
--- /dev/null
+++ b/internal/gatewayapi/testdata/clienttrafficpolicy-invalid-settings.out.yaml
@@ -0,0 +1,512 @@
+clientTrafficPolicies:
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: ClientTrafficPolicy
+ metadata:
+ creationTimestamp: null
+ name: target-gateway-1
+ namespace: default
+ spec:
+ headers:
+ xForwardedClientCert:
+ certDetailsToAdd:
+ - Cert
+ mode: Sanitize
+ targetRef:
+ group: gateway.networking.k8s.io
+ kind: Gateway
+ name: gateway-1
+ tls:
+ clientValidation:
+ caCertificateRefs:
+ - group: null
+ kind: null
+ name: tls-secret-1
+ namespace: default
+ status:
+ ancestors:
+ - ancestorRef:
+ group: gateway.networking.k8s.io
+ kind: Gateway
+ name: gateway-1
+ namespace: default
+ conditions:
+ - lastTransitionTime: null
+ message: |-
+ TLS: caCertificateRef not found in secret tls-secret-1
+ TLS: caCertificateRef not found in secret tls-secret-1.
+ reason: Invalid
+ status: "False"
+ type: Accepted
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
+gateways:
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: Gateway
+ metadata:
+ creationTimestamp: null
+ name: gateway-1
+ namespace: default
+ spec:
+ gatewayClassName: envoy-gateway-class
+ listeners:
+ - allowedRoutes:
+ namespaces:
+ from: Same
+ name: http-1
+ port: 443
+ protocol: HTTPS
+ tls:
+ certificateRefs:
+ - group: null
+ kind: null
+ name: tls-secret-1
+ namespace: default
+ mode: Terminate
+ - allowedRoutes:
+ namespaces:
+ from: Same
+ name: http-2
+ port: 8080
+ protocol: HTTP
+ - allowedRoutes:
+ namespaces:
+ from: Same
+ name: tcp-1
+ port: 8443
+ protocol: TLS
+ tls:
+ certificateRefs:
+ - group: null
+ kind: null
+ name: tls-secret-1
+ namespace: default
+ mode: Terminate
+ - allowedRoutes:
+ namespaces:
+ from: Same
+ name: tcp-2
+ port: 5000
+ protocol: TCP
+ status:
+ listeners:
+ - attachedRoutes: 2
+ 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-1
+ supportedKinds:
+ - group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ - group: gateway.networking.k8s.io
+ kind: GRPCRoute
+ - 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-2
+ supportedKinds:
+ - group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ - group: gateway.networking.k8s.io
+ kind: GRPCRoute
+ - 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: tcp-1
+ supportedKinds:
+ - group: gateway.networking.k8s.io
+ kind: TCPRoute
+ - 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: tcp-2
+ supportedKinds:
+ - group: gateway.networking.k8s.io
+ kind: TCPRoute
+httpRoutes:
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ creationTimestamp: null
+ name: httproute-1
+ namespace: default
+ spec:
+ parentRefs:
+ - name: gateway-1
+ namespace: default
+ sectionName: http-1
+ rules:
+ - backendRefs:
+ - name: service-1
+ port: 8080
+ 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: default
+ sectionName: http-1
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ creationTimestamp: null
+ name: httproute-2
+ namespace: default
+ spec:
+ parentRefs:
+ - name: gateway-1
+ namespace: default
+ sectionName: http-1
+ rules:
+ - backendRefs:
+ - name: service-1
+ port: 8080
+ 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: default
+ sectionName: http-1
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ creationTimestamp: null
+ name: httproute-3
+ namespace: default
+ spec:
+ parentRefs:
+ - name: gateway-1
+ namespace: default
+ sectionName: http-2
+ rules:
+ - backendRefs:
+ - name: service-1
+ port: 8080
+ 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: default
+ sectionName: http-2
+infraIR:
+ default/gateway-1:
+ proxy:
+ listeners:
+ - address: null
+ name: default/gateway-1/http-1
+ ports:
+ - containerPort: 10443
+ name: https-443
+ protocol: HTTPS
+ servicePort: 443
+ - address: null
+ name: default/gateway-1/http-2
+ ports:
+ - containerPort: 8080
+ name: http-8080
+ protocol: HTTP
+ servicePort: 8080
+ - address: null
+ name: default/gateway-1/tcp-1
+ ports:
+ - containerPort: 8443
+ name: tls-8443
+ protocol: TLS
+ servicePort: 8443
+ - address: null
+ name: default/gateway-1/tcp-2
+ ports:
+ - containerPort: 5000
+ name: tcp-5000
+ protocol: TCP
+ servicePort: 5000
+ metadata:
+ labels:
+ gateway.envoyproxy.io/owning-gateway-name: gateway-1
+ gateway.envoyproxy.io/owning-gateway-namespace: default
+ name: default/gateway-1
+tcpRoutes:
+- apiVersion: gateway.networking.k8s.io/v1alpha2
+ kind: TCPRoute
+ metadata:
+ creationTimestamp: null
+ name: tcp-route-1
+ namespace: default
+ spec:
+ parentRefs:
+ - name: gateway-1
+ namespace: default
+ sectionName: tcp-1
+ rules:
+ - backendRefs:
+ - name: service-1
+ port: 8080
+ 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: default
+ sectionName: tcp-1
+- apiVersion: gateway.networking.k8s.io/v1alpha2
+ kind: TCPRoute
+ metadata:
+ creationTimestamp: null
+ name: tcp-route-1
+ namespace: default
+ spec:
+ parentRefs:
+ - name: gateway-1
+ namespace: default
+ sectionName: tcp-2
+ rules:
+ - backendRefs:
+ - name: service-1
+ port: 8080
+ 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: default
+ sectionName: tcp-2
+xdsIR:
+ default/gateway-1:
+ accessLog:
+ text:
+ - path: /dev/stdout
+ http:
+ - address: 0.0.0.0
+ headers:
+ withUnderscoresAction: RejectRequest
+ xForwardedClientCert:
+ mode: Sanitize
+ hostnames:
+ - '*'
+ isHTTP2: false
+ metadata:
+ kind: Gateway
+ name: gateway-1
+ namespace: default
+ sectionName: http-1
+ name: default/gateway-1/http-1
+ path:
+ escapedSlashesAction: UnescapeAndRedirect
+ mergeSlashes: true
+ port: 10443
+ routes:
+ - destination:
+ name: httproute/default/httproute-1/rule/0
+ settings:
+ - addressType: IP
+ endpoints:
+ - host: 7.7.7.7
+ port: 8080
+ protocol: HTTP
+ weight: 1
+ directResponse:
+ statusCode: 500
+ hostname: '*'
+ isHTTP2: false
+ metadata:
+ kind: HTTPRoute
+ name: httproute-1
+ namespace: default
+ name: httproute/default/httproute-1/rule/0/match/-1/*
+ - destination:
+ name: httproute/default/httproute-2/rule/0
+ settings:
+ - addressType: IP
+ endpoints:
+ - host: 7.7.7.7
+ port: 8080
+ protocol: HTTP
+ weight: 1
+ directResponse:
+ statusCode: 500
+ hostname: '*'
+ isHTTP2: false
+ metadata:
+ kind: HTTPRoute
+ name: httproute-2
+ namespace: default
+ name: httproute/default/httproute-2/rule/0/match/-1/*
+ tls:
+ alpnProtocols: null
+ certificates:
+ - name: default/tls-secret-1
+ privateKey: '[redacted]'
+ serverCertificate: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM0RENDQWNnQ0FRQXdEUVlKS29aSWh2Y05BUUVMQlFBd0xURVZNQk1HQTFVRUNnd01aWGhoYlhCc1pTQkoKYm1NdU1SUXdFZ1lEVlFRRERBdGxlR0Z0Y0d4bExtTnZiVEFlRncweU5EQXhNall5TXpFMU16RmFGdzB5TlRBeApNalV5TXpFMU16RmFNRDh4R1RBWEJnTlZCQU1NRUdWa1oyVXVaWGhoYlhCc1pTNWpiMjB4SWpBZ0JnTlZCQW9NCkdXVmtaMlVnWlhoaGJYQnNaU0J2Y21kaGJtbDZZWFJwYjI0d2dnRWlNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUIKRHdBd2dnRUtBb0lCQVFDNzdodkFQQWVGUm5xL3RwR1ZkSk5lY2Fhaks2a1F5Q2pZNXIvcFh4TkJhOXZXVlFIVQpuQ1dWT3lscEVkaDZPZlltR2dvSmF0TVRUWUFYK1ZpdkxTOVhwSDhuUENZYVpvQmRpMlA0MWRrbmsyUnpGWm1sCi9YUjVIWnREWmplRE8zd3ZCSm9ubStNeFA3Qms1TGdlOWhGSFJ3akViTGNZN3crN3pxOEJEQXlJSHY3T0ozYTcKeDkvalgyUlpGdTdPNXI1eWZFUTZGc0tjelREb29DeGRVa05JZ3RwVkNvRExLdmJMNjFuZE51bGUzL21EbS92MgpTeVRIdWQxUzVkcXNwOGtKZHU4WFVSZmMyWUVsRktXdkJ0bmpoL2pyTE1YRmNhaFYvb3hKckNIdXQvUENMYkJUCkFqVGV1U0RhdVM3T0hiSlJES3dtSDdvVnZ5SUNhSXlhWWNaTkFnTUJBQUV3RFFZSktvWklodmNOQVFFTEJRQUQKZ2dFQkFHeW5yNGNPMWFZbjRNQk90aVJ2WHFJdllHNnpxZXNrNGpQbU96TjdiUTdyRzdNUngzSVQ2SW4zVFI4RApHbFAxVE54TTg5cXZRcXp4VERsdER3bXluTlV1SEdEUW4yV1Z1OFEyK0RqRnFoc3B1WHp0NnhVK2RoVVBxUnV1Ckt6c1l4TDNpMVlWZ2pDQWtBUmp4SGhMWHYwdkFUWUVRMlJ6Uko5c2ZGcWVCMHVxSk5WL0lHamJFSzQ2eTQ5QU0KNzU4TUY4T0R6cVR2Q3hMRjJYd3BScjdjSDFuZ2J4eUJ6cEdlbkpsVTI2Q2hJT1BMZUV1NTUyUVJYVGwrU2JlQQpXUzNpS01Pb3F5NGV0b0ExNWFueW43Zm01YnpINEcyZ3Yxd1pWYlBkT1dNQWRZU2I5NDIvR09CSWUzSnIyVHo3CjRJdDRROWFERnF1aG9iOTVQMUhHQkxSQ2Y5QT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
+ maxVersion: "1.3"
+ minVersion: "1.2"
+ - address: 0.0.0.0
+ headers:
+ withUnderscoresAction: RejectRequest
+ xForwardedClientCert:
+ mode: Sanitize
+ hostnames:
+ - '*'
+ isHTTP2: false
+ metadata:
+ kind: Gateway
+ name: gateway-1
+ namespace: default
+ sectionName: http-2
+ name: default/gateway-1/http-2
+ path:
+ escapedSlashesAction: UnescapeAndRedirect
+ mergeSlashes: true
+ port: 8080
+ routes:
+ - destination:
+ name: httproute/default/httproute-3/rule/0
+ settings:
+ - addressType: IP
+ endpoints:
+ - host: 7.7.7.7
+ port: 8080
+ protocol: HTTP
+ weight: 1
+ hostname: '*'
+ isHTTP2: false
+ metadata:
+ kind: HTTPRoute
+ name: httproute-3
+ namespace: default
+ name: httproute/default/httproute-3/rule/0/match/-1/*
+ tcp:
+ - address: 0.0.0.0
+ name: default/gateway-1/tcp-1
+ port: 8443
+ tls:
+ alpnProtocols: []
+ certificates:
+ - name: default/tls-secret-1
+ privateKey: '[redacted]'
+ serverCertificate: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM0RENDQWNnQ0FRQXdEUVlKS29aSWh2Y05BUUVMQlFBd0xURVZNQk1HQTFVRUNnd01aWGhoYlhCc1pTQkoKYm1NdU1SUXdFZ1lEVlFRRERBdGxlR0Z0Y0d4bExtTnZiVEFlRncweU5EQXhNall5TXpFMU16RmFGdzB5TlRBeApNalV5TXpFMU16RmFNRDh4R1RBWEJnTlZCQU1NRUdWa1oyVXVaWGhoYlhCc1pTNWpiMjB4SWpBZ0JnTlZCQW9NCkdXVmtaMlVnWlhoaGJYQnNaU0J2Y21kaGJtbDZZWFJwYjI0d2dnRWlNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUIKRHdBd2dnRUtBb0lCQVFDNzdodkFQQWVGUm5xL3RwR1ZkSk5lY2Fhaks2a1F5Q2pZNXIvcFh4TkJhOXZXVlFIVQpuQ1dWT3lscEVkaDZPZlltR2dvSmF0TVRUWUFYK1ZpdkxTOVhwSDhuUENZYVpvQmRpMlA0MWRrbmsyUnpGWm1sCi9YUjVIWnREWmplRE8zd3ZCSm9ubStNeFA3Qms1TGdlOWhGSFJ3akViTGNZN3crN3pxOEJEQXlJSHY3T0ozYTcKeDkvalgyUlpGdTdPNXI1eWZFUTZGc0tjelREb29DeGRVa05JZ3RwVkNvRExLdmJMNjFuZE51bGUzL21EbS92MgpTeVRIdWQxUzVkcXNwOGtKZHU4WFVSZmMyWUVsRktXdkJ0bmpoL2pyTE1YRmNhaFYvb3hKckNIdXQvUENMYkJUCkFqVGV1U0RhdVM3T0hiSlJES3dtSDdvVnZ5SUNhSXlhWWNaTkFnTUJBQUV3RFFZSktvWklodmNOQVFFTEJRQUQKZ2dFQkFHeW5yNGNPMWFZbjRNQk90aVJ2WHFJdllHNnpxZXNrNGpQbU96TjdiUTdyRzdNUngzSVQ2SW4zVFI4RApHbFAxVE54TTg5cXZRcXp4VERsdER3bXluTlV1SEdEUW4yV1Z1OFEyK0RqRnFoc3B1WHp0NnhVK2RoVVBxUnV1Ckt6c1l4TDNpMVlWZ2pDQWtBUmp4SGhMWHYwdkFUWUVRMlJ6Uko5c2ZGcWVCMHVxSk5WL0lHamJFSzQ2eTQ5QU0KNzU4TUY4T0R6cVR2Q3hMRjJYd3BScjdjSDFuZ2J4eUJ6cEdlbkpsVTI2Q2hJT1BMZUV1NTUyUVJYVGwrU2JlQQpXUzNpS01Pb3F5NGV0b0ExNWFueW43Zm01YnpINEcyZ3Yxd1pWYlBkT1dNQWRZU2I5NDIvR09CSWUzSnIyVHo3CjRJdDRROWFERnF1aG9iOTVQMUhHQkxSQ2Y5QT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
+ maxVersion: "1.3"
+ minVersion: "1.2"
+ - address: 0.0.0.0
+ name: default/gateway-1/tcp-2
+ port: 5000
+ routes:
+ - destination:
+ name: tcproute/default/tcp-route-1/rule/-1
+ settings:
+ - addressType: IP
+ endpoints:
+ - host: 7.7.7.7
+ port: 8080
+ protocol: TCP
+ weight: 1
+ name: tcproute/default/tcp-route-1
diff --git a/internal/gatewayapi/testdata/grpcroute-with-backend.out.yaml b/internal/gatewayapi/testdata/grpcroute-with-backend.out.yaml
index 1d7cb30742e..1f7f559aaf0 100644
--- a/internal/gatewayapi/testdata/grpcroute-with-backend.out.yaml
+++ b/internal/gatewayapi/testdata/grpcroute-with-backend.out.yaml
@@ -92,10 +92,9 @@ grpcRoutes:
status: "True"
type: Accepted
- lastTransitionTime: null
- message: Resource default/backend-ip of type Backend is not supported for
- GRPCRoute routes
- reason: UnsupportedValue
- status: "False"
+ message: Resolved all the Object references for the Route
+ reason: ResolvedRefs
+ status: "True"
type: ResolvedRefs
controllerName: gateway.envoyproxy.io/gatewayclass-controller
parentRef:
@@ -139,8 +138,15 @@ xdsIR:
mergeSlashes: true
port: 10080
routes:
- - directResponse:
- statusCode: 500
+ - destination:
+ name: grpcroute/default/grpcroute-1/rule/0
+ settings:
+ - addressType: IP
+ endpoints:
+ - host: 1.1.1.1
+ port: 3001
+ protocol: GRPC
+ weight: 1
hostname: '*'
isHTTP2: true
metadata:
@@ -153,8 +159,15 @@ xdsIR:
distinct: false
name: ""
safeRegex: /com.[A-Z]+/[A-Za-z_][A-Za-z_0-9]*
- - directResponse:
- statusCode: 500
+ - destination:
+ name: grpcroute/default/grpcroute-1/rule/0
+ settings:
+ - addressType: IP
+ endpoints:
+ - host: 1.1.1.1
+ port: 3001
+ protocol: GRPC
+ weight: 1
hostname: '*'
isHTTP2: true
metadata:
diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-backend-and-core-backendrefs.out.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-backend-and-core-backendrefs.out.yaml
index 484fe119154..e7ec462624c 100644
--- a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-backend-and-core-backendrefs.out.yaml
+++ b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-backend-and-core-backendrefs.out.yaml
@@ -224,6 +224,7 @@ xdsIR:
endpoints:
- host: 1.1.1.1
port: 3001
+ protocol: HTTP
weight: 1
- addressType: IP
endpoints:
@@ -255,6 +256,7 @@ xdsIR:
endpoints:
- host: primary.foo.com
port: 3000
+ protocol: HTTP
weight: 1
- addressType: FQDN
endpoints:
diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-backend-backendref.out.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-backend-backendref.out.yaml
index c252ac4d77f..709c48725c5 100644
--- a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-backend-backendref.out.yaml
+++ b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-backend-backendref.out.yaml
@@ -377,6 +377,7 @@ xdsIR:
endpoints:
- host: primary.foo.com
port: 3000
+ protocol: HTTP
weight: 1
hostname: '*'
isHTTP2: false
@@ -396,6 +397,7 @@ xdsIR:
endpoints:
- host: 1.1.1.1
port: 3001
+ protocol: HTTP
weight: 1
hostname: '*'
isHTTP2: false
diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-backend-backendrefs-diff-address-type.out.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-backend-backendrefs-diff-address-type.out.yaml
index 86255af66ce..deefbd7878e 100644
--- a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-backend-backendrefs-diff-address-type.out.yaml
+++ b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-backend-backendrefs-diff-address-type.out.yaml
@@ -329,11 +329,13 @@ xdsIR:
endpoints:
- host: 1.1.1.1
port: 3001
+ protocol: HTTP
weight: 1
- addressType: FQDN
endpoints:
- host: primary.foo.com
port: 3000
+ protocol: HTTP
weight: 1
hostname: '*'
isHTTP2: false
diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-backend-backendrefs-same-address-type.out.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-backend-backendrefs-same-address-type.out.yaml
index c16b8a064ca..a230922d110 100644
--- a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-backend-backendrefs-same-address-type.out.yaml
+++ b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-backend-backendrefs-same-address-type.out.yaml
@@ -325,11 +325,13 @@ xdsIR:
endpoints:
- host: 1.1.1.1
port: 3001
+ protocol: HTTP
weight: 1
- addressType: IP
endpoints:
- host: 2.2.2.2
port: 3001
+ protocol: HTTP
weight: 1
hostname: '*'
isHTTP2: false
@@ -349,11 +351,13 @@ xdsIR:
endpoints:
- host: primary.foo.com
port: 3000
+ protocol: HTTP
weight: 1
- addressType: FQDN
endpoints:
- host: primary2.foo.com
port: 3000
+ protocol: HTTP
weight: 1
hostname: '*'
isHTTP2: false
diff --git a/internal/gatewayapi/testdata/httproute-rule-with-non-service-backends-and-weights.out.yaml b/internal/gatewayapi/testdata/httproute-rule-with-non-service-backends-and-weights.out.yaml
index d56407b0dd9..ee0ea6d639a 100644
--- a/internal/gatewayapi/testdata/httproute-rule-with-non-service-backends-and-weights.out.yaml
+++ b/internal/gatewayapi/testdata/httproute-rule-with-non-service-backends-and-weights.out.yaml
@@ -201,6 +201,7 @@ xdsIR:
endpoints:
- host: 10.244.0.28
port: 3000
+ protocol: HTTP
weight: 2
hostname: '*'
isHTTP2: false
@@ -220,6 +221,7 @@ xdsIR:
endpoints:
- host: primary.foo.com
port: 3000
+ protocol: HTTP
weight: 1
hostname: '*'
isHTTP2: false
diff --git a/internal/gatewayapi/testdata/tcproute-with-backend.out.yaml b/internal/gatewayapi/testdata/tcproute-with-backend.out.yaml
index 951d4c7529c..0565f5e2fc9 100644
--- a/internal/gatewayapi/testdata/tcproute-with-backend.out.yaml
+++ b/internal/gatewayapi/testdata/tcproute-with-backend.out.yaml
@@ -92,16 +92,14 @@ tcpRoutes:
parents:
- conditions:
- lastTransitionTime: null
- message: 'backend reference validation failed: backend is not supported for
- route kind: TCPRoute'
- reason: Failed to process the settings associated with the TCP route.
- status: "False"
+ message: Route is accepted
+ reason: Accepted
+ status: "True"
type: Accepted
- lastTransitionTime: null
- message: Resource default/backend-ip of type Backend is not supported for
- TCPRoute routes
- reason: UnsupportedValue
- status: "False"
+ message: Resolved all the Object references for the Route
+ reason: ResolvedRefs
+ status: "True"
type: ResolvedRefs
controllerName: gateway.envoyproxy.io/gatewayclass-controller
parentRef:
@@ -116,3 +114,14 @@ xdsIR:
- address: 0.0.0.0
name: envoy-gateway/gateway-1/tcp
port: 10090
+ routes:
+ - destination:
+ name: tcproute/default/tcproute-1/rule/-1
+ settings:
+ - addressType: IP
+ endpoints:
+ - host: 1.1.1.1
+ port: 3001
+ protocol: TCP
+ weight: 1
+ name: tcproute/default/tcproute-1
diff --git a/internal/gatewayapi/testdata/tlsroute-with-backend.out.yaml b/internal/gatewayapi/testdata/tlsroute-with-backend.out.yaml
index 97bce6d0acf..d5ddd669db7 100644
--- a/internal/gatewayapi/testdata/tlsroute-with-backend.out.yaml
+++ b/internal/gatewayapi/testdata/tlsroute-with-backend.out.yaml
@@ -125,6 +125,7 @@ xdsIR:
endpoints:
- host: 1.1.1.1
port: 3001
+ protocol: HTTPS
weight: 1
name: tlsroute/default/tlsroute-1
tls:
diff --git a/internal/gatewayapi/testdata/udproute-with-backend.in.yaml b/internal/gatewayapi/testdata/udproute-with-backend.in.yaml
new file mode 100644
index 00000000000..e23ec3beaa3
--- /dev/null
+++ b/internal/gatewayapi/testdata/udproute-with-backend.in.yaml
@@ -0,0 +1,41 @@
+gateways:
+ - apiVersion: gateway.networking.k8s.io/v1
+ kind: Gateway
+ metadata:
+ namespace: envoy-gateway
+ name: gateway-1
+ spec:
+ gatewayClassName: envoy-gateway-class
+ listeners:
+ - name: udp
+ protocol: UDP
+ port: 90
+ allowedRoutes:
+ namespaces:
+ from: All
+udpRoutes:
+ - apiVersion: gateway.networking.k8s.io/v1alpha2
+ kind: UDPRoute
+ metadata:
+ namespace: default
+ name: udproute-1
+ spec:
+ parentRefs:
+ - namespace: envoy-gateway
+ name: gateway-1
+ rules:
+ - backendRefs:
+ - group: gateway.envoyproxy.io
+ kind: Backend
+ name: backend-ip
+backends:
+ - apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: Backend
+ metadata:
+ name: backend-ip
+ namespace: default
+ spec:
+ endpoints:
+ - ip:
+ address: 1.1.1.1
+ port: 3001
diff --git a/internal/gatewayapi/testdata/udproute-with-backend.out.yaml b/internal/gatewayapi/testdata/udproute-with-backend.out.yaml
new file mode 100644
index 00000000000..108d3949bb6
--- /dev/null
+++ b/internal/gatewayapi/testdata/udproute-with-backend.out.yaml
@@ -0,0 +1,127 @@
+backends:
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: Backend
+ metadata:
+ creationTimestamp: null
+ name: backend-ip
+ namespace: default
+ spec:
+ endpoints:
+ - ip:
+ address: 1.1.1.1
+ port: 3001
+ status:
+ conditions:
+ - lastTransitionTime: null
+ message: The Backend was accepted
+ reason: Accepted
+ status: "True"
+ type: Accepted
+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: udp
+ port: 90
+ protocol: UDP
+ 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: udp
+ supportedKinds:
+ - group: gateway.networking.k8s.io
+ kind: UDPRoute
+infraIR:
+ envoy-gateway/gateway-1:
+ proxy:
+ listeners:
+ - address: null
+ name: envoy-gateway/gateway-1/udp
+ ports:
+ - containerPort: 10090
+ name: udp-90
+ protocol: UDP
+ servicePort: 90
+ metadata:
+ labels:
+ gateway.envoyproxy.io/owning-gateway-name: gateway-1
+ gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway
+ name: envoy-gateway/gateway-1
+udpRoutes:
+- apiVersion: gateway.networking.k8s.io/v1alpha2
+ kind: UDPRoute
+ metadata:
+ creationTimestamp: null
+ name: udproute-1
+ namespace: default
+ spec:
+ parentRefs:
+ - name: gateway-1
+ namespace: envoy-gateway
+ rules:
+ - backendRefs:
+ - group: gateway.envoyproxy.io
+ kind: Backend
+ name: backend-ip
+ 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
+xdsIR:
+ envoy-gateway/gateway-1:
+ accessLog:
+ text:
+ - path: /dev/stdout
+ udp:
+ - address: 0.0.0.0
+ name: envoy-gateway/gateway-1/udp
+ port: 10090
+ route:
+ destination:
+ name: udproute/default/udproute-1/rule/-1
+ settings:
+ - addressType: IP
+ endpoints:
+ - host: 1.1.1.1
+ port: 3001
+ protocol: UDP
+ weight: 1
+ name: udproute/default/udproute-1
diff --git a/internal/gatewayapi/validate.go b/internal/gatewayapi/validate.go
index 37a007444e2..3ca97bec9bc 100644
--- a/internal/gatewayapi/validate.go
+++ b/internal/gatewayapi/validate.go
@@ -70,7 +70,7 @@ func (t *Translator) validateBackendRef(backendRefContext BackendRefContext, par
return fmt.Errorf("backend service import validation failed: %w", err)
}
case egv1a1.KindBackend:
- if err := t.validateBackendRefBackend(backendRef, parentRef, resources, backendNamespace, route, routeKind); err != nil {
+ if err := t.validateBackendRefBackend(backendRef, parentRef, resources, backendNamespace, route); err != nil {
return fmt.Errorf("backend reference validation failed: %w", err)
}
}
@@ -273,7 +273,7 @@ func (t *Translator) validateBackendServiceImport(backendRef *gwapiv1a2.BackendR
}
func (t *Translator) validateBackendRefBackend(backendRef *gwapiv1a2.BackendRef, parentRef *RouteParentContext, resources *resource.Resources,
- backendNamespace string, route RouteContext, kind gwapiv1.Kind,
+ backendNamespace string, route RouteContext,
) error {
routeStatus := GetRouteStatus(route)
@@ -290,19 +290,6 @@ func (t *Translator) validateBackendRefBackend(backendRef *gwapiv1a2.BackendRef,
return errors.New("backend is disabled in Envoy Gateway configuration")
}
- if kind != resource.KindHTTPRoute && kind != resource.KindTLSRoute {
- status.SetRouteStatusCondition(routeStatus,
- parentRef.routeParentStatusIdx,
- route.GetGeneration(),
- gwapiv1.RouteConditionResolvedRefs,
- metav1.ConditionFalse,
- gwapiv1.RouteReasonUnsupportedValue,
- fmt.Sprintf("Resource %s/%s of type Backend is not supported for %s routes", NamespaceDerefOr(backendRef.Namespace, route.GetNamespace()),
- string(backendRef.Name), kind),
- )
- return fmt.Errorf("backend is not supported for route kind: %s", kind)
- }
-
backend := resources.GetBackend(backendNamespace, string(backendRef.Name))
if backend == nil {
status.SetRouteStatusCondition(routeStatus,
diff --git a/internal/ir/xds.go b/internal/ir/xds.go
index 9d8bad00042..821e6b608b3 100644
--- a/internal/ir/xds.go
+++ b/internal/ir/xds.go
@@ -1946,6 +1946,17 @@ type RateLimitRule struct {
CIDRMatch *CIDRMatch `json:"cidrMatch,omitempty" yaml:"cidrMatch,omitempty"`
// Limit holds the rate limit values.
Limit RateLimitValue `json:"limit,omitempty" yaml:"limit,omitempty"`
+ // RequestCost specifies the cost of the request.
+ RequestCost *RateLimitCost `json:"requestCost,omitempty" yaml:"requestCost,omitempty"`
+ // ResponseCost specifies the cost of the response.
+ ResponseCost *RateLimitCost `json:"responseCost,omitempty" yaml:"responseCost,omitempty"`
+}
+
+// RateLimitCost specifies the cost of the request or response.
+// +k8s:deepcopy-gen=true
+type RateLimitCost struct {
+ Number *uint64 `json:"number,omitempty" yaml:"number,omitempty"`
+ Format *string `json:"format,omitempty" yaml:"format,omitempty"`
}
type CIDRMatch struct {
diff --git a/internal/ir/zz_generated.deepcopy.go b/internal/ir/zz_generated.deepcopy.go
index 5b007124198..9f2aa016c73 100644
--- a/internal/ir/zz_generated.deepcopy.go
+++ b/internal/ir/zz_generated.deepcopy.go
@@ -2489,6 +2489,31 @@ func (in *RateLimit) DeepCopy() *RateLimit {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *RateLimitCost) DeepCopyInto(out *RateLimitCost) {
+ *out = *in
+ if in.Number != nil {
+ in, out := &in.Number, &out.Number
+ *out = new(uint64)
+ **out = **in
+ }
+ if in.Format != nil {
+ in, out := &in.Format, &out.Format
+ *out = new(string)
+ **out = **in
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RateLimitCost.
+func (in *RateLimitCost) DeepCopy() *RateLimitCost {
+ if in == nil {
+ return nil
+ }
+ out := new(RateLimitCost)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RateLimitRule) DeepCopyInto(out *RateLimitRule) {
*out = *in
@@ -2509,6 +2534,16 @@ func (in *RateLimitRule) DeepCopyInto(out *RateLimitRule) {
**out = **in
}
out.Limit = in.Limit
+ if in.RequestCost != nil {
+ in, out := &in.RequestCost, &out.RequestCost
+ *out = new(RateLimitCost)
+ (*in).DeepCopyInto(*out)
+ }
+ if in.ResponseCost != nil {
+ in, out := &in.ResponseCost, &out.ResponseCost
+ *out = new(RateLimitCost)
+ (*in).DeepCopyInto(*out)
+ }
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RateLimitRule.
diff --git a/internal/xds/translator/httpfilters.go b/internal/xds/translator/httpfilters.go
index 0095c3573dc..60b4b8c5e03 100644
--- a/internal/xds/translator/httpfilters.go
+++ b/internal/xds/translator/httpfilters.go
@@ -299,8 +299,8 @@ func patchRouteWithPerRouteConfig(
}
// RateLimit filter is handled separately because it relies on the global
- // rate limit server configuration.
- if err := patchRouteWithRateLimit(route.GetRoute(), irRoute); err != nil {
+ // rate limit server configuration if costs are not provided.
+ if err := patchRouteWithRateLimit(route, irRoute); err != nil {
return nil
}
diff --git a/internal/xds/translator/ratelimit.go b/internal/xds/translator/ratelimit.go
index eb6a1c4a2cd..1bfb4a9230e 100644
--- a/internal/xds/translator/ratelimit.go
+++ b/internal/xds/translator/ratelimit.go
@@ -7,6 +7,7 @@ package translator
import (
"bytes"
+ "fmt"
"net/url"
"strconv"
"strings"
@@ -26,6 +27,7 @@ import (
goyaml "gopkg.in/yaml.v3" // nolint: depguard
"k8s.io/utils/ptr"
+ egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1"
"github.com/envoyproxy/gateway/internal/ir"
"github.com/envoyproxy/gateway/internal/xds/types"
)
@@ -118,7 +120,7 @@ func (t *Translator) buildRateLimitFilter(irListener *ir.HTTPListener) *hcmv3.Ht
}
rateLimitFilter := &hcmv3.HttpFilter{
- Name: wellknown.HTTPRateLimit,
+ Name: egv1a1.EnvoyFilterRateLimit.String(),
ConfigType: &hcmv3.HttpFilter_TypedConfig{
TypedConfig: rateLimitFilterAny,
},
@@ -127,20 +129,60 @@ func (t *Translator) buildRateLimitFilter(irListener *ir.HTTPListener) *hcmv3.Ht
}
// patchRouteWithRateLimit builds rate limit actions and appends to the route.
-func patchRouteWithRateLimit(xdsRouteAction *routev3.RouteAction, irRoute *ir.HTTPRoute) error { //nolint:unparam
+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 {
return nil
}
-
- rateLimits := buildRouteRateLimits(irRoute.Name, irRoute.Traffic.RateLimit.Global)
+ global := irRoute.Traffic.RateLimit.Global
+ rateLimits, costSpecified := buildRouteRateLimits(irRoute.Name, global)
+ if costSpecified {
+ // PerRoute global rate limit configuration via typed_per_filter_config can have its own rate routev3.RateLimit that overrides the route level rate limits.
+ // Per-descriptor level hits_addend can only be configured there: https://github.com/envoyproxy/envoy/pull/37972
+ // vs the "legacy" core route-embedded rate limits doesn't support the feature due to the "technical debt".
+ //
+ // This branch is only reached when the response cost is specified which allows us to assume that
+ // users are using Envoy >= v1.33.0 which also supports the typed_per_filter_config.
+ //
+ // https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/http/ratelimit/v3/rate_limit.proto#extensions-filters-http-ratelimit-v3-ratelimitperroute
+ //
+ // Though this is not explicitly documented, the rate limit functionality is the same as the core route-embedded rate limits.
+ // Only code path different is in the following code which is identical for both core and typed_per_filter_config
+ // as we are not using virtual_host level rate limits except that when typed_per_filter_config is used, per-descriptor
+ // level hits_addend is correctly resolved.
+ //
+ // https://github.com/envoyproxy/envoy/blob/47f99c5aacdb582606a48c85c6c54904fd439179/source/extensions/filters/http/ratelimit/ratelimit.cc#L93-L114
+ return patchRouteWithRateLimitOnTypedFilterConfig(route, rateLimits)
+ }
xdsRouteAction.RateLimits = rateLimits
return nil
}
-func buildRouteRateLimits(descriptorPrefix string, global *ir.GlobalRateLimit) []*routev3.RateLimit {
- var rateLimits []*routev3.RateLimit
+// patchRouteWithRateLimitOnTypedFilterConfig builds rate limit actions and appends to the route via
+// the TypedPerFilterConfig field.
+func patchRouteWithRateLimitOnTypedFilterConfig(route *routev3.Route, rateLimits []*routev3.RateLimit) error { //nolint:unparam
+ filterCfg := route.TypedPerFilterConfig
+ if filterCfg == nil {
+ filterCfg = make(map[string]*anypb.Any)
+ route.TypedPerFilterConfig = filterCfg
+ }
+ if _, ok := filterCfg[egv1a1.EnvoyFilterRateLimit.String()]; ok {
+ // This should not happen since this is the only place where the filter
+ // config is added in a route.
+ return fmt.Errorf(
+ "route already contains global rate limit filter config: %s", route.Name)
+ }
+
+ g, err := anypb.New(&ratelimitfilterv3.RateLimitPerRoute{RateLimits: rateLimits})
+ if err != nil {
+ return fmt.Errorf("failed to marshal per-route ratelimit filter config: %w", err)
+ }
+ filterCfg[egv1a1.EnvoyFilterRateLimit.String()] = g
+ return nil
+}
+func buildRouteRateLimits(descriptorPrefix string, global *ir.GlobalRateLimit) (rateLimits []*routev3.RateLimit, costSpecified bool) {
// Route descriptor for each route rule action
routeDescriptor := &routev3.RateLimit_Action{
ActionSpecifier: &routev3.RateLimit_Action_GenericKey_{
@@ -260,10 +302,32 @@ func buildRouteRateLimits(descriptorPrefix string, global *ir.GlobalRateLimit) [
}
rateLimit := &routev3.RateLimit{Actions: rlActions}
+ if c := rule.RequestCost; c != nil {
+ rateLimit.HitsAddend = rateLimitCostToHitsAddend(c)
+ costSpecified = true
+ }
rateLimits = append(rateLimits, rateLimit)
+ if c := rule.ResponseCost; c != nil {
+ // To apply the cost to the response, we need to set ApplyOnStreamDone to true which is per Rule option,
+ // so we need to create a new RateLimit for the response with the option set.
+ responseRule := &routev3.RateLimit{Actions: rlActions, ApplyOnStreamDone: true}
+ responseRule.HitsAddend = rateLimitCostToHitsAddend(c)
+ rateLimits = append(rateLimits, responseRule)
+ costSpecified = true
+ }
}
+ return
+}
- return rateLimits
+func rateLimitCostToHitsAddend(c *ir.RateLimitCost) *routev3.RateLimit_HitsAddend {
+ ret := &routev3.RateLimit_HitsAddend{}
+ if c.Number != nil {
+ ret.Number = &wrapperspb.UInt64Value{Value: *c.Number}
+ }
+ if c.Format != nil {
+ ret.Format = *c.Format
+ }
+ return ret
}
// GetRateLimitServiceConfigStr returns the PB string for the rate limit service configuration.
diff --git a/internal/xds/translator/testdata/in/xds-ir/ratelimit.yaml b/internal/xds/translator/testdata/in/xds-ir/ratelimit.yaml
index 7af166fca4d..f9eb434f6d2 100644
--- a/internal/xds/translator/testdata/in/xds-ir/ratelimit.yaml
+++ b/internal/xds/translator/testdata/in/xds-ir/ratelimit.yaml
@@ -86,3 +86,69 @@ http:
- endpoints:
- host: "1.2.3.4"
port: 50000
+ - name: "req-and-resp-cost"
+ hostname: "*"
+ traffic:
+ rateLimit:
+ global:
+ rules:
+ - headerMatches:
+ - name: "x-user-id"
+ exact: "one"
+ limit:
+ requests: 5
+ unit: second
+ requestCost:
+ number: 12345
+ responseCost:
+ format: "%DYNAMIC_METADATA(something.com:key)%"
+ pathMatch:
+ exact: "foo/bar"
+ destination:
+ name: "first-route-dest"
+ settings:
+ - endpoints:
+ - host: "1.2.3.4"
+ port: 50000
+ - name: "req-cost"
+ hostname: "*"
+ traffic:
+ rateLimit:
+ global:
+ rules:
+ - limit:
+ requests: 5
+ unit: second
+ requestCost:
+ number: 12345
+ pathMatch:
+ exact: "test"
+ destination:
+ name: "third-route-dest"
+ settings:
+ - endpoints:
+ - host: "1.2.3.4"
+ port: 50000
+ - name: "res-cost"
+ hostname: "*"
+ traffic:
+ rateLimit:
+ global:
+ rules:
+ - headerMatches:
+ - name: "x-org-id"
+ exact: "admin"
+ invert: true
+ limit:
+ requests: 5
+ unit: second
+ responseCost:
+ format: "%DYNAMIC_METADATA(something.com:key)%"
+ pathMatch:
+ exact: "foo/bar/login"
+ destination:
+ name: "fourth-route-dest"
+ settings:
+ - endpoints:
+ - host: "1.2.3.4"
+ port: 50000
diff --git a/internal/xds/translator/testdata/out/xds-ir/ratelimit.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/ratelimit.routes.yaml
index e6e83bc2bfb..ee127d9994d 100644
--- a/internal/xds/translator/testdata/out/xds-ir/ratelimit.routes.yaml
+++ b/internal/xds/translator/testdata/out/xds-ir/ratelimit.routes.yaml
@@ -75,3 +75,101 @@
exact: admin
upgradeConfigs:
- upgradeType: websocket
+ - match:
+ path: foo/bar
+ name: req-and-resp-cost
+ route:
+ cluster: first-route-dest
+ upgradeConfigs:
+ - upgradeType: websocket
+ typedPerFilterConfig:
+ envoy.filters.http.ratelimit:
+ '@type': type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimitPerRoute
+ rateLimits:
+ - actions:
+ - genericKey:
+ descriptorKey: req-and-resp-cost
+ descriptorValue: req-and-resp-cost
+ - headerValueMatch:
+ descriptorKey: rule-0-match-0
+ descriptorValue: rule-0-match-0
+ expectMatch: true
+ headers:
+ - name: x-user-id
+ stringMatch:
+ exact: one
+ hitsAddend:
+ number: "12345"
+ - actions:
+ - genericKey:
+ descriptorKey: req-and-resp-cost
+ descriptorValue: req-and-resp-cost
+ - headerValueMatch:
+ descriptorKey: rule-0-match-0
+ descriptorValue: rule-0-match-0
+ expectMatch: true
+ headers:
+ - name: x-user-id
+ stringMatch:
+ exact: one
+ applyOnStreamDone: true
+ hitsAddend:
+ format: '%DYNAMIC_METADATA(something.com:key)%'
+ - match:
+ path: test
+ name: req-cost
+ route:
+ cluster: third-route-dest
+ upgradeConfigs:
+ - upgradeType: websocket
+ typedPerFilterConfig:
+ envoy.filters.http.ratelimit:
+ '@type': type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimitPerRoute
+ rateLimits:
+ - actions:
+ - genericKey:
+ descriptorKey: req-cost
+ descriptorValue: req-cost
+ - genericKey:
+ descriptorKey: rule-0-match--1
+ descriptorValue: rule-0-match--1
+ hitsAddend:
+ number: "12345"
+ - match:
+ path: foo/bar/login
+ name: res-cost
+ route:
+ cluster: fourth-route-dest
+ upgradeConfigs:
+ - upgradeType: websocket
+ typedPerFilterConfig:
+ envoy.filters.http.ratelimit:
+ '@type': type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimitPerRoute
+ rateLimits:
+ - actions:
+ - genericKey:
+ descriptorKey: res-cost
+ descriptorValue: res-cost
+ - headerValueMatch:
+ descriptorKey: rule-0-match-0
+ descriptorValue: rule-0-match-0
+ expectMatch: false
+ headers:
+ - name: x-org-id
+ stringMatch:
+ exact: admin
+ - actions:
+ - genericKey:
+ descriptorKey: res-cost
+ descriptorValue: res-cost
+ - headerValueMatch:
+ descriptorKey: rule-0-match-0
+ descriptorValue: rule-0-match-0
+ expectMatch: false
+ headers:
+ - name: x-org-id
+ stringMatch:
+ exact: admin
+ applyOnStreamDone: true
+ hitsAddend:
+ format: '%DYNAMIC_METADATA(something.com:key)%'
diff --git a/release-notes/current.yaml b/release-notes/current.yaml
index 801555af38f..0aeef6eeef5 100644
--- a/release-notes/current.yaml
+++ b/release-notes/current.yaml
@@ -8,6 +8,8 @@ breaking changes: |
Outlier detection (passive health check) is now disabled by default.
refer to https://gateway.envoyproxy.io/docs/api/extension_types/#backendtrafficpolicy for working with passive health checks.
Envoy Gateway treats errors in calls to an extension service as fail-closed by default. Any error returned from the extension server will replace the affected resource with an "Internal Server Error" immediate response. The previous behavior can be enabled by setting the `failOpen` field to `true` in the extension service configuration.
+ Envoy Gateway now return a 500 response when a ClientTrafficPolicy translation fails for HTTP/GRPC routes, and forwards
+ client traffic to an empty cluster when a ClientTrafficPolicy translation fails for TCP routes.
# Updates addressing vulnerabilities, security flaws, or compliance requirements.
security updates: |
@@ -21,9 +23,11 @@ new features: |
Added support for defining rateLimitHpa in EnvoyGateway API
Added support for preserving the user defined HTTPRoute match order in EnvoyProxy API
Added support for response compression in the BackendTrafficPolicy API
+ Added support for cost specifier in the rate limit API.
Added support for specifying dynamic metadata namespaces that External Processing services can access read from and write to in EnvoyExtensionPolicy API
Added support for API Key Authentication in the SecurityPolicy API
Added support for GEP-1731 (HTTPRoute Retries)
+ Added support for routing to Backend resources in the GRPCRoute, TCPRoute and UDPRoute APIs
bug fixes: |
Fixed the ability to overwrite control plane certs with the certgen command by using a new command arg (-o)
diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md
index d7fdcb47798..19d79a73962 100644
--- a/site/content/en/latest/api/extension_types.md
+++ b/site/content/en/latest/api/extension_types.md
@@ -3458,6 +3458,8 @@ _Appears in:_
| Field | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
+| `request` | _[RateLimitCostSpecifier](#ratelimitcostspecifier)_ | false | | Request specifies the number to reduce the rate limit counters
on the request path. If this is not specified, the default behavior
is to reduce the rate limit counters by 1.
When Envoy receives a request that matches the rule, it tries to reduce the
rate limit counters by the specified number. If the counter doesn't have
enough capacity, the request is rate limited. |
+| `response` | _[RateLimitCostSpecifier](#ratelimitcostspecifier)_ | false | | Response specifies the number to reduce the rate limit counters
after the response is sent back to the client or the request stream is closed.
The cost is used to reduce the rate limit counters for the matching requests.
Since the reduction happens after the request stream is complete, the rate limit
won't be enforced for the current request, but for the subsequent matching requests.
This is optional and if not specified, the rate limit counters are not reduced
on the response path.
Currently, this is only supported for HTTP Global Rate Limits. |
#### RateLimitCostFrom
@@ -3503,6 +3505,8 @@ _Appears in:_
| Field | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `from` | _[RateLimitCostFrom](#ratelimitcostfrom)_ | true | | From specifies where to get the rate limit cost. Currently, only "Number" and "Metadata" are supported. |
+| `number` | _integer_ | false | | Number specifies the fixed usage number to reduce the rate limit counters.
Using zero can be used to only check the rate limit counters without reducing them. |
+| `metadata` | _[RateLimitCostMetadata](#ratelimitcostmetadata)_ | false | | Refer to Kubernetes API documentation for fields of `metadata`. |
#### RateLimitDatabaseBackend
@@ -3594,6 +3598,7 @@ _Appears in:_
| --- | --- | --- | --- | --- |
| `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. |
#### RateLimitSelectCondition
diff --git a/site/content/zh/latest/api/extension_types.md b/site/content/zh/latest/api/extension_types.md
index d7fdcb47798..19d79a73962 100644
--- a/site/content/zh/latest/api/extension_types.md
+++ b/site/content/zh/latest/api/extension_types.md
@@ -3458,6 +3458,8 @@ _Appears in:_
| Field | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
+| `request` | _[RateLimitCostSpecifier](#ratelimitcostspecifier)_ | false | | Request specifies the number to reduce the rate limit counters
on the request path. If this is not specified, the default behavior
is to reduce the rate limit counters by 1.
When Envoy receives a request that matches the rule, it tries to reduce the
rate limit counters by the specified number. If the counter doesn't have
enough capacity, the request is rate limited. |
+| `response` | _[RateLimitCostSpecifier](#ratelimitcostspecifier)_ | false | | Response specifies the number to reduce the rate limit counters
after the response is sent back to the client or the request stream is closed.
The cost is used to reduce the rate limit counters for the matching requests.
Since the reduction happens after the request stream is complete, the rate limit
won't be enforced for the current request, but for the subsequent matching requests.
This is optional and if not specified, the rate limit counters are not reduced
on the response path.
Currently, this is only supported for HTTP Global Rate Limits. |
#### RateLimitCostFrom
@@ -3503,6 +3505,8 @@ _Appears in:_
| Field | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `from` | _[RateLimitCostFrom](#ratelimitcostfrom)_ | true | | From specifies where to get the rate limit cost. Currently, only "Number" and "Metadata" are supported. |
+| `number` | _integer_ | false | | Number specifies the fixed usage number to reduce the rate limit counters.
Using zero can be used to only check the rate limit counters without reducing them. |
+| `metadata` | _[RateLimitCostMetadata](#ratelimitcostmetadata)_ | false | | Refer to Kubernetes API documentation for fields of `metadata`. |
#### RateLimitDatabaseBackend
@@ -3594,6 +3598,7 @@ _Appears in:_
| --- | --- | --- | --- | --- |
| `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. |
#### RateLimitSelectCondition
diff --git a/test/e2e/testdata/authorization-client-ip-trusted-cidrs.yaml b/test/e2e/testdata/authorization-client-ip-trusted-cidrs.yaml
index 5b56ddbab27..25aa2e9f7de 100644
--- a/test/e2e/testdata/authorization-client-ip-trusted-cidrs.yaml
+++ b/test/e2e/testdata/authorization-client-ip-trusted-cidrs.yaml
@@ -45,8 +45,9 @@ spec:
clientIPDetection:
xForwardedFor:
trustedCIDRs:
- - "172.16.0.0/12"
- - "10.0.1.0/24"
+ - "172.0.0.0/8"
+ - "10.0.0.0/8"
+ - "::/0" # trust all IPv6 addresses for E2E
targetRefs:
- group: gateway.networking.k8s.io
kind: Gateway
diff --git a/test/e2e/testdata/grpcroute-to-backend-fqdn.yaml b/test/e2e/testdata/grpcroute-to-backend-fqdn.yaml
new file mode 100644
index 00000000000..b7fdcbb1bf7
--- /dev/null
+++ b/test/e2e/testdata/grpcroute-to-backend-fqdn.yaml
@@ -0,0 +1,78 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: grpc-infra-backend-v1
+ namespace: gateway-conformance-infra
+spec:
+ selector:
+ app: grpc-infra-backend-v1
+ ports:
+ - protocol: TCP
+ port: 8080
+ targetPort: 3000
+ appProtocol: kubernetes.io/h2c
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: grpc-infra-backend-v1
+ namespace: gateway-conformance-infra
+ labels:
+ app: grpc-infra-backend-v1
+spec:
+ replicas: 2
+ selector:
+ matchLabels:
+ app: grpc-infra-backend-v1
+ template:
+ metadata:
+ labels:
+ app: grpc-infra-backend-v1
+ spec:
+ containers:
+ - name: grpc-infra-backend-v1
+ image: gcr.io/k8s-staging-gateway-api/echo-basic:v20240412-v1.0.0-394-g40c666fd
+ env:
+ - name: POD_NAME
+ valueFrom:
+ fieldRef:
+ fieldPath: metadata.name
+ - name: NAMESPACE
+ valueFrom:
+ fieldRef:
+ fieldPath: metadata.namespace
+ - name: GRPC_ECHO_SERVER
+ value: "1"
+ resources:
+ requests:
+ cpu: 10m
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: GRPCRoute
+metadata:
+ name: exact-matching
+ namespace: gateway-conformance-infra
+spec:
+ parentRefs:
+ - name: same-namespace
+ rules:
+ - matches:
+ - method:
+ service: gateway_api_conformance.echo_basic.grpcecho.GrpcEcho
+ method: Echo
+ backendRefs:
+ - group: gateway.envoyproxy.io
+ kind: Backend
+ name: backend-fqdn
+ port: 8080
+---
+apiVersion: gateway.envoyproxy.io/v1alpha1
+kind: Backend
+metadata:
+ name: backend-fqdn
+ namespace: gateway-conformance-infra
+spec:
+ endpoints:
+ - fqdn:
+ hostname: grpc-infra-backend-v1.gateway-conformance-infra.svc.cluster.local
+ port: 8080
diff --git a/test/e2e/testdata/grpcroute-to-backend-ip.yaml b/test/e2e/testdata/grpcroute-to-backend-ip.yaml
new file mode 100644
index 00000000000..d9e078b8dea
--- /dev/null
+++ b/test/e2e/testdata/grpcroute-to-backend-ip.yaml
@@ -0,0 +1,67 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: grpc-infra-backend-v1
+ namespace: gateway-conformance-infra
+spec:
+ selector:
+ app: grpc-infra-backend-v1
+ ports:
+ - protocol: TCP
+ port: 8080
+ targetPort: 3000
+ appProtocol: kubernetes.io/h2c
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: grpc-infra-backend-v1
+ namespace: gateway-conformance-infra
+ labels:
+ app: grpc-infra-backend-v1
+spec:
+ replicas: 2
+ selector:
+ matchLabels:
+ app: grpc-infra-backend-v1
+ template:
+ metadata:
+ labels:
+ app: grpc-infra-backend-v1
+ spec:
+ containers:
+ - name: grpc-infra-backend-v1
+ image: gcr.io/k8s-staging-gateway-api/echo-basic:v20240412-v1.0.0-394-g40c666fd
+ env:
+ - name: POD_NAME
+ valueFrom:
+ fieldRef:
+ fieldPath: metadata.name
+ - name: NAMESPACE
+ valueFrom:
+ fieldRef:
+ fieldPath: metadata.namespace
+ - name: GRPC_ECHO_SERVER
+ value: "1"
+ resources:
+ requests:
+ cpu: 10m
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: GRPCRoute
+metadata:
+ name: exact-matching
+ namespace: gateway-conformance-infra
+spec:
+ parentRefs:
+ - name: same-namespace
+ rules:
+ - matches:
+ - method:
+ service: gateway_api_conformance.echo_basic.grpcecho.GrpcEcho
+ method: Echo
+ backendRefs:
+ - group: gateway.envoyproxy.io
+ kind: Backend
+ name: backend-ip
+ port: 8080
diff --git a/test/e2e/testdata/ratelimit-usage-ratelimit.yaml b/test/e2e/testdata/ratelimit-usage-ratelimit.yaml
new file mode 100644
index 00000000000..94bc606df05
--- /dev/null
+++ b/test/e2e/testdata/ratelimit-usage-ratelimit.yaml
@@ -0,0 +1,102 @@
+apiVersion: gateway.envoyproxy.io/v1alpha1
+kind: BackendTrafficPolicy
+metadata:
+ name: usage-rate-limit
+ namespace: gateway-conformance-infra
+spec:
+ targetRefs:
+ - group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ name: usage-rate-limit
+ rateLimit:
+ type: Global
+ global:
+ rules:
+ - 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
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ name: usage-rate-limit
+ namespace: gateway-conformance-infra
+spec:
+ parentRefs:
+ - name: same-namespace
+ rules:
+ - matches:
+ - path:
+ type: PathPrefix
+ value: /get
+ backendRefs:
+ - name: infra-backend-v1
+ port: 8080
+---
+apiVersion: gateway.envoyproxy.io/v1alpha1
+kind: EnvoyExtensionPolicy
+metadata:
+ name: usage-rate-limit
+ namespace: gateway-conformance-infra
+spec:
+ targetRefs:
+ - group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ name: usage-rate-limit
+ extProc:
+ - backendRefs:
+ - name: grpc-ext-proc
+ namespace: gateway-conformance-infra
+ port: 9002
+ metadata:
+ writableNamespaces:
+ - io.envoyproxy.gateway.e2e
+ processingMode:
+ request: {}
+ response: {}
+---
+apiVersion: gateway.envoyproxy.io/v1alpha1
+kind: SecurityPolicy
+metadata:
+ name: authorization-client-ip
+ namespace: gateway-conformance-infra
+spec:
+ targetRefs:
+ - group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ name: usage-rate-limit
+ authorization:
+ defaultAction: Allow
+---
+apiVersion: gateway.networking.k8s.io/v1alpha3
+kind: BackendTLSPolicy
+metadata:
+ name: grpc-ext-proc-btls
+ namespace: gateway-conformance-infra
+spec:
+ targetRefs:
+ - group: ''
+ kind: Service
+ name: grpc-ext-proc
+ sectionName: grpc
+ validation:
+ caCertificateRefs:
+ - name: grpc-ext-proc-ca
+ group: ''
+ kind: ConfigMap
+ hostname: grpc-ext-proc.envoygateway
diff --git a/test/e2e/testdata/tcproute-to-backend-fqdn.yaml b/test/e2e/testdata/tcproute-to-backend-fqdn.yaml
new file mode 100644
index 00000000000..e61061be203
--- /dev/null
+++ b/test/e2e/testdata/tcproute-to-backend-fqdn.yaml
@@ -0,0 +1,47 @@
+apiVersion: gateway.networking.k8s.io/v1beta1
+kind: Gateway
+metadata:
+ name: my-tcp-gateway
+ namespace: gateway-conformance-infra
+spec:
+ gatewayClassName: "{GATEWAY_CLASS_NAME}"
+ listeners:
+ - name: foo
+ protocol: TCP
+ port: 8080
+ allowedRoutes:
+ kinds:
+ - kind: TCPRoute
+ - name: bar
+ protocol: TCP
+ port: 8090
+ allowedRoutes:
+ kinds:
+ - kind: TCPRoute
+---
+apiVersion: gateway.networking.k8s.io/v1alpha2
+kind: TCPRoute
+metadata:
+ name: tcp-app-1
+ namespace: gateway-conformance-infra
+spec:
+ parentRefs:
+ - name: my-tcp-gateway
+ sectionName: foo
+ rules:
+ - backendRefs:
+ - group: gateway.envoyproxy.io
+ kind: Backend
+ name: backend-fqdn
+ port: 8080
+---
+apiVersion: gateway.envoyproxy.io/v1alpha1
+kind: Backend
+metadata:
+ name: backend-fqdn
+ namespace: gateway-conformance-infra
+spec:
+ endpoints:
+ - fqdn:
+ hostname: infra-backend-v1.gateway-conformance-infra.svc.cluster.local
+ port: 8080
diff --git a/test/e2e/testdata/tcproute-to-backend-ip.yaml b/test/e2e/testdata/tcproute-to-backend-ip.yaml
new file mode 100644
index 00000000000..5d2764a2f60
--- /dev/null
+++ b/test/e2e/testdata/tcproute-to-backend-ip.yaml
@@ -0,0 +1,36 @@
+apiVersion: gateway.networking.k8s.io/v1beta1
+kind: Gateway
+metadata:
+ name: my-tcp-gateway
+ namespace: gateway-conformance-infra
+spec:
+ gatewayClassName: "{GATEWAY_CLASS_NAME}"
+ listeners:
+ - name: foo
+ protocol: TCP
+ port: 8080
+ allowedRoutes:
+ kinds:
+ - kind: TCPRoute
+ - name: bar
+ protocol: TCP
+ port: 8090
+ allowedRoutes:
+ kinds:
+ - kind: TCPRoute
+---
+apiVersion: gateway.networking.k8s.io/v1alpha2
+kind: TCPRoute
+metadata:
+ name: tcp-app-1
+ namespace: gateway-conformance-infra
+spec:
+ parentRefs:
+ - name: my-tcp-gateway
+ sectionName: foo
+ rules:
+ - backendRefs:
+ - group: gateway.envoyproxy.io
+ kind: Backend
+ name: backend-ip
+ port: 8080
diff --git a/test/e2e/testdata/tcp-route.yaml b/test/e2e/testdata/tcproute.yaml
similarity index 100%
rename from test/e2e/testdata/tcp-route.yaml
rename to test/e2e/testdata/tcproute.yaml
diff --git a/test/e2e/testdata/udproute-to-backend-fqdn.yaml b/test/e2e/testdata/udproute-to-backend-fqdn.yaml
new file mode 100644
index 00000000000..8db8e12341e
--- /dev/null
+++ b/test/e2e/testdata/udproute-to-backend-fqdn.yaml
@@ -0,0 +1,105 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: coredns
+ namespace: gateway-conformance-infra
+ labels:
+ app: udp
+spec:
+ ports:
+ - name: udp-dns
+ port: 53
+ protocol: UDP
+ targetPort: 53
+ selector:
+ app: udp
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: coredns
+ namespace: gateway-conformance-infra
+ labels:
+ app: udp
+spec:
+ selector:
+ matchLabels:
+ app: udp
+ template:
+ metadata:
+ labels:
+ app: udp
+ spec:
+ containers:
+ - args:
+ - -conf
+ - /root/Corefile
+ image: coredns/coredns
+ name: coredns
+ volumeMounts:
+ - mountPath: /root
+ name: conf
+ volumes:
+ - configMap:
+ defaultMode: 420
+ name: coredns
+ name: conf
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: coredns
+ namespace: gateway-conformance-infra
+data:
+ Corefile: |
+ .:53 {
+ forward . 8.8.8.8 9.9.9.9
+ log
+ errors
+ }
+
+ foo.bar.com:53 {
+ whoami
+ }
+---
+apiVersion: gateway.networking.k8s.io/v1beta1
+kind: Gateway
+metadata:
+ name: udp-gateway
+ namespace: gateway-conformance-infra
+spec:
+ gatewayClassName: "{GATEWAY_CLASS_NAME}"
+ listeners:
+ - name: coredns
+ protocol: UDP
+ port: 5300
+ allowedRoutes:
+ kinds:
+ - kind: UDPRoute
+---
+apiVersion: gateway.networking.k8s.io/v1alpha2
+kind: UDPRoute
+metadata:
+ name: udp-coredns
+ namespace: gateway-conformance-infra
+spec:
+ parentRefs:
+ - name: udp-gateway
+ sectionName: coredns
+ rules:
+ - backendRefs:
+ - group: gateway.envoyproxy.io
+ kind: Backend
+ name: backend-fqdn
+ port: 53
+---
+apiVersion: gateway.envoyproxy.io/v1alpha1
+kind: Backend
+metadata:
+ name: backend-fqdn
+ namespace: gateway-conformance-infra
+spec:
+ endpoints:
+ - fqdn:
+ hostname: coredns.gateway-conformance-infra.svc.cluster.local
+ port: 53
diff --git a/test/e2e/testdata/udproute-to-backend-ip.yaml b/test/e2e/testdata/udproute-to-backend-ip.yaml
new file mode 100644
index 00000000000..b75c35ae5b7
--- /dev/null
+++ b/test/e2e/testdata/udproute-to-backend-ip.yaml
@@ -0,0 +1,94 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: coredns
+ namespace: gateway-conformance-infra
+ labels:
+ app: udp
+spec:
+ ports:
+ - name: udp-dns
+ port: 53
+ protocol: UDP
+ targetPort: 53
+ selector:
+ app: udp
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: coredns
+ namespace: gateway-conformance-infra
+ labels:
+ app: udp
+spec:
+ selector:
+ matchLabels:
+ app: udp
+ template:
+ metadata:
+ labels:
+ app: udp
+ spec:
+ containers:
+ - args:
+ - -conf
+ - /root/Corefile
+ image: coredns/coredns
+ name: coredns
+ volumeMounts:
+ - mountPath: /root
+ name: conf
+ volumes:
+ - configMap:
+ defaultMode: 420
+ name: coredns
+ name: conf
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: coredns
+ namespace: gateway-conformance-infra
+data:
+ Corefile: |
+ .:53 {
+ forward . 8.8.8.8 9.9.9.9
+ log
+ errors
+ }
+
+ foo.bar.com:53 {
+ whoami
+ }
+---
+apiVersion: gateway.networking.k8s.io/v1beta1
+kind: Gateway
+metadata:
+ name: udp-gateway
+ namespace: gateway-conformance-infra
+spec:
+ gatewayClassName: "{GATEWAY_CLASS_NAME}"
+ listeners:
+ - name: coredns
+ protocol: UDP
+ port: 5300
+ allowedRoutes:
+ kinds:
+ - kind: UDPRoute
+---
+apiVersion: gateway.networking.k8s.io/v1alpha2
+kind: UDPRoute
+metadata:
+ name: udp-coredns
+ namespace: gateway-conformance-infra
+spec:
+ parentRefs:
+ - name: udp-gateway
+ sectionName: coredns
+ rules:
+ - backendRefs:
+ - group: gateway.envoyproxy.io
+ kind: Backend
+ name: backend-ip
+ port: 53
diff --git a/test/e2e/testdata/udproute.yaml b/test/e2e/testdata/udproute.yaml
index 51abe821390..29ff6ed22ab 100644
--- a/test/e2e/testdata/udproute.yaml
+++ b/test/e2e/testdata/udproute.yaml
@@ -1,23 +1,16 @@
apiVersion: v1
-kind: Namespace
-metadata:
- name: gateway-conformance-udp
- labels:
- gateway-conformance: udp
----
-apiVersion: v1
kind: Service
metadata:
name: coredns
- namespace: gateway-conformance-udp
+ namespace: gateway-conformance-infra
labels:
app: udp
spec:
ports:
- - name: udp-dns
- port: 53
- protocol: UDP
- targetPort: 53
+ - name: udp-dns
+ port: 53
+ protocol: UDP
+ targetPort: 53
selector:
app: udp
---
@@ -25,7 +18,7 @@ apiVersion: apps/v1
kind: Deployment
metadata:
name: coredns
- namespace: gateway-conformance-udp
+ namespace: gateway-conformance-infra
labels:
app: udp
spec:
@@ -38,25 +31,25 @@ spec:
app: udp
spec:
containers:
- - args:
- - -conf
- - /root/Corefile
- image: coredns/coredns
- name: coredns
- volumeMounts:
- - mountPath: /root
- name: conf
- volumes:
- - configMap:
- defaultMode: 420
+ - args:
+ - -conf
+ - /root/Corefile
+ image: coredns/coredns
name: coredns
- name: conf
+ volumeMounts:
+ - mountPath: /root
+ name: conf
+ volumes:
+ - configMap:
+ defaultMode: 420
+ name: coredns
+ name: conf
---
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
- namespace: gateway-conformance-udp
+ namespace: gateway-conformance-infra
data:
Corefile: |
.:53 {
@@ -73,27 +66,27 @@ apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
name: udp-gateway
- namespace: gateway-conformance-udp
+ namespace: gateway-conformance-infra
spec:
gatewayClassName: "{GATEWAY_CLASS_NAME}"
listeners:
- - name: coredns
- protocol: UDP
- port: 5300
- allowedRoutes:
- kinds:
- - kind: UDPRoute
+ - name: coredns
+ protocol: UDP
+ port: 5300
+ allowedRoutes:
+ kinds:
+ - kind: UDPRoute
---
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: UDPRoute
metadata:
name: udp-coredns
- namespace: gateway-conformance-udp
+ namespace: gateway-conformance-infra
spec:
parentRefs:
- - name: udp-gateway
- sectionName: coredns
+ - name: udp-gateway
+ sectionName: coredns
rules:
- - backendRefs:
- - name: coredns
- port: 53
+ - backendRefs:
+ - name: coredns
+ port: 53
diff --git a/test/e2e/tests/grpcroute_with_backend.go b/test/e2e/tests/grpcroute_with_backend.go
new file mode 100644
index 00000000000..8bbe13afa45
--- /dev/null
+++ b/test/e2e/tests/grpcroute_with_backend.go
@@ -0,0 +1,83 @@
+// Copyright Envoy Gateway Authors
+// SPDX-License-Identifier: Apache-2.0
+// The full text of the Apache license is available in the LICENSE file at
+// the root of the repo.
+
+// This file contains code derived from upstream gateway-api, it will be moved to upstream.
+
+//go:build e2e
+
+package tests
+
+import (
+ "testing"
+
+ "k8s.io/apimachinery/pkg/types"
+ gwapiv1 "sigs.k8s.io/gateway-api/apis/v1"
+ "sigs.k8s.io/gateway-api/conformance/echo-basic/grpcechoserver"
+ "sigs.k8s.io/gateway-api/conformance/utils/grpc"
+ "sigs.k8s.io/gateway-api/conformance/utils/kubernetes"
+ "sigs.k8s.io/gateway-api/conformance/utils/suite"
+)
+
+func init() {
+ ConformanceTests = append(ConformanceTests, GRPCRouteBackendFQDNTest)
+ ConformanceTests = append(ConformanceTests, GRPCRouteBackendIPTest)
+}
+
+var GRPCRouteBackendFQDNTest = suite.ConformanceTest{
+ ShortName: "GRPCRouteBackendFQDNTest",
+ Description: "GRPCRoutes with a backend ref to a FQDN Backend",
+ Manifests: []string{"testdata/grpcroute-to-backend-fqdn.yaml"},
+ Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
+ t.Run("grpc-route-1", func(t *testing.T) {
+ testGRPCRouteWithBackend(t, suite, "backend-fqdn")
+ })
+ },
+}
+
+var GRPCRouteBackendIPTest = suite.ConformanceTest{
+ ShortName: "GRPCRouteBackendIPTest",
+ Description: "GRPCRoutes with a backend ref to an IP Backend",
+ Manifests: []string{"testdata/grpcroute-to-backend-ip.yaml"},
+ Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
+ t.Run("grpc-route-1", func(t *testing.T) {
+ svcNN := types.NamespacedName{
+ Name: "grpc-infra-backend-v1",
+ Namespace: "gateway-conformance-infra",
+ }
+ svc, err := GetService(suite.Client, svcNN)
+ if err != nil {
+ t.Fatalf("failed to get service %s: %v", svcNN, err)
+ }
+
+ backendIPName := "backend-ip"
+ ns := "gateway-conformance-infra"
+ err = CreateBackend(suite.Client, types.NamespacedName{Name: backendIPName, Namespace: ns}, svc.Spec.ClusterIP, 8080)
+ if err != nil {
+ t.Fatalf("failed to create backend %s: %v", backendIPName, err)
+ }
+ t.Cleanup(func() {
+ if err := DeleteBackend(suite.Client, types.NamespacedName{Name: backendIPName, Namespace: ns}); err != nil {
+ t.Fatalf("failed to delete backend %s: %v", backendIPName, err)
+ }
+ })
+ testGRPCRouteWithBackend(t, suite, backendIPName)
+ })
+ },
+}
+
+func testGRPCRouteWithBackend(t *testing.T, suite *suite.ConformanceTestSuite, backendName string) {
+ ns := "gateway-conformance-infra"
+ routeNN := types.NamespacedName{Name: "exact-matching", Namespace: ns}
+ gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns}
+ gwAddr := kubernetes.GatewayAndRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), &gwapiv1.GRPCRoute{}, routeNN)
+ BackendMustBeAccepted(t, suite.Client, types.NamespacedName{Name: backendName, Namespace: ns})
+ OkResp := grpc.ExpectedResponse{
+ EchoRequest: &grpcechoserver.EchoRequest{},
+ Backend: "grpc-infra-backend-v1",
+ Namespace: ns,
+ }
+
+ grpc.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.GRPCClient, suite.TimeoutConfig, gwAddr, OkResp)
+}
diff --git a/test/e2e/tests/ratelimit.go b/test/e2e/tests/ratelimit.go
index 799b6bbece0..9e8e5d9d1cc 100644
--- a/test/e2e/tests/ratelimit.go
+++ b/test/e2e/tests/ratelimit.go
@@ -14,12 +14,18 @@ import (
"time"
"github.com/stretchr/testify/require"
+ corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
+ gwapiv1 "sigs.k8s.io/gateway-api/apis/v1"
+ gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
"sigs.k8s.io/gateway-api/conformance/utils/http"
"sigs.k8s.io/gateway-api/conformance/utils/kubernetes"
"sigs.k8s.io/gateway-api/conformance/utils/roundtripper"
"sigs.k8s.io/gateway-api/conformance/utils/suite"
+
+ "github.com/envoyproxy/gateway/internal/gatewayapi"
+ "github.com/envoyproxy/gateway/internal/gatewayapi/resource"
)
func init() {
@@ -30,6 +36,7 @@ func init() {
ConformanceTests = append(ConformanceTests, RateLimitBasedJwtClaimsTest)
ConformanceTests = append(ConformanceTests, RateLimitMultipleListenersTest)
ConformanceTests = append(ConformanceTests, RateLimitHeadersAndCIDRMatchTest)
+ ConformanceTests = append(ConformanceTests, UsageRateLimitTest)
}
var RateLimitCIDRMatchTest = suite.ConformanceTest{
@@ -662,6 +669,68 @@ var RateLimitHeadersAndCIDRMatchTest = suite.ConformanceTest{
},
}
+var UsageRateLimitTest = suite.ConformanceTest{
+ ShortName: "UsageRateLimit",
+ Description: "Perform usage-based rate limit based on response content",
+ Manifests: []string{"testdata/ext-proc-service.yaml", "testdata/ratelimit-usage-ratelimit.yaml"},
+ Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
+ ns := "gateway-conformance-infra"
+ routeNN := types.NamespacedName{Name: "usage-rate-limit", Namespace: ns}
+ gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns}
+ gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)
+
+ // Waiting for the extproc service to be ready.
+ ancestorRef := gwapiv1a2.ParentReference{
+ Group: gatewayapi.GroupPtr(gwapiv1.GroupName),
+ Kind: gatewayapi.KindPtr(resource.KindGateway),
+ Namespace: gatewayapi.NamespacePtr(gwNN.Namespace),
+ Name: gwapiv1.ObjectName(gwNN.Name),
+ }
+ EnvoyExtensionPolicyMustBeAccepted(t, suite.Client, types.NamespacedName{Name: "usage-rate-limit", Namespace: ns}, suite.ControllerName, ancestorRef)
+ podReady := corev1.PodCondition{Type: corev1.PodReady, Status: corev1.ConditionTrue}
+ // Wait for the grpc ext auth service pod to be ready
+ WaitForPods(t, suite.Client, ns, map[string]string{"app": "grpc-ext-proc"}, corev1.PodRunning, podReady)
+
+ requestHeaders := map[string]string{"x-user-id": "one"}
+
+ ratelimitHeader := make(map[string]string)
+ expectOkResp := http.ExpectedResponse{
+ Request: http.Request{
+ Path: "/get",
+ Headers: requestHeaders,
+ },
+ Response: http.Response{StatusCode: 200, Headers: ratelimitHeader},
+ Namespace: ns,
+ }
+ expectOkResp.Response.Headers["X-Ratelimit-Limit"] = "21, 21;w=3600"
+ expectOkReq := http.MakeRequest(t, &expectOkResp, gwAddr, "HTTP", "http")
+
+ expectLimitResp := http.ExpectedResponse{
+ Request: http.Request{
+ Path: "/get",
+ Headers: requestHeaders,
+ },
+ Response: http.Response{
+ StatusCode: 429,
+ },
+ Namespace: ns,
+ }
+ expectLimitReq := http.MakeRequest(t, &expectLimitResp, gwAddr, "HTTP", "http")
+
+ // Keep sending requests till get 200 first, that will cost 10 usage.
+ http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, expectOkResp)
+
+ // The next two request will be fine as the limit is set to 21.
+ if err := GotExactExpectedResponse(t, 2, suite.RoundTripper, expectOkReq, expectOkResp); err != nil {
+ t.Errorf("failed to get expected response for the first three requests: %v", err)
+ }
+ // At this point, the budget must be zero (21 -> 11 -> 1 -> 0), so the next request will be limited.
+ if err := GotExactExpectedResponse(t, 1, suite.RoundTripper, expectLimitReq, expectLimitResp); err != nil {
+ t.Errorf("failed to get expected response for the last (fourth) request: %v", err)
+ }
+ },
+}
+
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/e2e/tests/tcp_route.go b/test/e2e/tests/tcproute.go
similarity index 99%
rename from test/e2e/tests/tcp_route.go
rename to test/e2e/tests/tcproute.go
index 5526327a68e..1902fb956e6 100644
--- a/test/e2e/tests/tcp_route.go
+++ b/test/e2e/tests/tcproute.go
@@ -38,7 +38,7 @@ func init() {
var TCPRouteTest = suite.ConformanceTest{
ShortName: "TCPRoute",
Description: "Testing TCP Route",
- Manifests: []string{"testdata/tcp-route.yaml"},
+ Manifests: []string{"testdata/tcproute.yaml"},
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
t.Run("tcp-route-1", func(t *testing.T) {
ns := "gateway-conformance-infra"
diff --git a/test/e2e/tests/tcproute_with_backend.go b/test/e2e/tests/tcproute_with_backend.go
new file mode 100644
index 00000000000..e78afe26d17
--- /dev/null
+++ b/test/e2e/tests/tcproute_with_backend.go
@@ -0,0 +1,85 @@
+// Copyright Envoy Gateway Authors
+// SPDX-License-Identifier: Apache-2.0
+// The full text of the Apache license is available in the LICENSE file at
+// the root of the repo.
+
+// This file contains code derived from upstream gateway-api, it will be moved to upstream.
+
+//go:build e2e
+
+package tests
+
+import (
+ "testing"
+
+ "k8s.io/apimachinery/pkg/types"
+ "sigs.k8s.io/gateway-api/conformance/utils/http"
+ "sigs.k8s.io/gateway-api/conformance/utils/suite"
+)
+
+func init() {
+ ConformanceTests = append(ConformanceTests, TCPRouteBackendFQDNTest)
+ ConformanceTests = append(ConformanceTests, TCPRouteBackendIPTest)
+}
+
+var TCPRouteBackendFQDNTest = suite.ConformanceTest{
+ ShortName: "TCPRouteBackendFQDNTest",
+ Description: "TCPRoutes with a backend ref to a FQDN Backend",
+ Manifests: []string{"testdata/tcproute-to-backend-fqdn.yaml"},
+ Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
+ t.Run("tcp-route-1", func(t *testing.T) {
+ testTCPRouteWithBackend(t, suite, "backend-fqdn")
+ })
+ },
+}
+
+var TCPRouteBackendIPTest = suite.ConformanceTest{
+ ShortName: "TCPRouteBackendIPTest",
+ Description: "TCPRoutes with a backend ref to an IP Backend",
+ Manifests: []string{"testdata/tcproute-to-backend-ip.yaml"},
+ Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
+ t.Run("tcp-route-1", func(t *testing.T) {
+ svcNN := types.NamespacedName{
+ Name: "infra-backend-v1",
+ Namespace: "gateway-conformance-infra",
+ }
+ svc, err := GetService(suite.Client, svcNN)
+ if err != nil {
+ t.Fatalf("failed to get service %s: %v", svcNN, err)
+ }
+
+ backendIPName := "backend-ip"
+ ns := "gateway-conformance-infra"
+ err = CreateBackend(suite.Client, types.NamespacedName{Name: backendIPName, Namespace: ns}, svc.Spec.ClusterIP, 8080)
+ if err != nil {
+ t.Fatalf("failed to create backend %s: %v", backendIPName, err)
+ }
+ t.Cleanup(func() {
+ if err := DeleteBackend(suite.Client, types.NamespacedName{Name: backendIPName, Namespace: ns}); err != nil {
+ t.Fatalf("failed to delete backend %s: %v", backendIPName, err)
+ }
+ })
+ testTCPRouteWithBackend(t, suite, backendIPName)
+ })
+ },
+}
+
+func testTCPRouteWithBackend(t *testing.T, suite *suite.ConformanceTestSuite, backendName string) {
+ ns := "gateway-conformance-infra"
+ routeNN := types.NamespacedName{Name: "tcp-app-1", Namespace: ns}
+ gwNN := types.NamespacedName{Name: "my-tcp-gateway", Namespace: ns}
+ gwAddr := GatewayAndTCPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, NewGatewayRef(gwNN), routeNN)
+ BackendMustBeAccepted(t, suite.Client, types.NamespacedName{Name: backendName, Namespace: ns})
+ OkResp := http.ExpectedResponse{
+ Request: http.Request{
+ Path: "/",
+ },
+ Response: http.Response{
+ StatusCode: 200,
+ },
+ Namespace: ns,
+ }
+
+ // Send a request to an valid path and expect a successful response
+ http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, OkResp)
+}
diff --git a/test/e2e/tests/udproute.go b/test/e2e/tests/udproute.go
index 30211de759e..b2f3810582d 100644
--- a/test/e2e/tests/udproute.go
+++ b/test/e2e/tests/udproute.go
@@ -40,7 +40,7 @@ var UDPRouteTest = suite.ConformanceTest{
Manifests: []string{"testdata/udproute.yaml"},
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
t.Run("Simple UDP request matching UDPRoute should reach coredns backend", func(t *testing.T) {
- namespace := "gateway-conformance-udp"
+ namespace := "gateway-conformance-infra"
domain := "foo.bar.com."
routeNN := types.NamespacedName{Name: "udp-coredns", Namespace: namespace}
gwNN := types.NamespacedName{Name: "udp-gateway", Namespace: namespace}
diff --git a/test/e2e/tests/udproute_with_backend.go b/test/e2e/tests/udproute_with_backend.go
new file mode 100644
index 00000000000..c7ccbbeb002
--- /dev/null
+++ b/test/e2e/tests/udproute_with_backend.go
@@ -0,0 +1,100 @@
+// Copyright Envoy Gateway Authors
+// SPDX-License-Identifier: Apache-2.0
+// The full text of the Apache license is available in the LICENSE file at
+// the root of the repo.
+
+// This file contains code derived from upstream gateway-api, it will be moved to upstream.
+
+//go:build e2e
+
+package tests
+
+import (
+ "context"
+ "testing"
+ "time"
+
+ "github.com/miekg/dns"
+ "k8s.io/apimachinery/pkg/types"
+ "k8s.io/apimachinery/pkg/util/wait"
+ "sigs.k8s.io/gateway-api/conformance/utils/suite"
+ "sigs.k8s.io/gateway-api/conformance/utils/tlog"
+)
+
+func init() {
+ ConformanceTests = append(ConformanceTests, UDPRouteBackendFQDNTest)
+ ConformanceTests = append(ConformanceTests, UDPRouteBackendIPTest)
+}
+
+var UDPRouteBackendFQDNTest = suite.ConformanceTest{
+ ShortName: "UDPRouteBackendFQDNTest",
+ Description: "UDPRoutes with a backend ref to a FQDN Backend",
+ Manifests: []string{
+ "testdata/udproute-to-backend-fqdn.yaml",
+ },
+ Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
+ t.Run("UDPRoute with a FQDN type Backend", func(t *testing.T) {
+ testUDPRouteWithBackend(t, suite, "backend-fqdn")
+ })
+ },
+}
+
+var UDPRouteBackendIPTest = suite.ConformanceTest{
+ ShortName: "UDPRouteBackendIP",
+ Description: "UDPRoutes with a backend ref to an IP Backend",
+ Manifests: []string{
+ "testdata/udproute-to-backend-ip.yaml",
+ },
+ Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
+ t.Run("UDPRoute with a IP type Backend", func(t *testing.T) {
+ svcNN := types.NamespacedName{
+ Name: "coredns",
+ Namespace: "gateway-conformance-infra",
+ }
+ svc, err := GetService(suite.Client, svcNN)
+ if err != nil {
+ t.Fatalf("failed to get service %s: %v", svcNN, err)
+ }
+
+ backendIPName := "backend-ip"
+ ns := "gateway-conformance-infra"
+ err = CreateBackend(suite.Client, types.NamespacedName{Name: backendIPName, Namespace: ns}, svc.Spec.ClusterIP, 53)
+ if err != nil {
+ t.Fatalf("failed to create backend %s: %v", backendIPName, err)
+ }
+ t.Cleanup(func() {
+ if err := DeleteBackend(suite.Client, types.NamespacedName{Name: backendIPName, Namespace: ns}); err != nil {
+ t.Fatalf("failed to delete backend %s: %v", backendIPName, err)
+ }
+ })
+ testUDPRouteWithBackend(t, suite, backendIPName)
+ })
+ },
+}
+
+func testUDPRouteWithBackend(t *testing.T, suite *suite.ConformanceTestSuite, backend string) {
+ namespace := "gateway-conformance-infra"
+ domain := "foo.bar.com."
+ routeNN := types.NamespacedName{Name: "udp-coredns", Namespace: namespace}
+ gwNN := types.NamespacedName{Name: "udp-gateway", Namespace: namespace}
+ gwAddr := GatewayAndUDPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, NewGatewayRef(gwNN), routeNN)
+
+ BackendMustBeAccepted(t, suite.Client, types.NamespacedName{Name: backend, Namespace: namespace})
+
+ msg := new(dns.Msg)
+ msg.SetQuestion(domain, dns.TypeA)
+
+ if err := wait.PollUntilContextTimeout(context.TODO(), time.Second, time.Minute, true,
+ func(_ context.Context) (done bool, err error) {
+ tlog.Logf(t, "performing DNS query %s on %s", domain, gwAddr)
+ r, err := dns.Exchange(msg, gwAddr)
+ if err != nil {
+ tlog.Logf(t, "failed to perform a UDP query: %v", err)
+ return false, nil
+ }
+ tlog.Logf(t, "got DNS response: %s", r.String())
+ return true, nil
+ }); err != nil {
+ t.Errorf("failed to perform DNS query: %v", err)
+ }
+}
diff --git a/tools/src/buf/go.mod b/tools/src/buf/go.mod
index c813a5f7d2e..fa8c49a1524 100644
--- a/tools/src/buf/go.mod
+++ b/tools/src/buf/go.mod
@@ -2,27 +2,27 @@ module local
go 1.23.3
-require github.com/bufbuild/buf v1.48.0
+require github.com/bufbuild/buf v1.49.0
require (
- buf.build/gen/go/bufbuild/bufplugin/protocolbuffers/go v1.36.0-20241031151143-70f632351282.1 // indirect
- buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.0-20241127180247-a33202765966.1 // indirect
- buf.build/gen/go/bufbuild/registry/connectrpc/go v1.17.0-20241210175624-28487aef65cd.1 // indirect
- buf.build/gen/go/bufbuild/registry/protocolbuffers/go v1.36.0-20241210175624-28487aef65cd.1 // indirect
- buf.build/gen/go/pluginrpc/pluginrpc/protocolbuffers/go v1.36.0-20241007202033-cf42259fcbfc.1 // indirect
+ buf.build/gen/go/bufbuild/bufplugin/protocolbuffers/go v1.36.1-20241031151143-70f632351282.1 // indirect
+ buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.1-20241127180247-a33202765966.1 // indirect
+ buf.build/gen/go/bufbuild/registry/connectrpc/go v1.17.0-20241227185654-946b6dd39b27.1 // indirect
+ buf.build/gen/go/bufbuild/registry/protocolbuffers/go v1.36.1-20241227185654-946b6dd39b27.1 // indirect
+ buf.build/gen/go/pluginrpc/pluginrpc/protocolbuffers/go v1.36.1-20241007202033-cf42259fcbfc.1 // indirect
buf.build/go/bufplugin v0.6.0 // indirect
buf.build/go/protoyaml v0.3.1 // indirect
buf.build/go/spdx v0.2.0 // indirect
cel.dev/expr v0.19.1 // indirect
connectrpc.com/connect v1.17.0 // indirect
connectrpc.com/otelconnect v0.7.1 // indirect
- github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
+ github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/Microsoft/hcsshim v0.12.9 // indirect
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
github.com/bufbuild/protocompile v0.14.1 // indirect
github.com/bufbuild/protoplugin v0.0.0-20240911180120-7bb73e41a54a // indirect
- github.com/bufbuild/protovalidate-go v0.8.0 // indirect
+ github.com/bufbuild/protovalidate-go v0.8.2 // indirect
github.com/containerd/cgroups/v3 v3.0.5 // indirect
github.com/containerd/containerd v1.7.24 // indirect
github.com/containerd/continuity v0.4.5 // indirect
@@ -31,7 +31,7 @@ require (
github.com/containerd/log v0.1.0 // indirect
github.com/containerd/platforms v0.2.1 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.16.3 // indirect
- github.com/containerd/ttrpc v1.2.6 // indirect
+ github.com/containerd/ttrpc v1.2.7 // indirect
github.com/containerd/typeurl/v2 v2.2.3 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
github.com/distribution/reference v0.6.0 // indirect
@@ -69,9 +69,9 @@ require (
github.com/moby/sys/sequential v0.6.0 // indirect
github.com/moby/sys/user v0.3.0 // indirect
github.com/moby/sys/userns v0.1.0 // indirect
- github.com/moby/term v0.5.0 // indirect
+ github.com/moby/term v0.5.2 // indirect
github.com/morikuni/aec v1.0.0 // indirect
- github.com/onsi/ginkgo/v2 v2.22.0 // indirect
+ github.com/onsi/ginkgo/v2 v2.22.2 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/opencontainers/runtime-spec v1.2.0 // indirect
@@ -106,7 +106,7 @@ require (
go.uber.org/zap v1.27.0 // indirect
go.uber.org/zap/exp v0.3.0 // indirect
golang.org/x/crypto v0.32.0 // indirect
- golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 // indirect
+ golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329 // indirect
golang.org/x/mod v0.22.0 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/sync v0.10.0 // indirect
@@ -114,10 +114,10 @@ require (
golang.org/x/term v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/tools v0.28.0 // indirect
- google.golang.org/genproto/googleapis/api v0.0.0-20241219192143-6b3ec007d9bb // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20241219192143-6b3ec007d9bb // indirect
+ google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d // indirect
google.golang.org/grpc v1.69.2 // indirect
- google.golang.org/protobuf v1.36.0 // indirect
+ google.golang.org/protobuf v1.36.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
pluginrpc.com/pluginrpc v0.5.0 // indirect
)
diff --git a/tools/src/buf/go.sum b/tools/src/buf/go.sum
index 7f957b8c8be..701915f9f7e 100644
--- a/tools/src/buf/go.sum
+++ b/tools/src/buf/go.sum
@@ -1,13 +1,13 @@
-buf.build/gen/go/bufbuild/bufplugin/protocolbuffers/go v1.36.0-20241031151143-70f632351282.1 h1:FXEFgDFrBYuXjn3twNRo/t80qSbdKmkfZSgR2JGTuyk=
-buf.build/gen/go/bufbuild/bufplugin/protocolbuffers/go v1.36.0-20241031151143-70f632351282.1/go.mod h1:/bPD5uslGsdRKBeVavIK7D7yr+3ISI0OoyUOkokSJTA=
-buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.0-20241127180247-a33202765966.1 h1:ntAj16eF7AtUyzOOAFk5gvbAO52QmUKPKk7GmsIEORo=
-buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.0-20241127180247-a33202765966.1/go.mod h1:AxRT+qTj5PJCz2nyQzsR/qxAcveW5USRhJTt/edTO5w=
-buf.build/gen/go/bufbuild/registry/connectrpc/go v1.17.0-20241210175624-28487aef65cd.1 h1:x8cCitPNXODGzWbfApZMFc4ALtRe5LZJmTdAkNqk62A=
-buf.build/gen/go/bufbuild/registry/connectrpc/go v1.17.0-20241210175624-28487aef65cd.1/go.mod h1:kDOQd1sZ0wRp33hvCTQeaz9KprnHNfJ+a8dcIQ/6+0k=
-buf.build/gen/go/bufbuild/registry/protocolbuffers/go v1.36.0-20241210175624-28487aef65cd.1 h1:NOuNS+nCp3xc4CALIC7sNz7irT63UMcYAfofrxurUfE=
-buf.build/gen/go/bufbuild/registry/protocolbuffers/go v1.36.0-20241210175624-28487aef65cd.1/go.mod h1:NeX3YCZgM9E/wNp9e3g/9u5bu8/OPntr7K0ygUlgrDE=
-buf.build/gen/go/pluginrpc/pluginrpc/protocolbuffers/go v1.36.0-20241007202033-cf42259fcbfc.1 h1:HuK77NuzllXrJNgB+lAtnG2dKrB7WAjd9QQ+n0zTQHc=
-buf.build/gen/go/pluginrpc/pluginrpc/protocolbuffers/go v1.36.0-20241007202033-cf42259fcbfc.1/go.mod h1:BLQCnWbu3tZcKQfbU1f5ysbRk55FDFwOvjlyzN+uLXg=
+buf.build/gen/go/bufbuild/bufplugin/protocolbuffers/go v1.36.1-20241031151143-70f632351282.1 h1:DELauram5j4snPU5wB0/I7EIQiy7oRjngqbitbj4o6c=
+buf.build/gen/go/bufbuild/bufplugin/protocolbuffers/go v1.36.1-20241031151143-70f632351282.1/go.mod h1:VfQZo/3dl9O9Yg1nseI0gNGDXvLrNQfBRMMIhV9fyII=
+buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.1-20241127180247-a33202765966.1 h1:v223wh/bhlSHSc0tU9PXRWXHhkw3UWMtth7TmYGfHAQ=
+buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.1-20241127180247-a33202765966.1/go.mod h1:/zlFuuECgFgewxwW6qQKgvMJ07YZkWlVkcSxEhJprJw=
+buf.build/gen/go/bufbuild/registry/connectrpc/go v1.17.0-20241227185654-946b6dd39b27.1 h1:410axzwOITvAHu1w8pN9Gwhmk1QxrWneq6I/gowLfH8=
+buf.build/gen/go/bufbuild/registry/connectrpc/go v1.17.0-20241227185654-946b6dd39b27.1/go.mod h1:i4UUrB1UIqScCCj4d+OFXpUREDDz+p1gCl9WfjpYweY=
+buf.build/gen/go/bufbuild/registry/protocolbuffers/go v1.36.1-20241227185654-946b6dd39b27.1 h1:5vU2X68XNaJtF/NGS9shI/0X0jze8/5XH4RmHbsZg5w=
+buf.build/gen/go/bufbuild/registry/protocolbuffers/go v1.36.1-20241227185654-946b6dd39b27.1/go.mod h1:DgqZjUk2avpwh8jiMHBOoVXUyceQ3HxNwcRLEWWwNtQ=
+buf.build/gen/go/pluginrpc/pluginrpc/protocolbuffers/go v1.36.1-20241007202033-cf42259fcbfc.1 h1:0Pi0EQh6z2zJigi4UonoqczBQjvOzZ0CFYUfwPRM41M=
+buf.build/gen/go/pluginrpc/pluginrpc/protocolbuffers/go v1.36.1-20241007202033-cf42259fcbfc.1/go.mod h1:Uy8SKofLXIAUjswDmz6AIN8W+bGVTF4kNczZikMPHLM=
buf.build/go/bufplugin v0.6.0 h1:3lhoh+0z+IUPS3ZajTPn/27LaLIkero2BDVnV7yXD1s=
buf.build/go/bufplugin v0.6.0/go.mod h1:hWCjxxv24xdR6F5pNlQavZV2oo0J3uF4Ff1XEoyV6vU=
buf.build/go/protoyaml v0.3.1 h1:ucyzE7DRnjX+mQ6AH4JzN0Kg50ByHHu+yrSKbgQn2D4=
@@ -23,8 +23,8 @@ connectrpc.com/otelconnect v0.7.1 h1:scO5pOb0i4yUE66CnNrHeK1x51yq0bE0ehPg6WvzXJY
connectrpc.com/otelconnect v0.7.1/go.mod h1:dh3bFgHBTb2bkqGCeVVOtHJreSns7uu9wwL2Tbz17ms=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
-github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
-github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
+github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
+github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
@@ -32,14 +32,14 @@ github.com/Microsoft/hcsshim v0.12.9 h1:2zJy5KA+l0loz1HzEGqyNnjd3fyZA31ZBCGKacp6
github.com/Microsoft/hcsshim v0.12.9/go.mod h1:fJ0gkFAna6ukt0bLdKB8djt4XIJhF/vEPuoIWYVvZ8Y=
github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ=
github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw=
-github.com/bufbuild/buf v1.48.0 h1:JiA1Ynz6DE/MBDcEsFvWNoaPhnjaSdLzKH00/5SWomg=
-github.com/bufbuild/buf v1.48.0/go.mod h1:lHjK93s3FLn6GOec0f2uqFeREhfL0Qw5dvZ3eipclD8=
+github.com/bufbuild/buf v1.49.0 h1:KFSyea50xgvfnqGfK7Z4UCPTYhoCoUYAK2IoRd8/oUA=
+github.com/bufbuild/buf v1.49.0/go.mod h1:PssF/YByu7WU6K3FxRmoRxi4ZViugL7rjAaoVvxknso=
github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw=
github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c=
github.com/bufbuild/protoplugin v0.0.0-20240911180120-7bb73e41a54a h1:l3RhVoG0RtC61h6TVWnkniGj4TgBebuyPQRdleFAmTg=
github.com/bufbuild/protoplugin v0.0.0-20240911180120-7bb73e41a54a/go.mod h1:c5D8gWRIZ2HLWO3gXYTtUfw/hbJyD8xikv2ooPxnklQ=
-github.com/bufbuild/protovalidate-go v0.8.0 h1:Xs3kCLCJ4tQiogJ0iOXm+ClKw/KviW3nLAryCGW2I3Y=
-github.com/bufbuild/protovalidate-go v0.8.0/go.mod h1:JPWZInGm2y2NBg3vKDKdDIkvDjyLv31J3hLH5GIFc/Q=
+github.com/bufbuild/protovalidate-go v0.8.2 h1:sgzXHkHYP6HnAsL2Rd3I1JxkYUyEQUv9awU1PduMxbM=
+github.com/bufbuild/protovalidate-go v0.8.2/go.mod h1:K6w8iPNAXBoIivVueSELbUeUl+MmeTQfCDSug85pn3M=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@@ -70,8 +70,8 @@ github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpS
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
github.com/containerd/stargz-snapshotter/estargz v0.16.3 h1:7evrXtoh1mSbGj/pfRccTampEyKpjpOnS3CyiV1Ebr8=
github.com/containerd/stargz-snapshotter/estargz v0.16.3/go.mod h1:uyr4BfYfOj3G9WBVE8cOlQmXAbPN9VEQpBBeJIuOipU=
-github.com/containerd/ttrpc v1.2.6 h1:zG+Kn5EZ6MUYCS1t2Hmt2J4tMVaLSFEJVOraDQwNPC4=
-github.com/containerd/ttrpc v1.2.6/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o=
+github.com/containerd/ttrpc v1.2.7 h1:qIrroQvuOL9HQ1X6KHe2ohc7p+HP/0VE6XPU7elJRqQ=
+github.com/containerd/ttrpc v1.2.7/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o=
github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40=
github.com/containerd/typeurl/v2 v2.2.3/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
@@ -201,14 +201,14 @@ github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo=
github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
-github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
-github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
+github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=
+github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
-github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg=
-github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
-github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8=
-github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc=
+github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU=
+github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk=
+github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
+github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
@@ -312,8 +312,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 h1:1UoZQm6f0P/ZO0w1Ri+f+ifG/gXhegadRdwBIXEFWDo=
-golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
+golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329 h1:9kj3STMvgqy3YA4VQXBrN7925ICMxD5wzMRcgA30588=
+golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
@@ -379,10 +379,10 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
-google.golang.org/genproto/googleapis/api v0.0.0-20241219192143-6b3ec007d9bb h1:B7GIB7sr443wZ/EAEl7VZjmh1V6qzkt5V+RYcUYtS1U=
-google.golang.org/genproto/googleapis/api v0.0.0-20241219192143-6b3ec007d9bb/go.mod h1:E5//3O5ZIG2l71Xnt+P/CYUY8Bxs8E7WMoZ9tlcMbAY=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20241219192143-6b3ec007d9bb h1:3oy2tynMOP1QbTC0MsNNAV+Se8M2Bd0A5+x1QHyw+pI=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20241219192143-6b3ec007d9bb/go.mod h1:lcTa1sDdWEIHMWlITnIczmw5w60CF9ffkb8Z+DVmmjA=
+google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d h1:H8tOf8XM88HvKqLTxe755haY6r1fqqzLbEnfrmLXlSA=
+google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d/go.mod h1:2v7Z7gP2ZUOGsaFyxATQSRoBnKygqVq2Cwnvom7QiqY=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d h1:xJJRGY7TJcvIlpSrN3K6LAWgNFUILlO+OMAqtg9aqnw=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d/go.mod h1:3ENsm/5D1mzDyhpzeRi1NR784I0BcofWBoSc5QqqMK4=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
@@ -399,8 +399,8 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
-google.golang.org/protobuf v1.36.0 h1:mjIs9gYtt56AzC4ZaffQuh88TZurBGhIJMBZGSxNerQ=
-google.golang.org/protobuf v1.36.0/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
+google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
+google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=