diff --git a/api/v1alpha1/admission_control.go b/api/v1alpha1/admission_control.go
new file mode 100644
index 0000000000..9588d476ca
--- /dev/null
+++ b/api/v1alpha1/admission_control.go
@@ -0,0 +1,119 @@
+// 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.
+
+package v1alpha1
+
+import (
+ gwapiv1 "sigs.k8s.io/gateway-api/apis/v1"
+)
+
+// AdmissionControl defines the admission control policy to be applied.
+// This configuration probabilistically rejects requests based on the success rate
+// of previous requests in a configurable sliding time window.
+// All fields are optional and will use Envoy's defaults when not specified.
+//
+// +kubebuilder:validation:XValidation:rule="!has(self.successRateThreshold) || (self.successRateThreshold >= 1 && self.successRateThreshold <= 100)",message="successRateThreshold must be between 1 and 100"
+// +kubebuilder:validation:XValidation:rule="!has(self.maxRejectionProbability) || (self.maxRejectionProbability >= 0 && self.maxRejectionProbability <= 100)",message="maxRejectionProbability must be between 0 and 100"
+type AdmissionControl struct {
+ // SamplingWindow defines the time window over which request success rates are calculated.
+ // Defaults to 30s if not specified.
+ //
+ // +optional
+ SamplingWindow *gwapiv1.Duration `json:"samplingWindow,omitempty"`
+
+ // SuccessRateThreshold is the lowest request success rate, as a percentage in the
+ // range [1, 100], at which the filter will not reject requests. Defaults to 95 if
+ // not specified. Envoy rejects values below 1%, so values lower than 1 are not allowed.
+ //
+ // +optional
+ // +kubebuilder:validation:Minimum=1
+ // +kubebuilder:validation:Maximum=100
+ SuccessRateThreshold *uint32 `json:"successRateThreshold,omitempty"`
+
+ // Aggression controls the rejection probability curve. A value of 1 means a linear
+ // increase in rejection probability as the success rate decreases. Higher values
+ // result in more aggressive rejection at higher success rates.
+ // Envoy requires aggression to be greater than 0 and clamps values below 1 to 1.
+ // Defaults to 1 if not specified.
+ //
+ // +optional
+ // +kubebuilder:validation:Minimum=1
+ Aggression *uint32 `json:"aggression,omitempty"`
+
+ // RPSThreshold defines the minimum requests per second below which requests will
+ // pass through the filter without rejection. Defaults to 0 if not specified.
+ //
+ // +optional
+ // +kubebuilder:validation:Minimum=0
+ RPSThreshold *uint32 `json:"rpsThreshold,omitempty"`
+
+ // MaxRejectionProbability represents the upper limit of the rejection probability,
+ // expressed as a percentage in the range [0, 100]. Defaults to 80 if not specified.
+ //
+ // +optional
+ // +kubebuilder:validation:Minimum=0
+ // +kubebuilder:validation:Maximum=100
+ MaxRejectionProbability *uint32 `json:"maxRejectionProbability,omitempty"`
+
+ // SuccessCriteria defines what constitutes a successful request for both HTTP and gRPC.
+ //
+ // +optional
+ SuccessCriteria *AdmissionControlSuccessCriteria `json:"successCriteria,omitempty"`
+}
+
+// AdmissionControlSuccessCriteria defines the criteria for determining successful requests.
+type AdmissionControlSuccessCriteria struct {
+ // HTTP defines success criteria for HTTP requests.
+ //
+ // +optional
+ HTTP *HTTPSuccessCriteria `json:"http,omitempty"`
+
+ // GRPC defines success criteria for gRPC requests.
+ //
+ // +optional
+ GRPC *GRPCSuccessCriteria `json:"grpc,omitempty"`
+}
+
+// HTTPSuccessCriteria defines success criteria for HTTP requests.
+type HTTPSuccessCriteria struct {
+ // HTTPSuccessStatus defines HTTP status codes that are considered successful.
+ //
+ // +optional
+ HTTPSuccessStatus []HTTPStatus `json:"httpSuccessStatus,omitempty"`
+}
+
+// GRPCSuccessCode defines gRPC status codes as defined in
+// https://github.com/grpc/grpc/blob/master/doc/statuscodes.md#status-codes-and-their-use-in-grpc.
+// +kubebuilder:validation:Enum=Ok;Cancelled;Unknown;InvalidArgument;DeadlineExceeded;NotFound;AlreadyExists;PermissionDenied;ResourceExhausted;FailedPrecondition;Aborted;OutOfRange;Unimplemented;Internal;Unavailable;DataLoss;Unauthenticated
+type GRPCSuccessCode string
+
+const (
+ GRPCSuccessCodeOk GRPCSuccessCode = "Ok"
+ GRPCSuccessCodeCancelled GRPCSuccessCode = "Cancelled"
+ GRPCSuccessCodeUnknown GRPCSuccessCode = "Unknown"
+ GRPCSuccessCodeInvalidArgument GRPCSuccessCode = "InvalidArgument"
+ GRPCSuccessCodeDeadlineExceeded GRPCSuccessCode = "DeadlineExceeded"
+ GRPCSuccessCodeNotFound GRPCSuccessCode = "NotFound"
+ GRPCSuccessCodeAlreadyExists GRPCSuccessCode = "AlreadyExists"
+ GRPCSuccessCodePermissionDenied GRPCSuccessCode = "PermissionDenied"
+ GRPCSuccessCodeResourceExhausted GRPCSuccessCode = "ResourceExhausted"
+ GRPCSuccessCodeFailedPrecondition GRPCSuccessCode = "FailedPrecondition"
+ GRPCSuccessCodeAborted GRPCSuccessCode = "Aborted"
+ GRPCSuccessCodeOutOfRange GRPCSuccessCode = "OutOfRange"
+ GRPCSuccessCodeUnimplemented GRPCSuccessCode = "Unimplemented"
+ GRPCSuccessCodeInternal GRPCSuccessCode = "Internal"
+ GRPCSuccessCodeUnavailable GRPCSuccessCode = "Unavailable"
+ GRPCSuccessCodeDataLoss GRPCSuccessCode = "DataLoss"
+ GRPCSuccessCodeUnauthenticated GRPCSuccessCode = "Unauthenticated"
+)
+
+// GRPCSuccessCriteria defines success criteria for gRPC requests.
+type GRPCSuccessCriteria struct {
+ // GRPCSuccessStatus defines gRPC status codes that are considered successful.
+ // Status codes are defined in https://github.com/grpc/grpc/blob/master/doc/statuscodes.md#status-codes-and-their-use-in-grpc.
+ //
+ // +optional
+ GRPCSuccessStatus []GRPCSuccessCode `json:"grpcSuccessStatus,omitempty"`
+}
diff --git a/api/v1alpha1/backendtrafficpolicy_types.go b/api/v1alpha1/backendtrafficpolicy_types.go
index 6fee182818..57861d4699 100644
--- a/api/v1alpha1/backendtrafficpolicy_types.go
+++ b/api/v1alpha1/backendtrafficpolicy_types.go
@@ -69,6 +69,12 @@ type BackendTrafficPolicySpec struct {
// +optional
FaultInjection *FaultInjection `json:"faultInjection,omitempty"`
+ // AdmissionControl defines the admission control policy to be applied. This configuration
+ // probabilistically rejects requests based on the success rate of previous requests in a
+ // configurable sliding time window.
+ // +optional
+ AdmissionControl *AdmissionControl `json:"admissionControl,omitempty"`
+
// UseClientProtocol configures Envoy to prefer sending requests to backends using
// the same HTTP protocol that the incoming request used. Defaults to false, which means
// that Envoy will use the protocol indicated by the attached BackendRef.
diff --git a/api/v1alpha1/envoyproxy_types.go b/api/v1alpha1/envoyproxy_types.go
index b0531b6714..a37148d4f5 100644
--- a/api/v1alpha1/envoyproxy_types.go
+++ b/api/v1alpha1/envoyproxy_types.go
@@ -286,7 +286,7 @@ type FilterPosition struct {
}
// EnvoyFilter defines the type of Envoy HTTP filter.
-// +kubebuilder:validation:Enum=envoy.filters.http.custom_response;envoy.filters.http.health_check;envoy.filters.http.fault;envoy.filters.http.cors;envoy.filters.http.header_mutation;envoy.filters.http.ext_authz;envoy.filters.http.api_key_auth;envoy.filters.http.basic_auth;envoy.filters.http.oauth2;envoy.filters.http.jwt_authn;envoy.filters.http.stateful_session;envoy.filters.http.buffer;envoy.filters.http.lua;envoy.filters.http.ext_proc;envoy.filters.http.wasm;envoy.filters.http.dynamic_modules;envoy.filters.http.geoip;envoy.filters.http.rbac;envoy.filters.http.local_ratelimit;envoy.filters.http.ratelimit;envoy.filters.http.grpc_web;envoy.filters.http.grpc_stats;envoy.filters.http.credential_injector;envoy.filters.http.compressor;envoy.filters.http.dynamic_forward_proxy
+// +kubebuilder:validation:Enum=envoy.filters.http.custom_response;envoy.filters.http.health_check;envoy.filters.http.fault;envoy.filters.http.admission_control;envoy.filters.http.cors;envoy.filters.http.header_mutation;envoy.filters.http.ext_authz;envoy.filters.http.api_key_auth;envoy.filters.http.basic_auth;envoy.filters.http.oauth2;envoy.filters.http.jwt_authn;envoy.filters.http.stateful_session;envoy.filters.http.buffer;envoy.filters.http.lua;envoy.filters.http.ext_proc;envoy.filters.http.wasm;envoy.filters.http.dynamic_modules;envoy.filters.http.geoip;envoy.filters.http.rbac;envoy.filters.http.local_ratelimit;envoy.filters.http.ratelimit;envoy.filters.http.grpc_web;envoy.filters.http.grpc_stats;envoy.filters.http.credential_injector;envoy.filters.http.compressor;envoy.filters.http.dynamic_forward_proxy
type EnvoyFilter string
const (
@@ -299,6 +299,9 @@ const (
// EnvoyFilterFault defines the Envoy HTTP fault filter.
EnvoyFilterFault EnvoyFilter = "envoy.filters.http.fault"
+ // EnvoyFilterAdmissionControl defines the Envoy HTTP admission control filter.
+ EnvoyFilterAdmissionControl EnvoyFilter = "envoy.filters.http.admission_control"
+
// EnvoyFilterCORS defines the Envoy HTTP CORS filter.
EnvoyFilterCORS EnvoyFilter = "envoy.filters.http.cors"
diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go
index 22be0adbc7..70b352b1e7 100644
--- a/api/v1alpha1/zz_generated.deepcopy.go
+++ b/api/v1alpha1/zz_generated.deepcopy.go
@@ -204,6 +204,76 @@ func (in *ActiveHealthCheckPayload) DeepCopy() *ActiveHealthCheckPayload {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *AdmissionControl) DeepCopyInto(out *AdmissionControl) {
+ *out = *in
+ if in.SamplingWindow != nil {
+ in, out := &in.SamplingWindow, &out.SamplingWindow
+ *out = new(v1.Duration)
+ **out = **in
+ }
+ if in.SuccessRateThreshold != nil {
+ in, out := &in.SuccessRateThreshold, &out.SuccessRateThreshold
+ *out = new(uint32)
+ **out = **in
+ }
+ if in.Aggression != nil {
+ in, out := &in.Aggression, &out.Aggression
+ *out = new(uint32)
+ **out = **in
+ }
+ if in.RPSThreshold != nil {
+ in, out := &in.RPSThreshold, &out.RPSThreshold
+ *out = new(uint32)
+ **out = **in
+ }
+ if in.MaxRejectionProbability != nil {
+ in, out := &in.MaxRejectionProbability, &out.MaxRejectionProbability
+ *out = new(uint32)
+ **out = **in
+ }
+ if in.SuccessCriteria != nil {
+ in, out := &in.SuccessCriteria, &out.SuccessCriteria
+ *out = new(AdmissionControlSuccessCriteria)
+ (*in).DeepCopyInto(*out)
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AdmissionControl.
+func (in *AdmissionControl) DeepCopy() *AdmissionControl {
+ if in == nil {
+ return nil
+ }
+ out := new(AdmissionControl)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *AdmissionControlSuccessCriteria) DeepCopyInto(out *AdmissionControlSuccessCriteria) {
+ *out = *in
+ if in.HTTP != nil {
+ in, out := &in.HTTP, &out.HTTP
+ *out = new(HTTPSuccessCriteria)
+ (*in).DeepCopyInto(*out)
+ }
+ if in.GRPC != nil {
+ in, out := &in.GRPC, &out.GRPC
+ *out = new(GRPCSuccessCriteria)
+ (*in).DeepCopyInto(*out)
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AdmissionControlSuccessCriteria.
+func (in *AdmissionControlSuccessCriteria) DeepCopy() *AdmissionControlSuccessCriteria {
+ if in == nil {
+ return nil
+ }
+ out := new(AdmissionControlSuccessCriteria)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Authorization) DeepCopyInto(out *Authorization) {
*out = *in
@@ -738,6 +808,11 @@ func (in *BackendTrafficPolicySpec) DeepCopyInto(out *BackendTrafficPolicySpec)
*out = new(FaultInjection)
(*in).DeepCopyInto(*out)
}
+ if in.AdmissionControl != nil {
+ in, out := &in.AdmissionControl, &out.AdmissionControl
+ *out = new(AdmissionControl)
+ (*in).DeepCopyInto(*out)
+ }
if in.UseClientProtocol != nil {
in, out := &in.UseClientProtocol, &out.UseClientProtocol
*out = new(bool)
@@ -3681,6 +3756,26 @@ func (in *GRPCSettings) DeepCopy() *GRPCSettings {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *GRPCSuccessCriteria) DeepCopyInto(out *GRPCSuccessCriteria) {
+ *out = *in
+ if in.GRPCSuccessStatus != nil {
+ in, out := &in.GRPCSuccessStatus, &out.GRPCSuccessStatus
+ *out = make([]GRPCSuccessCode, len(*in))
+ copy(*out, *in)
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GRPCSuccessCriteria.
+func (in *GRPCSuccessCriteria) DeepCopy() *GRPCSuccessCriteria {
+ if in == nil {
+ return nil
+ }
+ out := new(GRPCSuccessCriteria)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Gateway) DeepCopyInto(out *Gateway) {
*out = *in
@@ -4412,6 +4507,26 @@ func (in *HTTPRouteMatchFilter) DeepCopy() *HTTPRouteMatchFilter {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *HTTPSuccessCriteria) DeepCopyInto(out *HTTPSuccessCriteria) {
+ *out = *in
+ if in.HTTPSuccessStatus != nil {
+ in, out := &in.HTTPSuccessStatus, &out.HTTPSuccessStatus
+ *out = make([]HTTPStatus, len(*in))
+ copy(*out, *in)
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPSuccessCriteria.
+func (in *HTTPSuccessCriteria) DeepCopy() *HTTPSuccessCriteria {
+ if in == nil {
+ return nil
+ }
+ out := new(HTTPSuccessCriteria)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *HTTPTimeout) DeepCopyInto(out *HTTPTimeout) {
*out = *in
diff --git a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml
index 0bdaea3f45..0743db8b4b 100644
--- a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml
+++ b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml
@@ -50,6 +50,110 @@ spec:
spec:
description: spec defines the desired state of BackendTrafficPolicy.
properties:
+ admissionControl:
+ description: |-
+ AdmissionControl defines the admission control policy to be applied. This configuration
+ probabilistically rejects requests based on the success rate of previous requests in a
+ configurable sliding time window.
+ properties:
+ aggression:
+ description: |-
+ Aggression controls the rejection probability curve. A value of 1 means a linear
+ increase in rejection probability as the success rate decreases. Higher values
+ result in more aggressive rejection at higher success rates.
+ Envoy requires aggression to be greater than 0 and clamps values below 1 to 1.
+ Defaults to 1 if not specified.
+ format: int32
+ minimum: 1
+ type: integer
+ maxRejectionProbability:
+ description: |-
+ MaxRejectionProbability represents the upper limit of the rejection probability,
+ expressed as a percentage in the range [0, 100]. Defaults to 80 if not specified.
+ format: int32
+ maximum: 100
+ minimum: 0
+ type: integer
+ rpsThreshold:
+ description: |-
+ RPSThreshold defines the minimum requests per second below which requests will
+ pass through the filter without rejection. Defaults to 0 if not specified.
+ format: int32
+ minimum: 0
+ type: integer
+ samplingWindow:
+ description: |-
+ SamplingWindow defines the time window over which request success rates are calculated.
+ Defaults to 30s if not specified.
+ pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+ type: string
+ successCriteria:
+ description: SuccessCriteria defines what constitutes a successful
+ request for both HTTP and gRPC.
+ properties:
+ grpc:
+ description: GRPC defines success criteria for gRPC requests.
+ properties:
+ grpcSuccessStatus:
+ description: |-
+ GRPCSuccessStatus defines gRPC status codes that are considered successful.
+ Status codes are defined in https://github.com/grpc/grpc/blob/master/doc/statuscodes.md#status-codes-and-their-use-in-grpc.
+ items:
+ description: |-
+ GRPCSuccessCode defines gRPC status codes as defined in
+ https://github.com/grpc/grpc/blob/master/doc/statuscodes.md#status-codes-and-their-use-in-grpc.
+ enum:
+ - Ok
+ - Cancelled
+ - Unknown
+ - InvalidArgument
+ - DeadlineExceeded
+ - NotFound
+ - AlreadyExists
+ - PermissionDenied
+ - ResourceExhausted
+ - FailedPrecondition
+ - Aborted
+ - OutOfRange
+ - Unimplemented
+ - Internal
+ - Unavailable
+ - DataLoss
+ - Unauthenticated
+ type: string
+ type: array
+ type: object
+ http:
+ description: HTTP defines success criteria for HTTP requests.
+ properties:
+ httpSuccessStatus:
+ description: HTTPSuccessStatus defines HTTP status codes
+ that are considered successful.
+ items:
+ description: HTTPStatus defines the http status code.
+ maximum: 599
+ minimum: 100
+ type: integer
+ type: array
+ type: object
+ type: object
+ successRateThreshold:
+ description: |-
+ SuccessRateThreshold is the lowest request success rate, as a percentage in the
+ range [1, 100], at which the filter will not reject requests. Defaults to 95 if
+ not specified. Envoy rejects values below 1%, so values lower than 1 are not allowed.
+ format: int32
+ maximum: 100
+ minimum: 1
+ type: integer
+ type: object
+ x-kubernetes-validations:
+ - message: successRateThreshold must be between 1 and 100
+ rule: '!has(self.successRateThreshold) || (self.successRateThreshold
+ >= 1 && self.successRateThreshold <= 100)'
+ - message: maxRejectionProbability must be between 0 and 100
+ rule: '!has(self.maxRejectionProbability) || (self.maxRejectionProbability
+ >= 0 && self.maxRejectionProbability <= 100)'
circuitBreaker:
description: |-
Circuit Breaker settings for the upstream connections and requests.
diff --git a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyproxies.yaml b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyproxies.yaml
index 124e867bc2..d99401e8ad 100644
--- a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyproxies.yaml
+++ b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyproxies.yaml
@@ -476,6 +476,7 @@ spec:
- envoy.filters.http.custom_response
- envoy.filters.http.health_check
- envoy.filters.http.fault
+ - envoy.filters.http.admission_control
- envoy.filters.http.cors
- envoy.filters.http.header_mutation
- envoy.filters.http.ext_authz
@@ -507,6 +508,7 @@ spec:
- envoy.filters.http.custom_response
- envoy.filters.http.health_check
- envoy.filters.http.fault
+ - envoy.filters.http.admission_control
- envoy.filters.http.cors
- envoy.filters.http.header_mutation
- envoy.filters.http.ext_authz
@@ -536,6 +538,7 @@ spec:
- envoy.filters.http.custom_response
- envoy.filters.http.health_check
- envoy.filters.http.fault
+ - envoy.filters.http.admission_control
- envoy.filters.http.cors
- envoy.filters.http.header_mutation
- envoy.filters.http.ext_authz
diff --git a/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml b/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml
index 064085ea95..740a6f2bb4 100644
--- a/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml
+++ b/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml
@@ -49,6 +49,110 @@ spec:
spec:
description: spec defines the desired state of BackendTrafficPolicy.
properties:
+ admissionControl:
+ description: |-
+ AdmissionControl defines the admission control policy to be applied. This configuration
+ probabilistically rejects requests based on the success rate of previous requests in a
+ configurable sliding time window.
+ properties:
+ aggression:
+ description: |-
+ Aggression controls the rejection probability curve. A value of 1 means a linear
+ increase in rejection probability as the success rate decreases. Higher values
+ result in more aggressive rejection at higher success rates.
+ Envoy requires aggression to be greater than 0 and clamps values below 1 to 1.
+ Defaults to 1 if not specified.
+ format: int32
+ minimum: 1
+ type: integer
+ maxRejectionProbability:
+ description: |-
+ MaxRejectionProbability represents the upper limit of the rejection probability,
+ expressed as a percentage in the range [0, 100]. Defaults to 80 if not specified.
+ format: int32
+ maximum: 100
+ minimum: 0
+ type: integer
+ rpsThreshold:
+ description: |-
+ RPSThreshold defines the minimum requests per second below which requests will
+ pass through the filter without rejection. Defaults to 0 if not specified.
+ format: int32
+ minimum: 0
+ type: integer
+ samplingWindow:
+ description: |-
+ SamplingWindow defines the time window over which request success rates are calculated.
+ Defaults to 30s if not specified.
+ pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+ type: string
+ successCriteria:
+ description: SuccessCriteria defines what constitutes a successful
+ request for both HTTP and gRPC.
+ properties:
+ grpc:
+ description: GRPC defines success criteria for gRPC requests.
+ properties:
+ grpcSuccessStatus:
+ description: |-
+ GRPCSuccessStatus defines gRPC status codes that are considered successful.
+ Status codes are defined in https://github.com/grpc/grpc/blob/master/doc/statuscodes.md#status-codes-and-their-use-in-grpc.
+ items:
+ description: |-
+ GRPCSuccessCode defines gRPC status codes as defined in
+ https://github.com/grpc/grpc/blob/master/doc/statuscodes.md#status-codes-and-their-use-in-grpc.
+ enum:
+ - Ok
+ - Cancelled
+ - Unknown
+ - InvalidArgument
+ - DeadlineExceeded
+ - NotFound
+ - AlreadyExists
+ - PermissionDenied
+ - ResourceExhausted
+ - FailedPrecondition
+ - Aborted
+ - OutOfRange
+ - Unimplemented
+ - Internal
+ - Unavailable
+ - DataLoss
+ - Unauthenticated
+ type: string
+ type: array
+ type: object
+ http:
+ description: HTTP defines success criteria for HTTP requests.
+ properties:
+ httpSuccessStatus:
+ description: HTTPSuccessStatus defines HTTP status codes
+ that are considered successful.
+ items:
+ description: HTTPStatus defines the http status code.
+ maximum: 599
+ minimum: 100
+ type: integer
+ type: array
+ type: object
+ type: object
+ successRateThreshold:
+ description: |-
+ SuccessRateThreshold is the lowest request success rate, as a percentage in the
+ range [1, 100], at which the filter will not reject requests. Defaults to 95 if
+ not specified. Envoy rejects values below 1%, so values lower than 1 are not allowed.
+ format: int32
+ maximum: 100
+ minimum: 1
+ type: integer
+ type: object
+ x-kubernetes-validations:
+ - message: successRateThreshold must be between 1 and 100
+ rule: '!has(self.successRateThreshold) || (self.successRateThreshold
+ >= 1 && self.successRateThreshold <= 100)'
+ - message: maxRejectionProbability must be between 0 and 100
+ rule: '!has(self.maxRejectionProbability) || (self.maxRejectionProbability
+ >= 0 && self.maxRejectionProbability <= 100)'
circuitBreaker:
description: |-
Circuit Breaker settings for the upstream connections and requests.
diff --git a/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml b/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml
index 833448d41c..9bb6ac8fc4 100644
--- a/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml
+++ b/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml
@@ -475,6 +475,7 @@ spec:
- envoy.filters.http.custom_response
- envoy.filters.http.health_check
- envoy.filters.http.fault
+ - envoy.filters.http.admission_control
- envoy.filters.http.cors
- envoy.filters.http.header_mutation
- envoy.filters.http.ext_authz
@@ -506,6 +507,7 @@ spec:
- envoy.filters.http.custom_response
- envoy.filters.http.health_check
- envoy.filters.http.fault
+ - envoy.filters.http.admission_control
- envoy.filters.http.cors
- envoy.filters.http.header_mutation
- envoy.filters.http.ext_authz
@@ -535,6 +537,7 @@ spec:
- envoy.filters.http.custom_response
- envoy.filters.http.health_check
- envoy.filters.http.fault
+ - envoy.filters.http.admission_control
- envoy.filters.http.cors
- envoy.filters.http.header_mutation
- envoy.filters.http.ext_authz
diff --git a/examples/kubernetes/admission-control.yaml b/examples/kubernetes/admission-control.yaml
new file mode 100644
index 0000000000..bed213b278
--- /dev/null
+++ b/examples/kubernetes/admission-control.yaml
@@ -0,0 +1,62 @@
+# 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.
+
+apiVersion: gateway.envoyproxy.io/v1alpha1
+kind: BackendTrafficPolicy
+metadata:
+ name: admission-control-policy
+ namespace: default
+spec:
+ targetRef:
+ group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ name: example-route
+ admissionControl:
+ samplingWindow: 30s
+ successRateThreshold: 95
+ aggression: 1
+ rpsThreshold: 5
+ maxRejectionProbability: 80
+ successCriteria:
+ http:
+ httpSuccessStatus:
+ - 200
+ - 201
+ - 204
+ grpc:
+ grpcSuccessStatus:
+ - Ok
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ name: example-route
+ namespace: default
+spec:
+ parentRefs:
+ - name: eg
+ namespace: default
+ hostnames:
+ - "example.com"
+ rules:
+ - matches:
+ - path:
+ type: PathPrefix
+ value: /
+ backendRefs:
+ - name: backend-service
+ port: 80
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: backend-service
+ namespace: default
+spec:
+ ports:
+ - port: 80
+ targetPort: 8080
+ selector:
+ app: backend
diff --git a/internal/gatewayapi/backendtrafficpolicy.go b/internal/gatewayapi/backendtrafficpolicy.go
index 036b9de0ef..3dc017913e 100644
--- a/internal/gatewayapi/backendtrafficpolicy.go
+++ b/internal/gatewayapi/backendtrafficpolicy.go
@@ -1036,6 +1036,7 @@ func (t *Translator) buildTrafficFeatures(policy *egv1a1.BackendTrafficPolicy) (
hc *ir.HealthCheck
cb *ir.CircuitBreaker
fi *ir.FaultInjection
+ ac *ir.AdmissionControl
to *ir.Timeout
ka *ir.TCPKeepalive
rt *ir.Retry
@@ -1068,6 +1069,9 @@ func (t *Translator) buildTrafficFeatures(policy *egv1a1.BackendTrafficPolicy) (
if policy.Spec.FaultInjection != nil {
fi = t.buildFaultInjection(policy)
}
+ if policy.Spec.AdmissionControl != nil {
+ ac = t.buildAdmissionControl(policy)
+ }
if ka, err = buildTCPKeepAlive(&policy.Spec.ClusterSettings); err != nil {
err = perr.WithMessage(err, "TCPKeepalive")
errs = errors.Join(errs, err)
@@ -1125,6 +1129,7 @@ func (t *Translator) buildTrafficFeatures(policy *egv1a1.BackendTrafficPolicy) (
HealthCheck: hc,
CircuitBreaker: cb,
FaultInjection: fi,
+ AdmissionControl: ac,
TCPKeepalive: ka,
Retry: rt,
BackendConnection: bc,
@@ -1705,6 +1710,51 @@ func (t *Translator) buildFaultInjection(policy *egv1a1.BackendTrafficPolicy) *i
return fi
}
+func (t *Translator) buildAdmissionControl(policy *egv1a1.BackendTrafficPolicy) *ir.AdmissionControl {
+ if policy.Spec.AdmissionControl == nil {
+ return nil
+ }
+
+ ac := &ir.AdmissionControl{
+ SuccessRateThreshold: policy.Spec.AdmissionControl.SuccessRateThreshold,
+ Aggression: policy.Spec.AdmissionControl.Aggression,
+ RPSThreshold: policy.Spec.AdmissionControl.RPSThreshold,
+ MaxRejectionProbability: policy.Spec.AdmissionControl.MaxRejectionProbability,
+ }
+
+ if policy.Spec.AdmissionControl.SamplingWindow != nil {
+ if d, err := time.ParseDuration(string(*policy.Spec.AdmissionControl.SamplingWindow)); err == nil {
+ ac.SamplingWindow = &metav1.Duration{Duration: d}
+ }
+ }
+
+ if policy.Spec.AdmissionControl.SuccessCriteria != nil {
+ ac.SuccessCriteria = &ir.AdmissionControlSuccessCriteria{}
+
+ if policy.Spec.AdmissionControl.SuccessCriteria.HTTP != nil {
+ httpStatuses := make([]int32, len(policy.Spec.AdmissionControl.SuccessCriteria.HTTP.HTTPSuccessStatus))
+ for i, s := range policy.Spec.AdmissionControl.SuccessCriteria.HTTP.HTTPSuccessStatus {
+ httpStatuses[i] = int32(s)
+ }
+ ac.SuccessCriteria.HTTP = &ir.HTTPSuccessCriteria{
+ HTTPSuccessStatus: httpStatuses,
+ }
+ }
+
+ if policy.Spec.AdmissionControl.SuccessCriteria.GRPC != nil {
+ grpcStatuses := make([]string, len(policy.Spec.AdmissionControl.SuccessCriteria.GRPC.GRPCSuccessStatus))
+ for i, s := range policy.Spec.AdmissionControl.SuccessCriteria.GRPC.GRPCSuccessStatus {
+ grpcStatuses[i] = string(s)
+ }
+ ac.SuccessCriteria.GRPC = &ir.GRPCSuccessCriteria{
+ GRPCSuccessStatus: grpcStatuses,
+ }
+ }
+ }
+
+ return ac
+}
+
func makeIrStatusSet(in []egv1a1.HTTPStatus) []ir.HTTPStatus {
statusSet := sets.NewInt()
for _, r := range in {
diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-admission-control.in.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-admission-control.in.yaml
new file mode 100644
index 0000000000..0b3678fb3f
--- /dev/null
+++ b/internal/gatewayapi/testdata/backendtrafficpolicy-admission-control.in.yaml
@@ -0,0 +1,108 @@
+gateways:
+ - apiVersion: gateway.networking.k8s.io/v1
+ kind: Gateway
+ metadata:
+ namespace: envoy-gateway
+ name: gateway-1
+ spec:
+ gatewayClassName: envoy-gateway-class
+ listeners:
+ - name: http
+ protocol: HTTP
+ port: 80
+ allowedRoutes:
+ namespaces:
+ from: All
+ - apiVersion: gateway.networking.k8s.io/v1
+ kind: Gateway
+ metadata:
+ namespace: envoy-gateway
+ name: gateway-2
+ spec:
+ gatewayClassName: envoy-gateway-class
+ listeners:
+ - name: http
+ protocol: HTTP
+ port: 80
+ allowedRoutes:
+ namespaces:
+ from: All
+grpcRoutes:
+ - apiVersion: gateway.networking.k8s.io/v1alpha2
+ kind: GRPCRoute
+ metadata:
+ namespace: default
+ name: grpcroute-1
+ spec:
+ parentRefs:
+ - namespace: envoy-gateway
+ name: gateway-1
+ sectionName: http
+ rules:
+ - backendRefs:
+ - name: service-1
+ port: 8080
+httpRoutes:
+ - apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ namespace: default
+ name: httproute-1
+ spec:
+ hostnames:
+ - gateway.envoyproxy.io
+ parentRefs:
+ - namespace: envoy-gateway
+ name: gateway-2
+ sectionName: http
+ rules:
+ - matches:
+ - path:
+ value: "/"
+ backendRefs:
+ - name: service-1
+ port: 8080
+backendTrafficPolicies:
+ - apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: BackendTrafficPolicy
+ metadata:
+ namespace: envoy-gateway
+ name: policy-for-gateway
+ generation: 10
+ spec:
+ targetRef:
+ group: gateway.networking.k8s.io
+ kind: Gateway
+ name: gateway-1
+ admissionControl:
+ samplingWindow: 60s
+ successRateThreshold: 90
+ aggression: 2
+ rpsThreshold: 10
+ maxRejectionProbability: 70
+ successCriteria:
+ grpc:
+ grpcSuccessStatus:
+ - Ok
+ - Cancelled
+ - DeadlineExceeded
+ - apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: BackendTrafficPolicy
+ metadata:
+ namespace: default
+ name: policy-for-route
+ generation: 20
+ spec:
+ targetRef:
+ group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ name: httproute-1
+ admissionControl:
+ samplingWindow: 30s
+ successRateThreshold: 95
+ successCriteria:
+ http:
+ httpSuccessStatus:
+ - 200
+ - 201
+ - 204
diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-admission-control.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-admission-control.out.yaml
new file mode 100644
index 0000000000..7cad5fccb1
--- /dev/null
+++ b/internal/gatewayapi/testdata/backendtrafficpolicy-admission-control.out.yaml
@@ -0,0 +1,457 @@
+backendTrafficPolicies:
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: BackendTrafficPolicy
+ metadata:
+ generation: 20
+ name: policy-for-route
+ namespace: default
+ spec:
+ admissionControl:
+ samplingWindow: 30s
+ successCriteria:
+ http:
+ httpSuccessStatus:
+ - 200
+ - 201
+ - 204
+ successRateThreshold: 95
+ targetRef:
+ group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ name: httproute-1
+ status:
+ ancestors:
+ - ancestorRef:
+ group: gateway.networking.k8s.io
+ kind: Gateway
+ name: gateway-2
+ namespace: envoy-gateway
+ sectionName: http
+ conditions:
+ - lastTransitionTime: null
+ message: Policy has been accepted.
+ observedGeneration: 20
+ reason: Accepted
+ status: "True"
+ type: Accepted
+ - lastTransitionTime: null
+ message: spec.targetRef is deprecated, use spec.targetRefs instead
+ observedGeneration: 20
+ reason: DeprecatedField
+ status: "True"
+ type: Warning
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: BackendTrafficPolicy
+ metadata:
+ generation: 10
+ name: policy-for-gateway
+ namespace: envoy-gateway
+ spec:
+ admissionControl:
+ aggression: 2
+ maxRejectionProbability: 70
+ rpsThreshold: 10
+ samplingWindow: 60s
+ successCriteria:
+ grpc:
+ grpcSuccessStatus:
+ - Ok
+ - Cancelled
+ - DeadlineExceeded
+ successRateThreshold: 90
+ targetRef:
+ group: gateway.networking.k8s.io
+ kind: Gateway
+ name: gateway-1
+ status:
+ ancestors:
+ - ancestorRef:
+ group: gateway.networking.k8s.io
+ kind: Gateway
+ name: gateway-1
+ namespace: envoy-gateway
+ conditions:
+ - lastTransitionTime: null
+ message: Policy has been accepted.
+ observedGeneration: 10
+ reason: Accepted
+ status: "True"
+ type: Accepted
+ - lastTransitionTime: null
+ message: spec.targetRef is deprecated, use spec.targetRefs instead
+ observedGeneration: 10
+ reason: DeprecatedField
+ status: "True"
+ type: Warning
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
+gateways:
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: Gateway
+ metadata:
+ name: gateway-1
+ namespace: envoy-gateway
+ spec:
+ gatewayClassName: envoy-gateway-class
+ listeners:
+ - allowedRoutes:
+ namespaces:
+ from: All
+ name: http
+ port: 80
+ protocol: HTTP
+ 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: http
+ supportedKinds:
+ - group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ - group: gateway.networking.k8s.io
+ kind: GRPCRoute
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: Gateway
+ metadata:
+ name: gateway-2
+ namespace: envoy-gateway
+ spec:
+ gatewayClassName: envoy-gateway-class
+ listeners:
+ - allowedRoutes:
+ namespaces:
+ from: All
+ name: http
+ port: 80
+ protocol: HTTP
+ 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: http
+ supportedKinds:
+ - group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ - group: gateway.networking.k8s.io
+ kind: GRPCRoute
+grpcRoutes:
+- apiVersion: gateway.networking.k8s.io/v1alpha2
+ kind: GRPCRoute
+ metadata:
+ name: grpcroute-1
+ namespace: default
+ spec:
+ parentRefs:
+ - name: gateway-1
+ namespace: envoy-gateway
+ sectionName: http
+ 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: envoy-gateway
+ sectionName: http
+httpRoutes:
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ name: httproute-1
+ namespace: default
+ spec:
+ hostnames:
+ - gateway.envoyproxy.io
+ parentRefs:
+ - name: gateway-2
+ namespace: envoy-gateway
+ sectionName: http
+ rules:
+ - backendRefs:
+ - name: service-1
+ port: 8080
+ matches:
+ - path:
+ value: /
+ 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-2
+ namespace: envoy-gateway
+ sectionName: http
+infraIR:
+ envoy-gateway/gateway-1:
+ proxy:
+ listeners:
+ - name: envoy-gateway/gateway-1/http
+ ports:
+ - containerPort: 10080
+ name: http-80
+ protocol: HTTP
+ servicePort: 80
+ metadata:
+ labels:
+ gateway.envoyproxy.io/owning-gateway-name: gateway-1
+ gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway
+ ownerReference:
+ kind: GatewayClass
+ name: envoy-gateway-class
+ name: envoy-gateway/gateway-1
+ namespace: envoy-gateway-system
+ envoy-gateway/gateway-2:
+ proxy:
+ listeners:
+ - name: envoy-gateway/gateway-2/http
+ ports:
+ - containerPort: 10080
+ name: http-80
+ protocol: HTTP
+ servicePort: 80
+ metadata:
+ labels:
+ gateway.envoyproxy.io/owning-gateway-name: gateway-2
+ gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway
+ ownerReference:
+ kind: GatewayClass
+ name: envoy-gateway-class
+ name: envoy-gateway/gateway-2
+ namespace: envoy-gateway-system
+xdsIR:
+ envoy-gateway/gateway-1:
+ accessLog:
+ json:
+ - path: /dev/stdout
+ globalResources:
+ proxyServiceCluster:
+ metadata:
+ kind: Service
+ name: envoy-envoy-gateway-gateway-1-196ae069
+ namespace: envoy-gateway-system
+ sectionName: "8080"
+ name: envoy-gateway/gateway-1
+ settings:
+ - addressType: IP
+ endpoints:
+ - host: 7.6.5.4
+ port: 8080
+ zone: zone1
+ metadata:
+ kind: Service
+ name: envoy-envoy-gateway-gateway-1-196ae069
+ namespace: envoy-gateway-system
+ sectionName: "8080"
+ name: envoy-gateway/gateway-1
+ protocol: TCP
+ http:
+ - address: 0.0.0.0
+ externalPort: 80
+ grpc:
+ enableGRPCStats: true
+ enableGRPCWeb: true
+ hostnames:
+ - '*'
+ metadata:
+ kind: Gateway
+ name: gateway-1
+ namespace: envoy-gateway
+ sectionName: http
+ name: envoy-gateway/gateway-1/http
+ path:
+ escapedSlashesAction: UnescapeAndRedirect
+ mergeSlashes: true
+ port: 10080
+ routes:
+ - destination:
+ metadata:
+ kind: GRPCRoute
+ name: grpcroute-1
+ namespace: default
+ name: grpcroute/default/grpcroute-1/rule/0
+ settings:
+ - addressType: IP
+ endpoints:
+ - host: 7.7.7.7
+ port: 8080
+ metadata:
+ kind: Service
+ name: service-1
+ namespace: default
+ sectionName: "8080"
+ name: grpcroute/default/grpcroute-1/rule/0/backend/0
+ protocol: GRPC
+ weight: 1
+ hostname: '*'
+ isHTTP2: true
+ metadata:
+ kind: GRPCRoute
+ name: grpcroute-1
+ namespace: default
+ policies:
+ - kind: BackendTrafficPolicy
+ name: policy-for-gateway
+ namespace: envoy-gateway
+ name: grpcroute/default/grpcroute-1/rule/0/match/-1/*
+ traffic:
+ admissionControl:
+ aggression: 2
+ maxRejectionProbability: 70
+ rpsThreshold: 10
+ samplingWindow: 1m0s
+ successCriteria:
+ grpc:
+ grpcSuccessStatus:
+ - Ok
+ - Cancelled
+ - DeadlineExceeded
+ successRateThreshold: 90
+ readyListener:
+ address: 0.0.0.0
+ ipFamily: IPv4
+ path: /ready
+ port: 19003
+ envoy-gateway/gateway-2:
+ accessLog:
+ json:
+ - path: /dev/stdout
+ globalResources:
+ proxyServiceCluster:
+ metadata:
+ kind: Service
+ name: envoy-envoy-gateway-gateway-2-4a0e4eb9
+ namespace: envoy-gateway-system
+ sectionName: "8080"
+ name: envoy-gateway/gateway-2
+ settings:
+ - addressType: IP
+ endpoints:
+ - host: 7.6.5.4
+ port: 8080
+ zone: zone1
+ metadata:
+ kind: Service
+ name: envoy-envoy-gateway-gateway-2-4a0e4eb9
+ namespace: envoy-gateway-system
+ sectionName: "8080"
+ name: envoy-gateway/gateway-2
+ protocol: TCP
+ http:
+ - address: 0.0.0.0
+ externalPort: 80
+ hostnames:
+ - '*'
+ metadata:
+ kind: Gateway
+ name: gateway-2
+ namespace: envoy-gateway
+ sectionName: http
+ name: envoy-gateway/gateway-2/http
+ path:
+ escapedSlashesAction: UnescapeAndRedirect
+ mergeSlashes: true
+ port: 10080
+ routes:
+ - destination:
+ metadata:
+ kind: HTTPRoute
+ name: httproute-1
+ namespace: default
+ name: httproute/default/httproute-1/rule/0
+ settings:
+ - addressType: IP
+ endpoints:
+ - host: 7.7.7.7
+ port: 8080
+ metadata:
+ kind: Service
+ name: service-1
+ namespace: default
+ sectionName: "8080"
+ name: httproute/default/httproute-1/rule/0/backend/0
+ protocol: HTTP
+ weight: 1
+ hostname: gateway.envoyproxy.io
+ isHTTP2: false
+ metadata:
+ kind: HTTPRoute
+ name: httproute-1
+ namespace: default
+ policies:
+ - kind: BackendTrafficPolicy
+ name: policy-for-route
+ namespace: default
+ name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io
+ pathMatch:
+ distinct: false
+ name: ""
+ prefix: /
+ traffic:
+ admissionControl:
+ samplingWindow: 30s
+ successCriteria:
+ http:
+ httpSuccessStatus:
+ - 200
+ - 201
+ - 204
+ successRateThreshold: 95
+ readyListener:
+ address: 0.0.0.0
+ ipFamily: IPv4
+ path: /ready
+ port: 19003
diff --git a/internal/ir/xds.go b/internal/ir/xds.go
index 1e778399b1..160b1d7aa5 100644
--- a/internal/ir/xds.go
+++ b/internal/ir/xds.go
@@ -1040,6 +1040,8 @@ type TrafficFeatures struct {
HealthCheck *HealthCheck `json:"healthCheck,omitempty" yaml:"healthCheck,omitempty"`
// FaultInjection defines the schema for injecting faults into HTTP requests.
FaultInjection *FaultInjection `json:"faultInjection,omitempty" yaml:"faultInjection,omitempty"`
+ // AdmissionControl defines the schema for admission control based on success rate.
+ AdmissionControl *AdmissionControl `json:"admissionControl,omitempty" yaml:"admissionControl,omitempty"`
// Circuit Breaker Settings
CircuitBreaker *CircuitBreaker `json:"circuitBreaker,omitempty" yaml:"circuitBreaker,omitempty"`
// Request and connection timeout settings
@@ -1694,6 +1696,53 @@ type FaultInjectionAbort struct {
Percentage *float32 `json:"percentage,omitempty" yaml:"percentage,omitempty"`
}
+// AdmissionControl defines the schema for admission control based on success rate.
+//
+// +k8s:deepcopy-gen=true
+type AdmissionControl struct {
+ // SamplingWindow defines the time window over which request success rates are calculated.
+ SamplingWindow *metav1.Duration `json:"samplingWindow,omitempty" yaml:"samplingWindow,omitempty"`
+ // SuccessRateThreshold is the lowest request success rate, as a percentage in the
+ // range [1, 100], at which the filter will not reject requests.
+ SuccessRateThreshold *uint32 `json:"successRateThreshold,omitempty" yaml:"successRateThreshold,omitempty"`
+ // Aggression controls the rejection probability curve.
+ Aggression *uint32 `json:"aggression,omitempty" yaml:"aggression,omitempty"`
+ // RPSThreshold defines the minimum requests per second below which requests will
+ // pass through the filter without rejection.
+ RPSThreshold *uint32 `json:"rpsThreshold,omitempty" yaml:"rpsThreshold,omitempty"`
+ // MaxRejectionProbability represents the upper limit of the rejection probability,
+ // as a percentage in the range [0, 100].
+ MaxRejectionProbability *uint32 `json:"maxRejectionProbability,omitempty" yaml:"maxRejectionProbability,omitempty"`
+ // SuccessCriteria defines what constitutes a successful request for both HTTP and gRPC.
+ SuccessCriteria *AdmissionControlSuccessCriteria `json:"successCriteria,omitempty" yaml:"successCriteria,omitempty"`
+}
+
+// AdmissionControlSuccessCriteria defines the criteria for determining successful requests.
+//
+// +k8s:deepcopy-gen=true
+type AdmissionControlSuccessCriteria struct {
+ // HTTP defines success criteria for HTTP requests.
+ HTTP *HTTPSuccessCriteria `json:"http,omitempty" yaml:"http,omitempty"`
+ // GRPC defines success criteria for gRPC requests.
+ GRPC *GRPCSuccessCriteria `json:"grpc,omitempty" yaml:"grpc,omitempty"`
+}
+
+// HTTPSuccessCriteria defines success criteria for HTTP requests.
+//
+// +k8s:deepcopy-gen=true
+type HTTPSuccessCriteria struct {
+ // HTTPSuccessStatus defines HTTP status codes that are considered successful.
+ HTTPSuccessStatus []int32 `json:"httpSuccessStatus,omitempty" yaml:"httpSuccessStatus,omitempty"`
+}
+
+// GRPCSuccessCriteria defines success criteria for gRPC requests.
+//
+// +k8s:deepcopy-gen=true
+type GRPCSuccessCriteria struct {
+ // GRPCSuccessStatus defines gRPC status codes that are considered successful (string enum names).
+ GRPCSuccessStatus []string `json:"grpcSuccessStatus,omitempty" yaml:"grpcSuccessStatus,omitempty"`
+}
+
// MirrorPolicy specifies a destination to mirror traffic in addition
// to the original destination
//
diff --git a/internal/ir/zz_generated.deepcopy.go b/internal/ir/zz_generated.deepcopy.go
index 0ce50fbd5a..3c27050b3f 100644
--- a/internal/ir/zz_generated.deepcopy.go
+++ b/internal/ir/zz_generated.deepcopy.go
@@ -299,6 +299,76 @@ func (in *AddHeader) DeepCopy() *AddHeader {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *AdmissionControl) DeepCopyInto(out *AdmissionControl) {
+ *out = *in
+ if in.SamplingWindow != nil {
+ in, out := &in.SamplingWindow, &out.SamplingWindow
+ *out = new(metav1.Duration)
+ **out = **in
+ }
+ if in.SuccessRateThreshold != nil {
+ in, out := &in.SuccessRateThreshold, &out.SuccessRateThreshold
+ *out = new(uint32)
+ **out = **in
+ }
+ if in.Aggression != nil {
+ in, out := &in.Aggression, &out.Aggression
+ *out = new(uint32)
+ **out = **in
+ }
+ if in.RPSThreshold != nil {
+ in, out := &in.RPSThreshold, &out.RPSThreshold
+ *out = new(uint32)
+ **out = **in
+ }
+ if in.MaxRejectionProbability != nil {
+ in, out := &in.MaxRejectionProbability, &out.MaxRejectionProbability
+ *out = new(uint32)
+ **out = **in
+ }
+ if in.SuccessCriteria != nil {
+ in, out := &in.SuccessCriteria, &out.SuccessCriteria
+ *out = new(AdmissionControlSuccessCriteria)
+ (*in).DeepCopyInto(*out)
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AdmissionControl.
+func (in *AdmissionControl) DeepCopy() *AdmissionControl {
+ if in == nil {
+ return nil
+ }
+ out := new(AdmissionControl)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *AdmissionControlSuccessCriteria) DeepCopyInto(out *AdmissionControlSuccessCriteria) {
+ *out = *in
+ if in.HTTP != nil {
+ in, out := &in.HTTP, &out.HTTP
+ *out = new(HTTPSuccessCriteria)
+ (*in).DeepCopyInto(*out)
+ }
+ if in.GRPC != nil {
+ in, out := &in.GRPC, &out.GRPC
+ *out = new(GRPCSuccessCriteria)
+ (*in).DeepCopyInto(*out)
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AdmissionControlSuccessCriteria.
+func (in *AdmissionControlSuccessCriteria) DeepCopy() *AdmissionControlSuccessCriteria {
+ if in == nil {
+ return nil
+ }
+ out := new(AdmissionControlSuccessCriteria)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Authorization) DeepCopyInto(out *Authorization) {
*out = *in
@@ -1748,6 +1818,26 @@ func (in *GRPCSettings) DeepCopy() *GRPCSettings {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *GRPCSuccessCriteria) DeepCopyInto(out *GRPCSuccessCriteria) {
+ *out = *in
+ if in.GRPCSuccessStatus != nil {
+ in, out := &in.GRPCSuccessStatus, &out.GRPCSuccessStatus
+ *out = make([]string, len(*in))
+ copy(*out, *in)
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GRPCSuccessCriteria.
+func (in *GRPCSuccessCriteria) DeepCopy() *GRPCSuccessCriteria {
+ if in == nil {
+ return nil
+ }
+ out := new(GRPCSuccessCriteria)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *GeoIPMaxMindProvider) DeepCopyInto(out *GeoIPMaxMindProvider) {
*out = *in
@@ -2403,6 +2493,26 @@ func (in *HTTPRoute) DeepCopy() *HTTPRoute {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *HTTPSuccessCriteria) DeepCopyInto(out *HTTPSuccessCriteria) {
+ *out = *in
+ if in.HTTPSuccessStatus != nil {
+ in, out := &in.HTTPSuccessStatus, &out.HTTPSuccessStatus
+ *out = make([]int32, len(*in))
+ copy(*out, *in)
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPSuccessCriteria.
+func (in *HTTPSuccessCriteria) DeepCopy() *HTTPSuccessCriteria {
+ if in == nil {
+ return nil
+ }
+ out := new(HTTPSuccessCriteria)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *HTTPTimeout) DeepCopyInto(out *HTTPTimeout) {
*out = *in
@@ -4874,6 +4984,11 @@ func (in *TrafficFeatures) DeepCopyInto(out *TrafficFeatures) {
*out = new(FaultInjection)
(*in).DeepCopyInto(*out)
}
+ if in.AdmissionControl != nil {
+ in, out := &in.AdmissionControl, &out.AdmissionControl
+ *out = new(AdmissionControl)
+ (*in).DeepCopyInto(*out)
+ }
if in.CircuitBreaker != nil {
in, out := &in.CircuitBreaker, &out.CircuitBreaker
*out = new(CircuitBreaker)
diff --git a/internal/xds/translator/admission_control.go b/internal/xds/translator/admission_control.go
new file mode 100644
index 0000000000..e32fda1e31
--- /dev/null
+++ b/internal/xds/translator/admission_control.go
@@ -0,0 +1,150 @@
+// 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.
+
+package translator
+
+import (
+ "errors"
+
+ corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
+ admissioncontrolv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/admission_control/v3"
+ hcmv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
+ typev3 "github.com/envoyproxy/go-control-plane/envoy/type/v3"
+ "google.golang.org/protobuf/types/known/durationpb"
+ "google.golang.org/protobuf/types/known/wrapperspb"
+
+ egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1"
+ "github.com/envoyproxy/gateway/internal/ir"
+ "github.com/envoyproxy/gateway/internal/utils/proto"
+)
+
+// buildUpstreamAdmissionControlFilter builds an admission control filter for use
+// as an upstream HTTP filter on a cluster. Envoy's admission_control filter is a
+// "dual filter" that supports both the downstream (HCM) and upstream (cluster)
+// extension categories, but does not support per-route typedPerFilterConfig.
+// Placing it as an upstream filter gives per-cluster success-rate tracking.
+func buildUpstreamAdmissionControlFilter(ac *ir.AdmissionControl) (*hcmv3.HttpFilter, error) {
+ config, err := buildAdmissionControlConfig(ac)
+ if err != nil {
+ return nil, err
+ }
+
+ configAny, err := proto.ToAnyWithValidation(config)
+ if err != nil {
+ return nil, err
+ }
+
+ return &hcmv3.HttpFilter{
+ Name: string(egv1a1.EnvoyFilterAdmissionControl),
+ ConfigType: &hcmv3.HttpFilter_TypedConfig{
+ TypedConfig: configAny,
+ },
+ }, nil
+}
+
+// buildAdmissionControlConfig builds the admission control configuration from the IR.
+func buildAdmissionControlConfig(admissionControl *ir.AdmissionControl) (*admissioncontrolv3.AdmissionControl, error) {
+ if admissionControl == nil {
+ return nil, errors.New("admissionControl cannot be nil")
+ }
+
+ // The filter is enabled whenever the policy is configured.
+ config := &admissioncontrolv3.AdmissionControl{
+ Enabled: &corev3.RuntimeFeatureFlag{
+ DefaultValue: &wrapperspb.BoolValue{Value: true},
+ },
+ }
+
+ if admissionControl.SamplingWindow != nil {
+ config.SamplingWindow = durationpb.New(admissionControl.SamplingWindow.Duration)
+ }
+
+ if admissionControl.SuccessRateThreshold != nil {
+ config.SrThreshold = &corev3.RuntimePercent{
+ DefaultValue: &typev3.Percent{Value: float64(*admissionControl.SuccessRateThreshold)},
+ }
+ }
+
+ if admissionControl.Aggression != nil {
+ config.Aggression = &corev3.RuntimeDouble{
+ DefaultValue: float64(*admissionControl.Aggression),
+ }
+ }
+
+ if admissionControl.RPSThreshold != nil {
+ config.RpsThreshold = &corev3.RuntimeUInt32{
+ DefaultValue: *admissionControl.RPSThreshold,
+ }
+ }
+
+ if admissionControl.MaxRejectionProbability != nil {
+ config.MaxRejectionProbability = &corev3.RuntimePercent{
+ DefaultValue: &typev3.Percent{Value: float64(*admissionControl.MaxRejectionProbability)},
+ }
+ }
+
+ successCriteria := &admissioncontrolv3.AdmissionControl_SuccessCriteria{}
+
+ // Set success criteria (part of EvaluationCriteria oneof)
+ if admissionControl.SuccessCriteria != nil {
+ // HTTP success criteria: each individual status code becomes a single-element range [code, code+1)
+ if admissionControl.SuccessCriteria.HTTP != nil && len(admissionControl.SuccessCriteria.HTTP.HTTPSuccessStatus) > 0 {
+ httpCriteria := &admissioncontrolv3.AdmissionControl_SuccessCriteria_HttpCriteria{}
+ for _, code := range admissionControl.SuccessCriteria.HTTP.HTTPSuccessStatus {
+ httpCriteria.HttpSuccessStatus = append(httpCriteria.HttpSuccessStatus, &typev3.Int32Range{
+ Start: code,
+ End: code + 1,
+ })
+ }
+ successCriteria.HttpCriteria = httpCriteria
+ }
+
+ if admissionControl.SuccessCriteria.GRPC != nil && len(admissionControl.SuccessCriteria.GRPC.GRPCSuccessStatus) > 0 {
+ grpcCriteria := &admissioncontrolv3.AdmissionControl_SuccessCriteria_GrpcCriteria{}
+ for _, status := range admissionControl.SuccessCriteria.GRPC.GRPCSuccessStatus {
+ if code, ok := grpcStatusCodeToUint32(status); ok {
+ grpcCriteria.GrpcSuccessStatus = append(grpcCriteria.GrpcSuccessStatus, code)
+ }
+ }
+ successCriteria.GrpcCriteria = grpcCriteria
+ }
+
+ }
+
+ // Always set EvaluationCriteria (required field)
+ config.EvaluationCriteria = &admissioncontrolv3.AdmissionControl_SuccessCriteria_{
+ SuccessCriteria: successCriteria,
+ }
+
+ return config, nil
+}
+
+// grpcStatusCodes maps a gRPC status code string name to its numeric value.
+// See https://github.com/grpc/grpc/blob/master/doc/statuscodes.md#status-codes-and-their-use-in-grpc
+var grpcStatusCodes = map[string]uint32{
+ "Ok": 0,
+ "Cancelled": 1,
+ "Unknown": 2,
+ "InvalidArgument": 3,
+ "DeadlineExceeded": 4,
+ "NotFound": 5,
+ "AlreadyExists": 6,
+ "PermissionDenied": 7,
+ "ResourceExhausted": 8,
+ "FailedPrecondition": 9,
+ "Aborted": 10,
+ "OutOfRange": 11,
+ "Unimplemented": 12,
+ "Internal": 13,
+ "Unavailable": 14,
+ "DataLoss": 15,
+ "Unauthenticated": 16,
+}
+
+// grpcStatusCodeToUint32 maps a gRPC status code string name to its numeric value.
+func grpcStatusCodeToUint32(name string) (uint32, bool) {
+ code, ok := grpcStatusCodes[name]
+ return code, ok
+}
diff --git a/internal/xds/translator/admission_control_test.go b/internal/xds/translator/admission_control_test.go
new file mode 100644
index 0000000000..4d99859b48
--- /dev/null
+++ b/internal/xds/translator/admission_control_test.go
@@ -0,0 +1,264 @@
+// 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.
+
+package translator
+
+import (
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+ egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1"
+ "github.com/envoyproxy/gateway/internal/ir"
+)
+
+func TestBuildAdmissionControlConfig(t *testing.T) {
+ tests := []struct {
+ name string
+ config *ir.AdmissionControl
+ wantErr bool
+ }{
+ {
+ name: "valid admission control config",
+ config: &ir.AdmissionControl{},
+ wantErr: false,
+ },
+ {
+ name: "nil config",
+ config: nil,
+ wantErr: true,
+ },
+ {
+ name: "empty config with defaults",
+ config: &ir.AdmissionControl{},
+ wantErr: false,
+ },
+ {
+ name: "full config with all fields",
+ config: &ir.AdmissionControl{
+ SamplingWindow: &metav1.Duration{
+ Duration: 30 * time.Second,
+ },
+ SuccessRateThreshold: new(uint32(90)),
+ Aggression: new(uint32(2)),
+ RPSThreshold: new(uint32(10)),
+ MaxRejectionProbability: new(uint32(80)),
+ SuccessCriteria: &ir.AdmissionControlSuccessCriteria{
+ HTTP: &ir.HTTPSuccessCriteria{
+ HTTPSuccessStatus: []int32{200, 201, 300, 301},
+ },
+ GRPC: &ir.GRPCSuccessCriteria{
+ GRPCSuccessStatus: []string{"Ok", "Cancelled"},
+ },
+ },
+ },
+ wantErr: false,
+ },
+ {
+ name: "config with only HTTP success criteria",
+ config: &ir.AdmissionControl{
+ SuccessCriteria: &ir.AdmissionControlSuccessCriteria{
+ HTTP: &ir.HTTPSuccessCriteria{
+ HTTPSuccessStatus: []int32{200, 201, 202},
+ },
+ },
+ },
+ wantErr: false,
+ },
+ {
+ name: "config with only gRPC success criteria",
+ config: &ir.AdmissionControl{
+ SuccessCriteria: &ir.AdmissionControlSuccessCriteria{
+ GRPC: &ir.GRPCSuccessCriteria{
+ GRPCSuccessStatus: []string{"Ok"},
+ },
+ },
+ },
+ wantErr: false,
+ },
+ {
+ name: "config with empty success criteria",
+ config: &ir.AdmissionControl{
+ SuccessCriteria: &ir.AdmissionControlSuccessCriteria{},
+ },
+ wantErr: false,
+ },
+ {
+ name: "config with custom sampling window",
+ config: &ir.AdmissionControl{
+ SamplingWindow: &metav1.Duration{
+ Duration: 120 * time.Second,
+ },
+ },
+ wantErr: false,
+ },
+ {
+ name: "config with min thresholds",
+ config: &ir.AdmissionControl{
+ SuccessRateThreshold: new(uint32(1)),
+ Aggression: new(uint32(1)),
+ RPSThreshold: new(uint32(0)),
+ MaxRejectionProbability: new(uint32(0)),
+ },
+ wantErr: false,
+ },
+ {
+ name: "config with max thresholds",
+ config: &ir.AdmissionControl{
+ SuccessRateThreshold: new(uint32(100)),
+ Aggression: new(uint32(10)),
+ RPSThreshold: new(uint32(1000)),
+ MaxRejectionProbability: new(uint32(100)),
+ },
+ wantErr: false,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := buildAdmissionControlConfig(tt.config)
+ if tt.wantErr {
+ require.Error(t, err)
+ return
+ }
+ require.NoError(t, err)
+ require.NotNil(t, got)
+
+ require.NotNil(t, got.Enabled)
+ require.NotNil(t, got.Enabled.DefaultValue)
+ assert.True(t, got.Enabled.DefaultValue.Value)
+
+ require.NotNil(t, got.EvaluationCriteria)
+ })
+ }
+}
+
+func TestBuildAdmissionControlConfigValues(t *testing.T) {
+ config := &ir.AdmissionControl{
+ SamplingWindow: &metav1.Duration{
+ Duration: 45 * time.Second,
+ },
+ SuccessRateThreshold: new(uint32(85)),
+ Aggression: new(uint32(2)),
+ RPSThreshold: new(uint32(20)),
+ MaxRejectionProbability: new(uint32(75)),
+ SuccessCriteria: &ir.AdmissionControlSuccessCriteria{
+ HTTP: &ir.HTTPSuccessCriteria{
+ HTTPSuccessStatus: []int32{200, 201, 202},
+ },
+ GRPC: &ir.GRPCSuccessCriteria{
+ GRPCSuccessStatus: []string{"Ok", "Cancelled", "Unknown"},
+ },
+ },
+ }
+
+ got, err := buildAdmissionControlConfig(config)
+ require.NoError(t, err)
+ require.NotNil(t, got)
+
+ assert.Equal(t, int64(45), got.SamplingWindow.Seconds)
+ assert.Equal(t, 85.0, got.SrThreshold.DefaultValue.Value)
+ assert.Equal(t, 2.0, got.Aggression.DefaultValue)
+ assert.Equal(t, uint32(20), got.RpsThreshold.DefaultValue)
+ assert.Equal(t, 75.0, got.MaxRejectionProbability.DefaultValue.Value)
+ require.NotNil(t, got.EvaluationCriteria)
+}
+
+func TestBuildAdmissionControlConfigDefaults(t *testing.T) {
+ config := &ir.AdmissionControl{}
+
+ got, err := buildAdmissionControlConfig(config)
+ require.NoError(t, err)
+ require.NotNil(t, got)
+
+ assert.Nil(t, got.SamplingWindow)
+ assert.Nil(t, got.SrThreshold)
+ assert.Nil(t, got.Aggression)
+ assert.Nil(t, got.RpsThreshold)
+ assert.Nil(t, got.MaxRejectionProbability)
+
+ require.NotNil(t, got.Enabled)
+ require.NotNil(t, got.EvaluationCriteria)
+}
+
+func TestBuildUpstreamAdmissionControlFilter(t *testing.T) {
+ tests := []struct {
+ name string
+ ac *ir.AdmissionControl
+ wantErr bool
+ }{
+ {
+ name: "nil config",
+ ac: nil,
+ wantErr: true,
+ },
+ {
+ name: "empty config",
+ ac: &ir.AdmissionControl{},
+ wantErr: false,
+ },
+ {
+ name: "full config",
+ ac: &ir.AdmissionControl{
+ SamplingWindow: &metav1.Duration{
+ Duration: 30 * time.Second,
+ },
+ SuccessRateThreshold: new(uint32(95)),
+ Aggression: new(uint32(1)),
+ RPSThreshold: new(uint32(5)),
+ MaxRejectionProbability: new(uint32(80)),
+ SuccessCriteria: &ir.AdmissionControlSuccessCriteria{
+ HTTP: &ir.HTTPSuccessCriteria{
+ HTTPSuccessStatus: []int32{200, 201},
+ },
+ },
+ },
+ wantErr: false,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ filter, err := buildUpstreamAdmissionControlFilter(tt.ac)
+ if tt.wantErr {
+ require.Error(t, err)
+ return
+ }
+ require.NoError(t, err)
+ require.NotNil(t, filter)
+ assert.Equal(t, string(egv1a1.EnvoyFilterAdmissionControl), filter.Name)
+ assert.NotNil(t, filter.ConfigType)
+ })
+ }
+}
+
+func TestGRPCStatusCodeToUint32(t *testing.T) {
+ tests := []struct {
+ name string
+ input string
+ want uint32
+ wantOk bool
+ }{
+ {name: "Ok", input: "Ok", want: 0, wantOk: true},
+ {name: "Cancelled", input: "Cancelled", want: 1, wantOk: true},
+ {name: "Unavailable", input: "Unavailable", want: 14, wantOk: true},
+ {name: "Unauthenticated", input: "Unauthenticated", want: 16, wantOk: true},
+ {name: "invalid code", input: "Invalid", want: 0, wantOk: false},
+ {name: "wrong case OK", input: "OK", want: 0, wantOk: false},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, ok := grpcStatusCodeToUint32(tt.input)
+ assert.Equal(t, tt.wantOk, ok)
+ if tt.wantOk {
+ assert.Equal(t, tt.want, got)
+ }
+ })
+ }
+}
diff --git a/internal/xds/translator/cluster.go b/internal/xds/translator/cluster.go
index 1659739985..40249b03bb 100644
--- a/internal/xds/translator/cluster.go
+++ b/internal/xds/translator/cluster.go
@@ -76,6 +76,7 @@ type xdsClusterArgs struct {
metrics *ir.Metrics
backendConnection *ir.BackendConnection
dns *ir.DNS
+ admissionControl *ir.AdmissionControl
useClientProtocol bool
ipFamily *egv1a1.IPFamily
metadata *ir.ResourceMetadata
@@ -1025,7 +1026,8 @@ func buildTypedExtensionProtocolOptions(args *xdsClusterArgs, requiresAutoHTTPCo
requiresHTTP1Options := args.http1Settings != nil &&
(args.http1Settings.EnableTrailers || args.http1Settings.PreserveHeaderCase || args.http1Settings.HTTP10 != nil)
- requiresHTTPFilters := len(args.settings) > 0 && args.settings[0].Filters != nil && args.settings[0].Filters.CredentialInjection != nil
+ requiresHTTPFilters := (len(args.settings) > 0 && args.settings[0].Filters != nil && args.settings[0].Filters.CredentialInjection != nil) ||
+ args.admissionControl != nil
requiredHTTPProtocolOptions := args.useClientProtocol || requiresAutoHTTPConfig ||
requiresCommonHTTPOptions || requiresHTTP1Options || requiresHTTP2Options || requiresHTTPFilters || requiresAutoSNI || forceHTTP1UpstreamProtocol
@@ -1143,8 +1145,7 @@ func buildTypedExtensionProtocolOptions(args *xdsClusterArgs, requiresAutoHTTPCo
return extensionOptions, secrets, nil
}
-// buildClusterHTTPFilters builds the HTTP filters for the cluster.
-// EG only supports credential injector filter for now, more filters can be added in the future.
+// buildClusterHTTPFilters builds the upstream HTTP filters for the cluster.
func buildClusterHTTPFilters(args *xdsClusterArgs) ([]*hcmv3.HttpFilter, []*tlsv3.Secret, error) {
filters := make([]*hcmv3.HttpFilter, 0)
secrets := make([]*tlsv3.Secret, 0)
@@ -1163,6 +1164,14 @@ func buildClusterHTTPFilters(args *xdsClusterArgs) ([]*hcmv3.HttpFilter, []*tlsv
}
}
+ if args.admissionControl != nil {
+ filter, err := buildUpstreamAdmissionControlFilter(args.admissionControl)
+ if err != nil {
+ return nil, nil, err
+ }
+ filters = append(filters, filter)
+ }
+
// UpstreamCodec filter is required as the terminal filter for the upstream HTTP filters.
if len(filters) > 0 {
upstreamCodec, err := buildUpstreamCodecFilter()
@@ -1171,7 +1180,6 @@ func buildClusterHTTPFilters(args *xdsClusterArgs) ([]*hcmv3.HttpFilter, []*tlsv
}
filters = append(filters, upstreamCodec)
}
- // We may need to add more Cluster filters in the future, so we return a slice of filters.
return filters, secrets, nil
}
diff --git a/internal/xds/translator/testdata/in/xds-ir/admission-control.yaml b/internal/xds/translator/testdata/in/xds-ir/admission-control.yaml
new file mode 100644
index 0000000000..a1dbb0dca2
--- /dev/null
+++ b/internal/xds/translator/testdata/in/xds-ir/admission-control.yaml
@@ -0,0 +1,70 @@
+http:
+- name: "first-listener"
+ address: "::"
+ port: 10080
+ path:
+ mergeSlashes: true
+ escapedSlashesAction: UnescapeAndRedirect
+ hostnames:
+ - "*"
+ routes:
+ - name: "first-route"
+ hostname: "*"
+ traffic:
+ admissionControl:
+ samplingWindow: 30s
+ successRateThreshold: 95
+ aggression: 1
+ rpsThreshold: 5
+ maxRejectionProbability: 80
+ successCriteria:
+ http:
+ httpSuccessStatus:
+ - 200
+ - 201
+ - 202
+ - 204
+ pathMatch:
+ exact: "foo/bar"
+ destination:
+ name: "first-route-dest"
+ settings:
+ - endpoints:
+ - host: "1.2.3.4"
+ port: 50000
+ name: "first-route-dest/backend/0"
+ - name: "second-route"
+ hostname: "*"
+ traffic:
+ admissionControl:
+ successRateThreshold: 80
+ aggression: 2
+ rpsThreshold: 10
+ maxRejectionProbability: 95
+ successCriteria:
+ grpc:
+ grpcSuccessStatus:
+ - "Ok"
+ - "Cancelled"
+ pathMatch:
+ exact: "example"
+ destination:
+ name: "second-route-dest"
+ settings:
+ - endpoints:
+ - host: "1.2.3.4"
+ port: 50000
+ name: "second-route-dest/backend/0"
+ - name: "third-route"
+ hostname: "*"
+ traffic:
+ admissionControl: {}
+ pathMatch:
+ exact: "test"
+ destination:
+ name: "third-route-dest"
+ settings:
+ - endpoints:
+ - host: "1.2.3.4"
+ port: 50000
+ name: "third-route-dest/backend/0"
diff --git a/internal/xds/translator/testdata/out/xds-ir/admission-control.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/admission-control.clusters.yaml
new file mode 100644
index 0000000000..8604a630ed
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/admission-control.clusters.yaml
@@ -0,0 +1,149 @@
+- circuitBreakers:
+ thresholds:
+ - maxRetries: 1024
+ commonLbConfig: {}
+ connectTimeout: 10s
+ dnsLookupFamily: V4_PREFERRED
+ edsClusterConfig:
+ edsConfig:
+ ads: {}
+ resourceApiVersion: V3
+ serviceName: first-route-dest
+ ignoreHealthOnHostRemoval: true
+ loadBalancingPolicy:
+ policies:
+ - typedExtensionConfig:
+ name: envoy.load_balancing_policies.least_request
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.load_balancing_policies.least_request.v3.LeastRequest
+ localityLbConfig:
+ localityWeightedLbConfig: {}
+ name: first-route-dest
+ perConnectionBufferLimitBytes: 32768
+ type: EDS
+ typedExtensionProtocolOptions:
+ envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
+ '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
+ explicitHttpConfig:
+ httpProtocolOptions: {}
+ httpFilters:
+ - name: envoy.filters.http.admission_control
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.filters.http.admission_control.v3.AdmissionControl
+ aggression:
+ defaultValue: 1
+ enabled:
+ defaultValue: true
+ maxRejectionProbability:
+ defaultValue:
+ value: 80
+ rpsThreshold:
+ defaultValue: 5
+ samplingWindow: 30s
+ srThreshold:
+ defaultValue:
+ value: 95
+ successCriteria:
+ httpCriteria:
+ httpSuccessStatus:
+ - end: 201
+ start: 200
+ - end: 202
+ start: 201
+ - end: 203
+ start: 202
+ - end: 205
+ start: 204
+ - name: envoy.extensions.filters.http.upstream_codec.v3.UpstreamCodec
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.filters.http.upstream_codec.v3.UpstreamCodec
+- circuitBreakers:
+ thresholds:
+ - maxRetries: 1024
+ commonLbConfig: {}
+ connectTimeout: 10s
+ dnsLookupFamily: V4_PREFERRED
+ edsClusterConfig:
+ edsConfig:
+ ads: {}
+ resourceApiVersion: V3
+ serviceName: second-route-dest
+ ignoreHealthOnHostRemoval: true
+ loadBalancingPolicy:
+ policies:
+ - typedExtensionConfig:
+ name: envoy.load_balancing_policies.least_request
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.load_balancing_policies.least_request.v3.LeastRequest
+ localityLbConfig:
+ localityWeightedLbConfig: {}
+ name: second-route-dest
+ perConnectionBufferLimitBytes: 32768
+ type: EDS
+ typedExtensionProtocolOptions:
+ envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
+ '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
+ explicitHttpConfig:
+ httpProtocolOptions: {}
+ httpFilters:
+ - name: envoy.filters.http.admission_control
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.filters.http.admission_control.v3.AdmissionControl
+ aggression:
+ defaultValue: 2
+ enabled:
+ defaultValue: true
+ maxRejectionProbability:
+ defaultValue:
+ value: 95
+ rpsThreshold:
+ defaultValue: 10
+ srThreshold:
+ defaultValue:
+ value: 80
+ successCriteria:
+ grpcCriteria:
+ grpcSuccessStatus:
+ - 0
+ - 1
+ - name: envoy.extensions.filters.http.upstream_codec.v3.UpstreamCodec
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.filters.http.upstream_codec.v3.UpstreamCodec
+- circuitBreakers:
+ thresholds:
+ - maxRetries: 1024
+ commonLbConfig: {}
+ connectTimeout: 10s
+ dnsLookupFamily: V4_PREFERRED
+ edsClusterConfig:
+ edsConfig:
+ ads: {}
+ resourceApiVersion: V3
+ serviceName: third-route-dest
+ ignoreHealthOnHostRemoval: true
+ loadBalancingPolicy:
+ policies:
+ - typedExtensionConfig:
+ name: envoy.load_balancing_policies.least_request
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.load_balancing_policies.least_request.v3.LeastRequest
+ localityLbConfig:
+ localityWeightedLbConfig: {}
+ name: third-route-dest
+ perConnectionBufferLimitBytes: 32768
+ type: EDS
+ typedExtensionProtocolOptions:
+ envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
+ '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
+ explicitHttpConfig:
+ httpProtocolOptions: {}
+ httpFilters:
+ - name: envoy.filters.http.admission_control
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.filters.http.admission_control.v3.AdmissionControl
+ enabled:
+ defaultValue: true
+ successCriteria: {}
+ - name: envoy.extensions.filters.http.upstream_codec.v3.UpstreamCodec
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.filters.http.upstream_codec.v3.UpstreamCodec
diff --git a/internal/xds/translator/testdata/out/xds-ir/admission-control.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/admission-control.endpoints.yaml
new file mode 100644
index 0000000000..475b89a087
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/admission-control.endpoints.yaml
@@ -0,0 +1,36 @@
+- clusterName: first-route-dest
+ endpoints:
+ - lbEndpoints:
+ - endpoint:
+ address:
+ socketAddress:
+ address: 1.2.3.4
+ portValue: 50000
+ loadBalancingWeight: 1
+ loadBalancingWeight: 1
+ locality:
+ region: first-route-dest/backend/0
+- clusterName: second-route-dest
+ endpoints:
+ - lbEndpoints:
+ - endpoint:
+ address:
+ socketAddress:
+ address: 1.2.3.4
+ portValue: 50000
+ loadBalancingWeight: 1
+ loadBalancingWeight: 1
+ locality:
+ region: second-route-dest/backend/0
+- clusterName: third-route-dest
+ endpoints:
+ - lbEndpoints:
+ - endpoint:
+ address:
+ socketAddress:
+ address: 1.2.3.4
+ portValue: 50000
+ loadBalancingWeight: 1
+ loadBalancingWeight: 1
+ locality:
+ region: third-route-dest/backend/0
diff --git a/internal/xds/translator/testdata/out/xds-ir/admission-control.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/admission-control.listeners.yaml
new file mode 100644
index 0000000000..5dd5e46e3c
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/admission-control.listeners.yaml
@@ -0,0 +1,35 @@
+- address:
+ socketAddress:
+ address: '::'
+ portValue: 10080
+ defaultFilterChain:
+ filters:
+ - name: envoy.filters.network.http_connection_manager
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
+ commonHttpProtocolOptions:
+ headersWithUnderscoresAction: REJECT_REQUEST
+ http2ProtocolOptions:
+ initialConnectionWindowSize: 1048576
+ initialStreamWindowSize: 65536
+ maxConcurrentStreams: 100
+ httpFilters:
+ - name: envoy.filters.http.router
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
+ suppressEnvoyHeaders: true
+ mergeSlashes: true
+ normalizePath: true
+ pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT
+ rds:
+ configSource:
+ ads: {}
+ resourceApiVersion: V3
+ routeConfigName: first-listener
+ serverHeaderTransformation: PASS_THROUGH
+ statPrefix: http-10080
+ useRemoteAddress: true
+ name: first-listener
+ maxConnectionsToAcceptPerSocketEvent: 1
+ name: first-listener
+ perConnectionBufferLimitBytes: 32768
diff --git a/internal/xds/translator/testdata/out/xds-ir/admission-control.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/admission-control.routes.yaml
new file mode 100644
index 0000000000..67715e26e3
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/admission-control.routes.yaml
@@ -0,0 +1,28 @@
+- ignorePortInHostMatching: true
+ name: first-listener
+ virtualHosts:
+ - domains:
+ - '*'
+ name: first-listener/*
+ routes:
+ - match:
+ path: foo/bar
+ name: first-route
+ route:
+ cluster: first-route-dest
+ upgradeConfigs:
+ - upgradeType: websocket
+ - match:
+ path: example
+ name: second-route
+ route:
+ cluster: second-route-dest
+ upgradeConfigs:
+ - upgradeType: websocket
+ - match:
+ path: test
+ name: third-route
+ route:
+ cluster: third-route-dest
+ upgradeConfigs:
+ - upgradeType: websocket
diff --git a/internal/xds/translator/utils.go b/internal/xds/translator/utils.go
index 84ec9dd4dd..6e3a570e03 100644
--- a/internal/xds/translator/utils.go
+++ b/internal/xds/translator/utils.go
@@ -219,6 +219,7 @@ func applyTraffic(args *xdsClusterArgs, traffic *ir.TrafficFeatures) {
args.backendConnection = traffic.BackendConnection
args.dns = traffic.DNS
args.http2Settings = traffic.HTTP2
+ args.admissionControl = traffic.AdmissionControl
}
// determineIPFamily determines the IP family based on multiple destination settings
diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md
index a04adfb42e..a0fcedd6d6 100644
--- a/site/content/en/latest/api/extension_types.md
+++ b/site/content/en/latest/api/extension_types.md
@@ -189,6 +189,43 @@ _Appears in:_
| `GRPC` | ActiveHealthCheckerTypeGRPC defines the GRPC type of health checking.
|
+#### AdmissionControl
+
+
+
+AdmissionControl defines the admission control policy to be applied.
+This configuration probabilistically rejects requests based on the success rate
+of previous requests in a configurable sliding time window.
+All fields are optional and will use Envoy's defaults when not specified.
+
+_Appears in:_
+- [BackendTrafficPolicySpec](#backendtrafficpolicyspec)
+
+| Field | Type | Required | Default | Description |
+| --- | --- | --- | --- | --- |
+| `samplingWindow` | _[Duration](https://gateway-api.sigs.k8s.io/reference/1.5/spec/#duration)_ | false | | SamplingWindow defines the time window over which request success rates are calculated.
Defaults to 30s if not specified. |
+| `successRateThreshold` | _integer_ | false | | SuccessRateThreshold is the lowest request success rate, as a percentage in the
range [1, 100], at which the filter will not reject requests. Defaults to 95 if
not specified. Envoy rejects values below 1%, so values lower than 1 are not allowed. |
+| `aggression` | _integer_ | false | | Aggression controls the rejection probability curve. A value of 1 means a linear
increase in rejection probability as the success rate decreases. Higher values
result in more aggressive rejection at higher success rates.
Envoy requires aggression to be greater than 0 and clamps values below 1 to 1.
Defaults to 1 if not specified. |
+| `rpsThreshold` | _integer_ | false | | RPSThreshold defines the minimum requests per second below which requests will
pass through the filter without rejection. Defaults to 0 if not specified. |
+| `maxRejectionProbability` | _integer_ | false | | MaxRejectionProbability represents the upper limit of the rejection probability,
expressed as a percentage in the range [0, 100]. Defaults to 80 if not specified. |
+| `successCriteria` | _[AdmissionControlSuccessCriteria](#admissioncontrolsuccesscriteria)_ | false | | SuccessCriteria defines what constitutes a successful request for both HTTP and gRPC. |
+
+
+#### AdmissionControlSuccessCriteria
+
+
+
+AdmissionControlSuccessCriteria defines the criteria for determining successful requests.
+
+_Appears in:_
+- [AdmissionControl](#admissioncontrol)
+
+| Field | Type | Required | Default | Description |
+| --- | --- | --- | --- | --- |
+| `http` | _[HTTPSuccessCriteria](#httpsuccesscriteria)_ | false | | HTTP defines success criteria for HTTP requests. |
+| `grpc` | _[GRPCSuccessCriteria](#grpcsuccesscriteria)_ | false | | GRPC defines success criteria for gRPC requests. |
+
+
#### AppProtocolType
_Underlying type:_ _string_
@@ -543,6 +580,7 @@ _Appears in:_
| `mergeType` | _[MergeType](#mergetype)_ | false | | MergeType determines how this configuration is merged with existing BackendTrafficPolicy
configurations targeting a parent resource. When set, this configuration will be merged
into a parent BackendTrafficPolicy (i.e. the one targeting a Gateway or Listener).
This field cannot be set when targeting a parent resource (Gateway).
If unset, no merging occurs, and only the most specific configuration takes effect. |
| `rateLimit` | _[RateLimitSpec](#ratelimitspec)_ | false | | RateLimit allows the user to limit the number of incoming requests
to a predefined value based on attributes within the traffic flow. |
| `faultInjection` | _[FaultInjection](#faultinjection)_ | false | | FaultInjection defines the fault injection policy to be applied. This configuration can be used to
inject delays and abort requests to mimic failure scenarios such as service failures and overloads |
+| `admissionControl` | _[AdmissionControl](#admissioncontrol)_ | false | | AdmissionControl defines the admission control policy to be applied. This configuration
probabilistically rejects requests based on the success rate of previous requests in a
configurable sliding time window. |
| `useClientProtocol` | _boolean_ | false | | UseClientProtocol configures Envoy to prefer sending requests to backends using
the same HTTP protocol that the incoming request used. Defaults to false, which means
that Envoy will use the protocol indicated by the attached BackendRef. |
| `compression` | _[Compression](#compression) array_ | false | | The compression config for the http streams.
Deprecated: Use Compressor instead. |
| `compressor` | _[Compression](#compression) array_ | false | | The compressor config for the http streams.
This provides more granular control over compression configuration.
Order matters: The first compressor in the list is preferred when q-values in Accept-Encoding are equal. |
@@ -1477,6 +1515,7 @@ _Appears in:_
| `envoy.filters.http.custom_response` | EnvoyFilterCustomResponse defines the Envoy HTTP custom response filter.
|
| `envoy.filters.http.health_check` | EnvoyFilterHealthCheck defines the Envoy HTTP health check filter.
|
| `envoy.filters.http.fault` | EnvoyFilterFault defines the Envoy HTTP fault filter.
|
+| `envoy.filters.http.admission_control` | EnvoyFilterAdmissionControl defines the Envoy HTTP admission control filter.
|
| `envoy.filters.http.cors` | EnvoyFilterCORS defines the Envoy HTTP CORS filter.
|
| `envoy.filters.http.header_mutation` | EnvoyFilterHeaderMutation defines the Envoy HTTP header mutation filter
|
| `envoy.filters.http.ext_authz` | EnvoyFilterExtAuthz defines the Envoy HTTP external authorization filter.
|
@@ -2498,6 +2537,51 @@ _Appears in:_
| `enableWeb` | _boolean_ | false | | EnableWeb configures the gRPC-web filter on the listener.
The gRPC-web filter allows clients (typically browsers) to make gRPC calls
using HTTP/1.1 or HTTP/2.
This is enabled by default for GRPCRoute and opt-in for HTTPRoute.
In general, gRPC traffic should be handled via GRPCRoute, but there are cases where
users want to route gRPC using HTTPRoute for its richer matching capabilities.
Therefore, we enable this behavior only when it is explicitly opted in. |
+#### GRPCSuccessCode
+
+_Underlying type:_ _string_
+
+GRPCSuccessCode defines gRPC status codes as defined in
+https://github.com/grpc/grpc/blob/master/doc/statuscodes.md#status-codes-and-their-use-in-grpc.
+
+_Appears in:_
+- [GRPCSuccessCriteria](#grpcsuccesscriteria)
+
+| Value | Description |
+| ----- | ----------- |
+| `Ok` | |
+| `Cancelled` | |
+| `Unknown` | |
+| `InvalidArgument` | |
+| `DeadlineExceeded` | |
+| `NotFound` | |
+| `AlreadyExists` | |
+| `PermissionDenied` | |
+| `ResourceExhausted` | |
+| `FailedPrecondition` | |
+| `Aborted` | |
+| `OutOfRange` | |
+| `Unimplemented` | |
+| `Internal` | |
+| `Unavailable` | |
+| `DataLoss` | |
+| `Unauthenticated` | |
+
+
+#### GRPCSuccessCriteria
+
+
+
+GRPCSuccessCriteria defines success criteria for gRPC requests.
+
+_Appears in:_
+- [AdmissionControlSuccessCriteria](#admissioncontrolsuccesscriteria)
+
+| Field | Type | Required | Default | Description |
+| --- | --- | --- | --- | --- |
+| `grpcSuccessStatus` | _[GRPCSuccessCode](#grpcsuccesscode) array_ | false | | GRPCSuccessStatus defines gRPC status codes that are considered successful.
Status codes are defined in https://github.com/grpc/grpc/blob/master/doc/statuscodes.md#status-codes-and-their-use-in-grpc. |
+
+
#### Gateway
@@ -2992,10 +3076,25 @@ HTTPStatus defines the http status code.
_Appears in:_
- [HTTPActiveHealthChecker](#httpactivehealthchecker)
+- [HTTPSuccessCriteria](#httpsuccesscriteria)
- [RetryOn](#retryon)
+#### HTTPSuccessCriteria
+
+
+
+HTTPSuccessCriteria defines success criteria for HTTP requests.
+
+_Appears in:_
+- [AdmissionControlSuccessCriteria](#admissioncontrolsuccesscriteria)
+
+| Field | Type | Required | Default | Description |
+| --- | --- | --- | --- | --- |
+| `httpSuccessStatus` | _[HTTPStatus](#httpstatus) array_ | false | | HTTPSuccessStatus defines HTTP status codes that are considered successful. |
+
+
#### HTTPTimeout
diff --git a/test/cel-validation/backendtrafficpolicy_test.go b/test/cel-validation/backendtrafficpolicy_test.go
index 9527413801..39263f3ee1 100644
--- a/test/cel-validation/backendtrafficpolicy_test.go
+++ b/test/cel-validation/backendtrafficpolicy_test.go
@@ -75,6 +75,67 @@ func TestBackendTrafficPolicyTarget(t *testing.T) {
},
wantErrors: []string{},
},
+ {
+ desc: "valid admissionControl percentage bounds",
+ mutate: func(btp *egv1a1.BackendTrafficPolicy) {
+ btp.Spec = egv1a1.BackendTrafficPolicySpec{
+ PolicyTargetReferences: egv1a1.PolicyTargetReferences{
+ TargetRef: &gwapiv1.LocalPolicyTargetReferenceWithSectionName{
+ LocalPolicyTargetReference: gwapiv1.LocalPolicyTargetReference{
+ Group: gwapiv1.Group("gateway.networking.k8s.io"),
+ Kind: gwapiv1.Kind("HTTPRoute"),
+ Name: gwapiv1.ObjectName("httpbin-route"),
+ },
+ },
+ },
+ AdmissionControl: &egv1a1.AdmissionControl{
+ SuccessRateThreshold: new(uint32(1)),
+ MaxRejectionProbability: new(uint32(100)),
+ },
+ }
+ },
+ wantErrors: []string{},
+ },
+ {
+ desc: "admissionControl successRateThreshold below minimum",
+ mutate: func(btp *egv1a1.BackendTrafficPolicy) {
+ btp.Spec = egv1a1.BackendTrafficPolicySpec{
+ PolicyTargetReferences: egv1a1.PolicyTargetReferences{
+ TargetRef: &gwapiv1.LocalPolicyTargetReferenceWithSectionName{
+ LocalPolicyTargetReference: gwapiv1.LocalPolicyTargetReference{
+ Group: gwapiv1.Group("gateway.networking.k8s.io"),
+ Kind: gwapiv1.Kind("HTTPRoute"),
+ Name: gwapiv1.ObjectName("httpbin-route"),
+ },
+ },
+ },
+ AdmissionControl: &egv1a1.AdmissionControl{
+ SuccessRateThreshold: new(uint32(0)),
+ },
+ }
+ },
+ wantErrors: []string{"successRateThreshold"},
+ },
+ {
+ desc: "admissionControl maxRejectionProbability above maximum",
+ mutate: func(btp *egv1a1.BackendTrafficPolicy) {
+ btp.Spec = egv1a1.BackendTrafficPolicySpec{
+ PolicyTargetReferences: egv1a1.PolicyTargetReferences{
+ TargetRef: &gwapiv1.LocalPolicyTargetReferenceWithSectionName{
+ LocalPolicyTargetReference: gwapiv1.LocalPolicyTargetReference{
+ Group: gwapiv1.Group("gateway.networking.k8s.io"),
+ Kind: gwapiv1.Kind("HTTPRoute"),
+ Name: gwapiv1.ObjectName("httpbin-route"),
+ },
+ },
+ },
+ AdmissionControl: &egv1a1.AdmissionControl{
+ MaxRejectionProbability: new(uint32(101)),
+ },
+ }
+ },
+ wantErrors: []string{"maxRejectionProbability"},
+ },
{
desc: "no targetRef",
mutate: func(btp *egv1a1.BackendTrafficPolicy) {
diff --git a/test/helm/gateway-crds-helm/all.out.yaml b/test/helm/gateway-crds-helm/all.out.yaml
index 010be86793..a68f0a9ee1 100644
--- a/test/helm/gateway-crds-helm/all.out.yaml
+++ b/test/helm/gateway-crds-helm/all.out.yaml
@@ -22577,6 +22577,110 @@ spec:
spec:
description: spec defines the desired state of BackendTrafficPolicy.
properties:
+ admissionControl:
+ description: |-
+ AdmissionControl defines the admission control policy to be applied. This configuration
+ probabilistically rejects requests based on the success rate of previous requests in a
+ configurable sliding time window.
+ properties:
+ aggression:
+ description: |-
+ Aggression controls the rejection probability curve. A value of 1 means a linear
+ increase in rejection probability as the success rate decreases. Higher values
+ result in more aggressive rejection at higher success rates.
+ Envoy requires aggression to be greater than 0 and clamps values below 1 to 1.
+ Defaults to 1 if not specified.
+ format: int32
+ minimum: 1
+ type: integer
+ maxRejectionProbability:
+ description: |-
+ MaxRejectionProbability represents the upper limit of the rejection probability,
+ expressed as a percentage in the range [0, 100]. Defaults to 80 if not specified.
+ format: int32
+ maximum: 100
+ minimum: 0
+ type: integer
+ rpsThreshold:
+ description: |-
+ RPSThreshold defines the minimum requests per second below which requests will
+ pass through the filter without rejection. Defaults to 0 if not specified.
+ format: int32
+ minimum: 0
+ type: integer
+ samplingWindow:
+ description: |-
+ SamplingWindow defines the time window over which request success rates are calculated.
+ Defaults to 30s if not specified.
+ pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+ type: string
+ successCriteria:
+ description: SuccessCriteria defines what constitutes a successful
+ request for both HTTP and gRPC.
+ properties:
+ grpc:
+ description: GRPC defines success criteria for gRPC requests.
+ properties:
+ grpcSuccessStatus:
+ description: |-
+ GRPCSuccessStatus defines gRPC status codes that are considered successful.
+ Status codes are defined in https://github.com/grpc/grpc/blob/master/doc/statuscodes.md#status-codes-and-their-use-in-grpc.
+ items:
+ description: |-
+ GRPCSuccessCode defines gRPC status codes as defined in
+ https://github.com/grpc/grpc/blob/master/doc/statuscodes.md#status-codes-and-their-use-in-grpc.
+ enum:
+ - Ok
+ - Cancelled
+ - Unknown
+ - InvalidArgument
+ - DeadlineExceeded
+ - NotFound
+ - AlreadyExists
+ - PermissionDenied
+ - ResourceExhausted
+ - FailedPrecondition
+ - Aborted
+ - OutOfRange
+ - Unimplemented
+ - Internal
+ - Unavailable
+ - DataLoss
+ - Unauthenticated
+ type: string
+ type: array
+ type: object
+ http:
+ description: HTTP defines success criteria for HTTP requests.
+ properties:
+ httpSuccessStatus:
+ description: HTTPSuccessStatus defines HTTP status codes
+ that are considered successful.
+ items:
+ description: HTTPStatus defines the http status code.
+ maximum: 599
+ minimum: 100
+ type: integer
+ type: array
+ type: object
+ type: object
+ successRateThreshold:
+ description: |-
+ SuccessRateThreshold is the lowest request success rate, as a percentage in the
+ range [1, 100], at which the filter will not reject requests. Defaults to 95 if
+ not specified. Envoy rejects values below 1%, so values lower than 1 are not allowed.
+ format: int32
+ maximum: 100
+ minimum: 1
+ type: integer
+ type: object
+ x-kubernetes-validations:
+ - message: successRateThreshold must be between 1 and 100
+ rule: '!has(self.successRateThreshold) || (self.successRateThreshold
+ >= 1 && self.successRateThreshold <= 100)'
+ - message: maxRejectionProbability must be between 0 and 100
+ rule: '!has(self.maxRejectionProbability) || (self.maxRejectionProbability
+ >= 0 && self.maxRejectionProbability <= 100)'
circuitBreaker:
description: |-
Circuit Breaker settings for the upstream connections and requests.
@@ -31359,6 +31463,7 @@ spec:
- envoy.filters.http.custom_response
- envoy.filters.http.health_check
- envoy.filters.http.fault
+ - envoy.filters.http.admission_control
- envoy.filters.http.cors
- envoy.filters.http.header_mutation
- envoy.filters.http.ext_authz
@@ -31390,6 +31495,7 @@ spec:
- envoy.filters.http.custom_response
- envoy.filters.http.health_check
- envoy.filters.http.fault
+ - envoy.filters.http.admission_control
- envoy.filters.http.cors
- envoy.filters.http.header_mutation
- envoy.filters.http.ext_authz
@@ -31419,6 +31525,7 @@ spec:
- envoy.filters.http.custom_response
- envoy.filters.http.health_check
- envoy.filters.http.fault
+ - envoy.filters.http.admission_control
- envoy.filters.http.cors
- envoy.filters.http.header_mutation
- envoy.filters.http.ext_authz
diff --git a/test/helm/gateway-crds-helm/e2e.out.yaml b/test/helm/gateway-crds-helm/e2e.out.yaml
index 1e50eb9b5a..fbe7d4a265 100644
--- a/test/helm/gateway-crds-helm/e2e.out.yaml
+++ b/test/helm/gateway-crds-helm/e2e.out.yaml
@@ -550,6 +550,110 @@ spec:
spec:
description: spec defines the desired state of BackendTrafficPolicy.
properties:
+ admissionControl:
+ description: |-
+ AdmissionControl defines the admission control policy to be applied. This configuration
+ probabilistically rejects requests based on the success rate of previous requests in a
+ configurable sliding time window.
+ properties:
+ aggression:
+ description: |-
+ Aggression controls the rejection probability curve. A value of 1 means a linear
+ increase in rejection probability as the success rate decreases. Higher values
+ result in more aggressive rejection at higher success rates.
+ Envoy requires aggression to be greater than 0 and clamps values below 1 to 1.
+ Defaults to 1 if not specified.
+ format: int32
+ minimum: 1
+ type: integer
+ maxRejectionProbability:
+ description: |-
+ MaxRejectionProbability represents the upper limit of the rejection probability,
+ expressed as a percentage in the range [0, 100]. Defaults to 80 if not specified.
+ format: int32
+ maximum: 100
+ minimum: 0
+ type: integer
+ rpsThreshold:
+ description: |-
+ RPSThreshold defines the minimum requests per second below which requests will
+ pass through the filter without rejection. Defaults to 0 if not specified.
+ format: int32
+ minimum: 0
+ type: integer
+ samplingWindow:
+ description: |-
+ SamplingWindow defines the time window over which request success rates are calculated.
+ Defaults to 30s if not specified.
+ pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+ type: string
+ successCriteria:
+ description: SuccessCriteria defines what constitutes a successful
+ request for both HTTP and gRPC.
+ properties:
+ grpc:
+ description: GRPC defines success criteria for gRPC requests.
+ properties:
+ grpcSuccessStatus:
+ description: |-
+ GRPCSuccessStatus defines gRPC status codes that are considered successful.
+ Status codes are defined in https://github.com/grpc/grpc/blob/master/doc/statuscodes.md#status-codes-and-their-use-in-grpc.
+ items:
+ description: |-
+ GRPCSuccessCode defines gRPC status codes as defined in
+ https://github.com/grpc/grpc/blob/master/doc/statuscodes.md#status-codes-and-their-use-in-grpc.
+ enum:
+ - Ok
+ - Cancelled
+ - Unknown
+ - InvalidArgument
+ - DeadlineExceeded
+ - NotFound
+ - AlreadyExists
+ - PermissionDenied
+ - ResourceExhausted
+ - FailedPrecondition
+ - Aborted
+ - OutOfRange
+ - Unimplemented
+ - Internal
+ - Unavailable
+ - DataLoss
+ - Unauthenticated
+ type: string
+ type: array
+ type: object
+ http:
+ description: HTTP defines success criteria for HTTP requests.
+ properties:
+ httpSuccessStatus:
+ description: HTTPSuccessStatus defines HTTP status codes
+ that are considered successful.
+ items:
+ description: HTTPStatus defines the http status code.
+ maximum: 599
+ minimum: 100
+ type: integer
+ type: array
+ type: object
+ type: object
+ successRateThreshold:
+ description: |-
+ SuccessRateThreshold is the lowest request success rate, as a percentage in the
+ range [1, 100], at which the filter will not reject requests. Defaults to 95 if
+ not specified. Envoy rejects values below 1%, so values lower than 1 are not allowed.
+ format: int32
+ maximum: 100
+ minimum: 1
+ type: integer
+ type: object
+ x-kubernetes-validations:
+ - message: successRateThreshold must be between 1 and 100
+ rule: '!has(self.successRateThreshold) || (self.successRateThreshold
+ >= 1 && self.successRateThreshold <= 100)'
+ - message: maxRejectionProbability must be between 0 and 100
+ rule: '!has(self.maxRejectionProbability) || (self.maxRejectionProbability
+ >= 0 && self.maxRejectionProbability <= 100)'
circuitBreaker:
description: |-
Circuit Breaker settings for the upstream connections and requests.
@@ -9332,6 +9436,7 @@ spec:
- envoy.filters.http.custom_response
- envoy.filters.http.health_check
- envoy.filters.http.fault
+ - envoy.filters.http.admission_control
- envoy.filters.http.cors
- envoy.filters.http.header_mutation
- envoy.filters.http.ext_authz
@@ -9363,6 +9468,7 @@ spec:
- envoy.filters.http.custom_response
- envoy.filters.http.health_check
- envoy.filters.http.fault
+ - envoy.filters.http.admission_control
- envoy.filters.http.cors
- envoy.filters.http.header_mutation
- envoy.filters.http.ext_authz
@@ -9392,6 +9498,7 @@ spec:
- envoy.filters.http.custom_response
- envoy.filters.http.health_check
- envoy.filters.http.fault
+ - envoy.filters.http.admission_control
- envoy.filters.http.cors
- envoy.filters.http.header_mutation
- envoy.filters.http.ext_authz
diff --git a/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml b/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml
index 12abf42660..bd393c2f49 100644
--- a/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml
+++ b/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml
@@ -550,6 +550,110 @@ spec:
spec:
description: spec defines the desired state of BackendTrafficPolicy.
properties:
+ admissionControl:
+ description: |-
+ AdmissionControl defines the admission control policy to be applied. This configuration
+ probabilistically rejects requests based on the success rate of previous requests in a
+ configurable sliding time window.
+ properties:
+ aggression:
+ description: |-
+ Aggression controls the rejection probability curve. A value of 1 means a linear
+ increase in rejection probability as the success rate decreases. Higher values
+ result in more aggressive rejection at higher success rates.
+ Envoy requires aggression to be greater than 0 and clamps values below 1 to 1.
+ Defaults to 1 if not specified.
+ format: int32
+ minimum: 1
+ type: integer
+ maxRejectionProbability:
+ description: |-
+ MaxRejectionProbability represents the upper limit of the rejection probability,
+ expressed as a percentage in the range [0, 100]. Defaults to 80 if not specified.
+ format: int32
+ maximum: 100
+ minimum: 0
+ type: integer
+ rpsThreshold:
+ description: |-
+ RPSThreshold defines the minimum requests per second below which requests will
+ pass through the filter without rejection. Defaults to 0 if not specified.
+ format: int32
+ minimum: 0
+ type: integer
+ samplingWindow:
+ description: |-
+ SamplingWindow defines the time window over which request success rates are calculated.
+ Defaults to 30s if not specified.
+ pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+ type: string
+ successCriteria:
+ description: SuccessCriteria defines what constitutes a successful
+ request for both HTTP and gRPC.
+ properties:
+ grpc:
+ description: GRPC defines success criteria for gRPC requests.
+ properties:
+ grpcSuccessStatus:
+ description: |-
+ GRPCSuccessStatus defines gRPC status codes that are considered successful.
+ Status codes are defined in https://github.com/grpc/grpc/blob/master/doc/statuscodes.md#status-codes-and-their-use-in-grpc.
+ items:
+ description: |-
+ GRPCSuccessCode defines gRPC status codes as defined in
+ https://github.com/grpc/grpc/blob/master/doc/statuscodes.md#status-codes-and-their-use-in-grpc.
+ enum:
+ - Ok
+ - Cancelled
+ - Unknown
+ - InvalidArgument
+ - DeadlineExceeded
+ - NotFound
+ - AlreadyExists
+ - PermissionDenied
+ - ResourceExhausted
+ - FailedPrecondition
+ - Aborted
+ - OutOfRange
+ - Unimplemented
+ - Internal
+ - Unavailable
+ - DataLoss
+ - Unauthenticated
+ type: string
+ type: array
+ type: object
+ http:
+ description: HTTP defines success criteria for HTTP requests.
+ properties:
+ httpSuccessStatus:
+ description: HTTPSuccessStatus defines HTTP status codes
+ that are considered successful.
+ items:
+ description: HTTPStatus defines the http status code.
+ maximum: 599
+ minimum: 100
+ type: integer
+ type: array
+ type: object
+ type: object
+ successRateThreshold:
+ description: |-
+ SuccessRateThreshold is the lowest request success rate, as a percentage in the
+ range [1, 100], at which the filter will not reject requests. Defaults to 95 if
+ not specified. Envoy rejects values below 1%, so values lower than 1 are not allowed.
+ format: int32
+ maximum: 100
+ minimum: 1
+ type: integer
+ type: object
+ x-kubernetes-validations:
+ - message: successRateThreshold must be between 1 and 100
+ rule: '!has(self.successRateThreshold) || (self.successRateThreshold
+ >= 1 && self.successRateThreshold <= 100)'
+ - message: maxRejectionProbability must be between 0 and 100
+ rule: '!has(self.maxRejectionProbability) || (self.maxRejectionProbability
+ >= 0 && self.maxRejectionProbability <= 100)'
circuitBreaker:
description: |-
Circuit Breaker settings for the upstream connections and requests.
@@ -9332,6 +9436,7 @@ spec:
- envoy.filters.http.custom_response
- envoy.filters.http.health_check
- envoy.filters.http.fault
+ - envoy.filters.http.admission_control
- envoy.filters.http.cors
- envoy.filters.http.header_mutation
- envoy.filters.http.ext_authz
@@ -9363,6 +9468,7 @@ spec:
- envoy.filters.http.custom_response
- envoy.filters.http.health_check
- envoy.filters.http.fault
+ - envoy.filters.http.admission_control
- envoy.filters.http.cors
- envoy.filters.http.header_mutation
- envoy.filters.http.ext_authz
@@ -9392,6 +9498,7 @@ spec:
- envoy.filters.http.custom_response
- envoy.filters.http.health_check
- envoy.filters.http.fault
+ - envoy.filters.http.admission_control
- envoy.filters.http.cors
- envoy.filters.http.header_mutation
- envoy.filters.http.ext_authz