diff --git a/api/v1alpha1/loadbalancer_types.go b/api/v1alpha1/loadbalancer_types.go index 6ee79f9c8bd..fd52391505d 100644 --- a/api/v1alpha1/loadbalancer_types.go +++ b/api/v1alpha1/loadbalancer_types.go @@ -29,6 +29,14 @@ type LoadBalancer struct { // +optional ConsistentHash *ConsistentHash `json:"consistentHash,omitempty"` + // EndpointOverride defines the configuration for endpoint override. + // When specified, the load balancer will attempt to route requests to endpoints + // based on the override information extracted from request headers or metadata. + // If the override endpoints are not available, the configured load balancer policy will be used as fallback. + // + // +optional + EndpointOverride *EndpointOverride `json:"endpointOverride,omitempty"` + // SlowStart defines the configuration related to the slow start load balancer policy. // If set, during slow start window, traffic sent to the newly added hosts will gradually increase. // Currently this is only supported for RoundRobin and LeastRequest load balancers @@ -178,3 +186,26 @@ type ForceLocalZone struct { // +notImplementedHide MinEndpointsInZoneThreshold *uint32 `json:"minEndpointsInZoneThreshold,omitempty"` } + +// EndpointOverride defines the configuration for endpoint override. +// This allows endpoint picking to be implemented based on request headers or metadata. +// It extracts selected override endpoints from the specified sources (request headers, metadata, etc.). +// If no valid endpoint in the override list, then the configured load balancing policy is used as fallback. +type EndpointOverride struct { + // ExtractFrom defines the sources to extract endpoint override information from. + // + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=10 + ExtractFrom []EndpointOverrideExtractFrom `json:"extractFrom"` +} + +// EndpointOverrideExtractFrom defines a source to extract endpoint override information from. +type EndpointOverrideExtractFrom struct { + // Header defines the header to get the override endpoint addresses. + // The header value must specify at least one endpoint in `IP:Port` format or multiple endpoints in `IP:Port,IP:Port,...` format. + // For example `10.0.0.5:8080` or `[2600:4040:5204::1574:24ae]:80`. + // The IPv6 address is enclosed in square brackets. + // + // +optional + Header *string `json:"header,omitempty"` +} diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 4c15267a178..4ec48585c80 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -1603,6 +1603,48 @@ func (in *DNS) DeepCopy() *DNS { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EndpointOverride) DeepCopyInto(out *EndpointOverride) { + *out = *in + if in.ExtractFrom != nil { + in, out := &in.ExtractFrom, &out.ExtractFrom + *out = make([]EndpointOverrideExtractFrom, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EndpointOverride. +func (in *EndpointOverride) DeepCopy() *EndpointOverride { + if in == nil { + return nil + } + out := new(EndpointOverride) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EndpointOverrideExtractFrom) DeepCopyInto(out *EndpointOverrideExtractFrom) { + *out = *in + if in.Header != nil { + in, out := &in.Header, &out.Header + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EndpointOverrideExtractFrom. +func (in *EndpointOverrideExtractFrom) DeepCopy() *EndpointOverrideExtractFrom { + if in == nil { + return nil + } + out := new(EndpointOverrideExtractFrom) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *EnvironmentCustomTag) DeepCopyInto(out *EnvironmentCustomTag) { *out = *in @@ -4515,6 +4557,11 @@ func (in *LoadBalancer) DeepCopyInto(out *LoadBalancer) { *out = new(ConsistentHash) (*in).DeepCopyInto(*out) } + if in.EndpointOverride != nil { + in, out := &in.EndpointOverride, &out.EndpointOverride + *out = new(EndpointOverride) + (*in).DeepCopyInto(*out) + } if in.SlowStart != nil { in, out := &in.SlowStart, &out.SlowStart *out = new(SlowStart) 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 884f72a5155..4be80d9bc62 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 @@ -668,6 +668,34 @@ spec: - message: If consistent hash type is cookie, the cookie field must be set. rule: 'self.type == ''Cookie'' ? has(self.cookie) : !has(self.cookie)' + endpointOverride: + description: |- + EndpointOverride defines the configuration for endpoint override. + When specified, the load balancer will attempt to route requests to endpoints + based on the override information extracted from request headers or metadata. + If the override endpoints are not available, the configured load balancer policy will be used as fallback. + properties: + extractFrom: + description: ExtractFrom defines the sources to extract endpoint + override information from. + items: + description: EndpointOverrideExtractFrom defines a source + to extract endpoint override information from. + properties: + header: + description: |- + Header defines the header to get the override endpoint addresses. + The header value must specify at least one endpoint in `IP:Port` format or multiple endpoints in `IP:Port,IP:Port,...` format. + For example `10.0.0.5:8080` or `[2600:4040:5204::1574:24ae]:80`. + The IPv6 address is enclosed in square brackets. + type: string + type: object + maxItems: 10 + minItems: 1 + type: array + required: + - extractFrom + type: object slowStart: description: |- SlowStart defines the configuration related to the slow start load balancer policy. diff --git a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml index efb39d5fca2..73556e46e82 100644 --- a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml +++ b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml @@ -746,6 +746,35 @@ spec: field must be set. rule: 'self.type == ''Cookie'' ? has(self.cookie) : !has(self.cookie)' + endpointOverride: + description: |- + EndpointOverride defines the configuration for endpoint override. + When specified, the load balancer will attempt to route requests to endpoints + based on the override information extracted from request headers or metadata. + If the override endpoints are not available, the configured load balancer policy will be used as fallback. + properties: + extractFrom: + description: ExtractFrom defines the sources to + extract endpoint override information from. + items: + description: EndpointOverrideExtractFrom defines + a source to extract endpoint override information + from. + properties: + header: + description: |- + Header defines the header to get the override endpoint addresses. + The header value must specify at least one endpoint in `IP:Port` format or multiple endpoints in `IP:Port,IP:Port,...` format. + For example `10.0.0.5:8080` or `[2600:4040:5204::1574:24ae]:80`. + The IPv6 address is enclosed in square brackets. + type: string + type: object + maxItems: 10 + minItems: 1 + type: array + required: + - extractFrom + type: object slowStart: description: |- SlowStart defines the configuration related to the slow start load balancer policy. 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 b88c8c18548..5cb364725cc 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 @@ -11341,6 +11341,37 @@ spec: be set. rule: 'self.type == ''Cookie'' ? has(self.cookie) : !has(self.cookie)' + endpointOverride: + description: |- + EndpointOverride defines the configuration for endpoint override. + When specified, the load balancer will attempt to route requests to endpoints + based on the override information extracted from request headers or metadata. + If the override endpoints are not available, the configured load balancer policy will be used as fallback. + properties: + extractFrom: + description: ExtractFrom defines + the sources to extract endpoint + override information from. + items: + description: EndpointOverrideExtractFrom + defines a source to extract + endpoint override information + from. + properties: + header: + description: |- + Header defines the header to get the override endpoint addresses. + The header value must specify at least one endpoint in `IP:Port` format or multiple endpoints in `IP:Port,IP:Port,...` format. + For example `10.0.0.5:8080` or `[2600:4040:5204::1574:24ae]:80`. + The IPv6 address is enclosed in square brackets. + type: string + type: object + maxItems: 10 + minItems: 1 + type: array + required: + - extractFrom + type: object slowStart: description: |- SlowStart defines the configuration related to the slow start load balancer policy. @@ -12421,6 +12452,37 @@ spec: be set. rule: 'self.type == ''Cookie'' ? has(self.cookie) : !has(self.cookie)' + endpointOverride: + description: |- + EndpointOverride defines the configuration for endpoint override. + When specified, the load balancer will attempt to route requests to endpoints + based on the override information extracted from request headers or metadata. + If the override endpoints are not available, the configured load balancer policy will be used as fallback. + properties: + extractFrom: + description: ExtractFrom defines + the sources to extract endpoint + override information from. + items: + description: EndpointOverrideExtractFrom + defines a source to extract + endpoint override information + from. + properties: + header: + description: |- + Header defines the header to get the override endpoint addresses. + The header value must specify at least one endpoint in `IP:Port` format or multiple endpoints in `IP:Port,IP:Port,...` format. + For example `10.0.0.5:8080` or `[2600:4040:5204::1574:24ae]:80`. + The IPv6 address is enclosed in square brackets. + type: string + type: object + maxItems: 10 + minItems: 1 + type: array + required: + - extractFrom + type: object slowStart: description: |- SlowStart defines the configuration related to the slow start load balancer policy. @@ -13586,6 +13648,36 @@ spec: the cookie field must be set. rule: 'self.type == ''Cookie'' ? has(self.cookie) : !has(self.cookie)' + endpointOverride: + description: |- + EndpointOverride defines the configuration for endpoint override. + When specified, the load balancer will attempt to route requests to endpoints + based on the override information extracted from request headers or metadata. + If the override endpoints are not available, the configured load balancer policy will be used as fallback. + properties: + extractFrom: + description: ExtractFrom defines the + sources to extract endpoint override + information from. + items: + description: EndpointOverrideExtractFrom + defines a source to extract endpoint + override information from. + properties: + header: + description: |- + Header defines the header to get the override endpoint addresses. + The header value must specify at least one endpoint in `IP:Port` format or multiple endpoints in `IP:Port,IP:Port,...` format. + For example `10.0.0.5:8080` or `[2600:4040:5204::1574:24ae]:80`. + The IPv6 address is enclosed in square brackets. + type: string + type: object + maxItems: 10 + minItems: 1 + type: array + required: + - extractFrom + type: object slowStart: description: |- SlowStart defines the configuration related to the slow start load balancer policy. @@ -14669,6 +14761,36 @@ spec: the cookie field must be set. rule: 'self.type == ''Cookie'' ? has(self.cookie) : !has(self.cookie)' + endpointOverride: + description: |- + EndpointOverride defines the configuration for endpoint override. + When specified, the load balancer will attempt to route requests to endpoints + based on the override information extracted from request headers or metadata. + If the override endpoints are not available, the configured load balancer policy will be used as fallback. + properties: + extractFrom: + description: ExtractFrom defines the sources + to extract endpoint override information + from. + items: + description: EndpointOverrideExtractFrom + defines a source to extract endpoint override + information from. + properties: + header: + description: |- + Header defines the header to get the override endpoint addresses. + The header value must specify at least one endpoint in `IP:Port` format or multiple endpoints in `IP:Port,IP:Port,...` format. + For example `10.0.0.5:8080` or `[2600:4040:5204::1574:24ae]:80`. + The IPv6 address is enclosed in square brackets. + type: string + type: object + maxItems: 10 + minItems: 1 + type: array + required: + - extractFrom + type: object slowStart: description: |- SlowStart defines the configuration related to the slow start load balancer policy. diff --git a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_securitypolicies.yaml b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_securitypolicies.yaml index 77fee40bf84..79af9ce40c0 100644 --- a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_securitypolicies.yaml +++ b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_securitypolicies.yaml @@ -1256,6 +1256,35 @@ spec: cookie field must be set. rule: 'self.type == ''Cookie'' ? has(self.cookie) : !has(self.cookie)' + endpointOverride: + description: |- + EndpointOverride defines the configuration for endpoint override. + When specified, the load balancer will attempt to route requests to endpoints + based on the override information extracted from request headers or metadata. + If the override endpoints are not available, the configured load balancer policy will be used as fallback. + properties: + extractFrom: + description: ExtractFrom defines the sources to + extract endpoint override information from. + items: + description: EndpointOverrideExtractFrom defines + a source to extract endpoint override information + from. + properties: + header: + description: |- + Header defines the header to get the override endpoint addresses. + The header value must specify at least one endpoint in `IP:Port` format or multiple endpoints in `IP:Port,IP:Port,...` format. + For example `10.0.0.5:8080` or `[2600:4040:5204::1574:24ae]:80`. + The IPv6 address is enclosed in square brackets. + type: string + type: object + maxItems: 10 + minItems: 1 + type: array + required: + - extractFrom + type: object slowStart: description: |- SlowStart defines the configuration related to the slow start load balancer policy. @@ -2234,6 +2263,35 @@ spec: cookie field must be set. rule: 'self.type == ''Cookie'' ? has(self.cookie) : !has(self.cookie)' + endpointOverride: + description: |- + EndpointOverride defines the configuration for endpoint override. + When specified, the load balancer will attempt to route requests to endpoints + based on the override information extracted from request headers or metadata. + If the override endpoints are not available, the configured load balancer policy will be used as fallback. + properties: + extractFrom: + description: ExtractFrom defines the sources to + extract endpoint override information from. + items: + description: EndpointOverrideExtractFrom defines + a source to extract endpoint override information + from. + properties: + header: + description: |- + Header defines the header to get the override endpoint addresses. + The header value must specify at least one endpoint in `IP:Port` format or multiple endpoints in `IP:Port,IP:Port,...` format. + For example `10.0.0.5:8080` or `[2600:4040:5204::1574:24ae]:80`. + The IPv6 address is enclosed in square brackets. + type: string + type: object + maxItems: 10 + minItems: 1 + type: array + required: + - extractFrom + type: object slowStart: description: |- SlowStart defines the configuration related to the slow start load balancer policy. @@ -3418,6 +3476,36 @@ spec: the cookie field must be set. rule: 'self.type == ''Cookie'' ? has(self.cookie) : !has(self.cookie)' + endpointOverride: + description: |- + EndpointOverride defines the configuration for endpoint override. + When specified, the load balancer will attempt to route requests to endpoints + based on the override information extracted from request headers or metadata. + If the override endpoints are not available, the configured load balancer policy will be used as fallback. + properties: + extractFrom: + description: ExtractFrom defines the sources + to extract endpoint override information + from. + items: + description: EndpointOverrideExtractFrom + defines a source to extract endpoint + override information from. + properties: + header: + description: |- + Header defines the header to get the override endpoint addresses. + The header value must specify at least one endpoint in `IP:Port` format or multiple endpoints in `IP:Port,IP:Port,...` format. + For example `10.0.0.5:8080` or `[2600:4040:5204::1574:24ae]:80`. + The IPv6 address is enclosed in square brackets. + type: string + type: object + maxItems: 10 + minItems: 1 + type: array + required: + - extractFrom + type: object slowStart: description: |- SlowStart defines the configuration related to the slow start load balancer policy. @@ -4637,6 +4725,35 @@ spec: cookie field must be set. rule: 'self.type == ''Cookie'' ? has(self.cookie) : !has(self.cookie)' + endpointOverride: + description: |- + EndpointOverride defines the configuration for endpoint override. + When specified, the load balancer will attempt to route requests to endpoints + based on the override information extracted from request headers or metadata. + If the override endpoints are not available, the configured load balancer policy will be used as fallback. + properties: + extractFrom: + description: ExtractFrom defines the sources to + extract endpoint override information from. + items: + description: EndpointOverrideExtractFrom defines + a source to extract endpoint override information + from. + properties: + header: + description: |- + Header defines the header to get the override endpoint addresses. + The header value must specify at least one endpoint in `IP:Port` format or multiple endpoints in `IP:Port,IP:Port,...` format. + For example `10.0.0.5:8080` or `[2600:4040:5204::1574:24ae]:80`. + The IPv6 address is enclosed in square brackets. + type: string + type: object + maxItems: 10 + minItems: 1 + type: array + required: + - extractFrom + type: object slowStart: description: |- SlowStart defines the configuration related to the slow start load balancer policy. diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml index aa6a8c69ae1..f87cb95968c 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml @@ -667,6 +667,34 @@ spec: - message: If consistent hash type is cookie, the cookie field must be set. rule: 'self.type == ''Cookie'' ? has(self.cookie) : !has(self.cookie)' + endpointOverride: + description: |- + EndpointOverride defines the configuration for endpoint override. + When specified, the load balancer will attempt to route requests to endpoints + based on the override information extracted from request headers or metadata. + If the override endpoints are not available, the configured load balancer policy will be used as fallback. + properties: + extractFrom: + description: ExtractFrom defines the sources to extract endpoint + override information from. + items: + description: EndpointOverrideExtractFrom defines a source + to extract endpoint override information from. + properties: + header: + description: |- + Header defines the header to get the override endpoint addresses. + The header value must specify at least one endpoint in `IP:Port` format or multiple endpoints in `IP:Port,IP:Port,...` format. + For example `10.0.0.5:8080` or `[2600:4040:5204::1574:24ae]:80`. + The IPv6 address is enclosed in square brackets. + type: string + type: object + maxItems: 10 + minItems: 1 + type: array + required: + - extractFrom + type: object slowStart: description: |- SlowStart defines the configuration related to the slow start load balancer policy. diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml index 22b5e3c1a39..2ed348e214e 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml @@ -745,6 +745,35 @@ spec: field must be set. rule: 'self.type == ''Cookie'' ? has(self.cookie) : !has(self.cookie)' + endpointOverride: + description: |- + EndpointOverride defines the configuration for endpoint override. + When specified, the load balancer will attempt to route requests to endpoints + based on the override information extracted from request headers or metadata. + If the override endpoints are not available, the configured load balancer policy will be used as fallback. + properties: + extractFrom: + description: ExtractFrom defines the sources to + extract endpoint override information from. + items: + description: EndpointOverrideExtractFrom defines + a source to extract endpoint override information + from. + properties: + header: + description: |- + Header defines the header to get the override endpoint addresses. + The header value must specify at least one endpoint in `IP:Port` format or multiple endpoints in `IP:Port,IP:Port,...` format. + For example `10.0.0.5:8080` or `[2600:4040:5204::1574:24ae]:80`. + The IPv6 address is enclosed in square brackets. + type: string + type: object + maxItems: 10 + minItems: 1 + type: array + required: + - extractFrom + type: object slowStart: description: |- SlowStart defines the configuration related to the slow start load balancer policy. diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml index d59643e970a..6302ee4716c 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml @@ -11340,6 +11340,37 @@ spec: be set. rule: 'self.type == ''Cookie'' ? has(self.cookie) : !has(self.cookie)' + endpointOverride: + description: |- + EndpointOverride defines the configuration for endpoint override. + When specified, the load balancer will attempt to route requests to endpoints + based on the override information extracted from request headers or metadata. + If the override endpoints are not available, the configured load balancer policy will be used as fallback. + properties: + extractFrom: + description: ExtractFrom defines + the sources to extract endpoint + override information from. + items: + description: EndpointOverrideExtractFrom + defines a source to extract + endpoint override information + from. + properties: + header: + description: |- + Header defines the header to get the override endpoint addresses. + The header value must specify at least one endpoint in `IP:Port` format or multiple endpoints in `IP:Port,IP:Port,...` format. + For example `10.0.0.5:8080` or `[2600:4040:5204::1574:24ae]:80`. + The IPv6 address is enclosed in square brackets. + type: string + type: object + maxItems: 10 + minItems: 1 + type: array + required: + - extractFrom + type: object slowStart: description: |- SlowStart defines the configuration related to the slow start load balancer policy. @@ -12420,6 +12451,37 @@ spec: be set. rule: 'self.type == ''Cookie'' ? has(self.cookie) : !has(self.cookie)' + endpointOverride: + description: |- + EndpointOverride defines the configuration for endpoint override. + When specified, the load balancer will attempt to route requests to endpoints + based on the override information extracted from request headers or metadata. + If the override endpoints are not available, the configured load balancer policy will be used as fallback. + properties: + extractFrom: + description: ExtractFrom defines + the sources to extract endpoint + override information from. + items: + description: EndpointOverrideExtractFrom + defines a source to extract + endpoint override information + from. + properties: + header: + description: |- + Header defines the header to get the override endpoint addresses. + The header value must specify at least one endpoint in `IP:Port` format or multiple endpoints in `IP:Port,IP:Port,...` format. + For example `10.0.0.5:8080` or `[2600:4040:5204::1574:24ae]:80`. + The IPv6 address is enclosed in square brackets. + type: string + type: object + maxItems: 10 + minItems: 1 + type: array + required: + - extractFrom + type: object slowStart: description: |- SlowStart defines the configuration related to the slow start load balancer policy. @@ -13585,6 +13647,36 @@ spec: the cookie field must be set. rule: 'self.type == ''Cookie'' ? has(self.cookie) : !has(self.cookie)' + endpointOverride: + description: |- + EndpointOverride defines the configuration for endpoint override. + When specified, the load balancer will attempt to route requests to endpoints + based on the override information extracted from request headers or metadata. + If the override endpoints are not available, the configured load balancer policy will be used as fallback. + properties: + extractFrom: + description: ExtractFrom defines the + sources to extract endpoint override + information from. + items: + description: EndpointOverrideExtractFrom + defines a source to extract endpoint + override information from. + properties: + header: + description: |- + Header defines the header to get the override endpoint addresses. + The header value must specify at least one endpoint in `IP:Port` format or multiple endpoints in `IP:Port,IP:Port,...` format. + For example `10.0.0.5:8080` or `[2600:4040:5204::1574:24ae]:80`. + The IPv6 address is enclosed in square brackets. + type: string + type: object + maxItems: 10 + minItems: 1 + type: array + required: + - extractFrom + type: object slowStart: description: |- SlowStart defines the configuration related to the slow start load balancer policy. @@ -14668,6 +14760,36 @@ spec: the cookie field must be set. rule: 'self.type == ''Cookie'' ? has(self.cookie) : !has(self.cookie)' + endpointOverride: + description: |- + EndpointOverride defines the configuration for endpoint override. + When specified, the load balancer will attempt to route requests to endpoints + based on the override information extracted from request headers or metadata. + If the override endpoints are not available, the configured load balancer policy will be used as fallback. + properties: + extractFrom: + description: ExtractFrom defines the sources + to extract endpoint override information + from. + items: + description: EndpointOverrideExtractFrom + defines a source to extract endpoint override + information from. + properties: + header: + description: |- + Header defines the header to get the override endpoint addresses. + The header value must specify at least one endpoint in `IP:Port` format or multiple endpoints in `IP:Port,IP:Port,...` format. + For example `10.0.0.5:8080` or `[2600:4040:5204::1574:24ae]:80`. + The IPv6 address is enclosed in square brackets. + type: string + type: object + maxItems: 10 + minItems: 1 + type: array + required: + - extractFrom + type: object slowStart: description: |- SlowStart defines the configuration related to the slow start load balancer policy. diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml index 04f43549c7c..ea1b263acad 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml @@ -1255,6 +1255,35 @@ spec: cookie field must be set. rule: 'self.type == ''Cookie'' ? has(self.cookie) : !has(self.cookie)' + endpointOverride: + description: |- + EndpointOverride defines the configuration for endpoint override. + When specified, the load balancer will attempt to route requests to endpoints + based on the override information extracted from request headers or metadata. + If the override endpoints are not available, the configured load balancer policy will be used as fallback. + properties: + extractFrom: + description: ExtractFrom defines the sources to + extract endpoint override information from. + items: + description: EndpointOverrideExtractFrom defines + a source to extract endpoint override information + from. + properties: + header: + description: |- + Header defines the header to get the override endpoint addresses. + The header value must specify at least one endpoint in `IP:Port` format or multiple endpoints in `IP:Port,IP:Port,...` format. + For example `10.0.0.5:8080` or `[2600:4040:5204::1574:24ae]:80`. + The IPv6 address is enclosed in square brackets. + type: string + type: object + maxItems: 10 + minItems: 1 + type: array + required: + - extractFrom + type: object slowStart: description: |- SlowStart defines the configuration related to the slow start load balancer policy. @@ -2233,6 +2262,35 @@ spec: cookie field must be set. rule: 'self.type == ''Cookie'' ? has(self.cookie) : !has(self.cookie)' + endpointOverride: + description: |- + EndpointOverride defines the configuration for endpoint override. + When specified, the load balancer will attempt to route requests to endpoints + based on the override information extracted from request headers or metadata. + If the override endpoints are not available, the configured load balancer policy will be used as fallback. + properties: + extractFrom: + description: ExtractFrom defines the sources to + extract endpoint override information from. + items: + description: EndpointOverrideExtractFrom defines + a source to extract endpoint override information + from. + properties: + header: + description: |- + Header defines the header to get the override endpoint addresses. + The header value must specify at least one endpoint in `IP:Port` format or multiple endpoints in `IP:Port,IP:Port,...` format. + For example `10.0.0.5:8080` or `[2600:4040:5204::1574:24ae]:80`. + The IPv6 address is enclosed in square brackets. + type: string + type: object + maxItems: 10 + minItems: 1 + type: array + required: + - extractFrom + type: object slowStart: description: |- SlowStart defines the configuration related to the slow start load balancer policy. @@ -3417,6 +3475,36 @@ spec: the cookie field must be set. rule: 'self.type == ''Cookie'' ? has(self.cookie) : !has(self.cookie)' + endpointOverride: + description: |- + EndpointOverride defines the configuration for endpoint override. + When specified, the load balancer will attempt to route requests to endpoints + based on the override information extracted from request headers or metadata. + If the override endpoints are not available, the configured load balancer policy will be used as fallback. + properties: + extractFrom: + description: ExtractFrom defines the sources + to extract endpoint override information + from. + items: + description: EndpointOverrideExtractFrom + defines a source to extract endpoint + override information from. + properties: + header: + description: |- + Header defines the header to get the override endpoint addresses. + The header value must specify at least one endpoint in `IP:Port` format or multiple endpoints in `IP:Port,IP:Port,...` format. + For example `10.0.0.5:8080` or `[2600:4040:5204::1574:24ae]:80`. + The IPv6 address is enclosed in square brackets. + type: string + type: object + maxItems: 10 + minItems: 1 + type: array + required: + - extractFrom + type: object slowStart: description: |- SlowStart defines the configuration related to the slow start load balancer policy. @@ -4636,6 +4724,35 @@ spec: cookie field must be set. rule: 'self.type == ''Cookie'' ? has(self.cookie) : !has(self.cookie)' + endpointOverride: + description: |- + EndpointOverride defines the configuration for endpoint override. + When specified, the load balancer will attempt to route requests to endpoints + based on the override information extracted from request headers or metadata. + If the override endpoints are not available, the configured load balancer policy will be used as fallback. + properties: + extractFrom: + description: ExtractFrom defines the sources to + extract endpoint override information from. + items: + description: EndpointOverrideExtractFrom defines + a source to extract endpoint override information + from. + properties: + header: + description: |- + Header defines the header to get the override endpoint addresses. + The header value must specify at least one endpoint in `IP:Port` format or multiple endpoints in `IP:Port,IP:Port,...` format. + For example `10.0.0.5:8080` or `[2600:4040:5204::1574:24ae]:80`. + The IPv6 address is enclosed in square brackets. + type: string + type: object + maxItems: 10 + minItems: 1 + type: array + required: + - extractFrom + type: object slowStart: description: |- SlowStart defines the configuration related to the slow start load balancer policy. diff --git a/internal/gatewayapi/clustersettings.go b/internal/gatewayapi/clustersettings.go index 8fe0d4178e2..6473e9b9dd6 100644 --- a/internal/gatewayapi/clustersettings.go +++ b/internal/gatewayapi/clustersettings.go @@ -312,6 +312,11 @@ func buildLoadBalancer(policy egv1a1.ClusterSettings) (*ir.LoadBalancer, error) } } + // Add EndpointOverride if specified + if policy.LoadBalancer.EndpointOverride != nil { + lb.EndpointOverride = buildEndpointOverride(*policy.LoadBalancer.EndpointOverride) + } + return lb, nil } @@ -342,6 +347,23 @@ func buildConsistentHashLoadBalancer(policy egv1a1.LoadBalancer) (*ir.Consistent return consistentHash, nil } +func buildEndpointOverride(policy egv1a1.EndpointOverride) *ir.EndpointOverride { + endpointOverride := &ir.EndpointOverride{} + + // Convert extract from sources + for _, source := range policy.ExtractFrom { + irSource := ir.EndpointOverrideExtractFrom{} + + if source.Header != nil { + irSource.Header = source.Header + } + + endpointOverride.ExtractFrom = append(endpointOverride.ExtractFrom, irSource) + } + + return endpointOverride +} + func buildProxyProtocol(policy egv1a1.ClusterSettings) *ir.ProxyProtocol { if policy.ProxyProtocol == nil { return nil diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-endpointoverride-loadbalancer-multiple-mixed.in.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-endpointoverride-loadbalancer-multiple-mixed.in.yaml new file mode 100644 index 00000000000..c5c7d54a68e --- /dev/null +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-endpointoverride-loadbalancer-multiple-mixed.in.yaml @@ -0,0 +1,113 @@ +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 +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-1 + sectionName: http + rules: + - matches: + - path: + value: "/header-override" + backendRefs: + - name: service-1 + port: 8080 + +backendTrafficPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: default + name: policy-for-header-override + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + loadBalancer: + type: RoundRobin + endpointOverride: + extractFrom: + - header: "x-fallback-host" + +services: +- apiVersion: v1 + kind: Service + metadata: + namespace: default + name: service-1 + spec: + clusterIP: 1.2.3.4 + ports: + - port: 8080 + name: http + protocol: TCP + targetPort: 8080 +- apiVersion: v1 + kind: Service + metadata: + namespace: default + name: service-2 + spec: + clusterIP: 5.6.7.8 + ports: + - port: 8080 + name: http + protocol: TCP + targetPort: 8080 +endpointSlices: +- apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: endpointslice-service-1 + namespace: default + labels: + kubernetes.io/service-name: service-1 + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - "1.2.3.4" + conditions: + ready: true +- apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: endpointslice-service-2 + namespace: default + labels: + kubernetes.io/service-name: service-2 + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - "5.6.7.8" + conditions: + ready: true diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-endpointoverride-loadbalancer-multiple-mixed.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-endpointoverride-loadbalancer-multiple-mixed.out.yaml new file mode 100644 index 00000000000..0b7b0096246 --- /dev/null +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-endpointoverride-loadbalancer-multiple-mixed.out.yaml @@ -0,0 +1,210 @@ +backendTrafficPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-header-override + namespace: default + spec: + loadBalancer: + endpointOverride: + extractFrom: + - header: x-fallback-host + type: RoundRobin + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + sectionName: http + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: gateway.envoyproxy.io/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: 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 +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: /header-override + 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 +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + 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 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + json: + - path: /dev/stdout + globalResources: + proxyServiceCluster: + name: envoy-gateway/gateway-1 + settings: + - addressType: IP + endpoints: + - host: 7.6.5.4 + port: 8080 + zone: zone1 + metadata: + 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 + hostnames: + - '*' + isHTTP2: false + 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: HTTPRoute + name: httproute-1 + namespace: default + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 1.2.3.4 + port: 8080 + - host: 7.7.7.7 + port: 8080 + metadata: + 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 + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: /header-override + traffic: + loadBalancer: + endpointOverride: + extractFrom: + - header: x-fallback-host + roundRobin: {} + readyListener: + address: 0.0.0.0 + ipFamily: IPv4 + path: /ready + port: 19003 diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-endpointoverride-loadbalancer-single-header.in.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-endpointoverride-loadbalancer-single-header.in.yaml new file mode 100644 index 00000000000..d468cafeab5 --- /dev/null +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-endpointoverride-loadbalancer-single-header.in.yaml @@ -0,0 +1,82 @@ +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 +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-1 + sectionName: http + rules: + - matches: + - path: + value: "/header-override" + backendRefs: + - name: service-1 + port: 8080 +backendTrafficPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: default + name: policy-for-header-override + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + loadBalancer: + type: LeastRequest + endpointOverride: + extractFrom: + - header: "x-fallback-host" +services: +- apiVersion: v1 + kind: Service + metadata: + namespace: default + name: service-1 + spec: + clusterIP: 1.2.3.4 + ports: + - port: 8080 + name: http + protocol: TCP + targetPort: 8080 +endpointSlices: +- apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: endpointslice-service-1 + namespace: default + labels: + kubernetes.io/service-name: service-1 + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - "1.2.3.4" + conditions: + ready: true diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-endpointoverride-loadbalancer-single-header.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-endpointoverride-loadbalancer-single-header.out.yaml new file mode 100644 index 00000000000..1c0460c3793 --- /dev/null +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-endpointoverride-loadbalancer-single-header.out.yaml @@ -0,0 +1,210 @@ +backendTrafficPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-header-override + namespace: default + spec: + loadBalancer: + endpointOverride: + extractFrom: + - header: x-fallback-host + type: LeastRequest + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + sectionName: http + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: gateway.envoyproxy.io/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: 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 +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: /header-override + 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 +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + 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 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + json: + - path: /dev/stdout + globalResources: + proxyServiceCluster: + name: envoy-gateway/gateway-1 + settings: + - addressType: IP + endpoints: + - host: 7.6.5.4 + port: 8080 + zone: zone1 + metadata: + 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 + hostnames: + - '*' + isHTTP2: false + 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: HTTPRoute + name: httproute-1 + namespace: default + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 1.2.3.4 + port: 8080 + - host: 7.7.7.7 + port: 8080 + metadata: + 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 + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: /header-override + traffic: + loadBalancer: + endpointOverride: + extractFrom: + - header: x-fallback-host + leastRequest: {} + readyListener: + address: 0.0.0.0 + ipFamily: IPv4 + path: /ready + port: 19003 diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-endpointoverride-loadbalancer.in.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-endpointoverride-loadbalancer.in.yaml new file mode 100644 index 00000000000..ddca95774da --- /dev/null +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-endpointoverride-loadbalancer.in.yaml @@ -0,0 +1,126 @@ +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 +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-1 + sectionName: http + rules: + - matches: + - path: + value: "/header-override" + backendRefs: + - name: service-1 + port: 8080 +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-2 + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/metadata-override" + backendRefs: + - name: service-2 + port: 8080 +backendTrafficPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: default + name: policy-for-header-override + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + loadBalancer: + type: RoundRobin + endpointOverride: + extractFrom: + - header: "x-custom-host" +services: +- apiVersion: v1 + kind: Service + metadata: + namespace: default + name: service-1 + spec: + clusterIP: 1.2.3.4 + ports: + - port: 8080 + name: http + protocol: TCP + targetPort: 8080 +- apiVersion: v1 + kind: Service + metadata: + namespace: default + name: service-2 + spec: + clusterIP: 5.6.7.8 + ports: + - port: 8080 + name: http + protocol: TCP + targetPort: 8080 +endpointSlices: +- apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: endpointslice-service-1 + namespace: default + labels: + kubernetes.io/service-name: service-1 + addressType: IPv4 + endpoints: + - addresses: + - "10.0.0.1" + ports: + - name: http + port: 8080 + protocol: TCP +- apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: endpointslice-service-2 + namespace: default + labels: + kubernetes.io/service-name: service-2 + addressType: IPv4 + endpoints: + - addresses: + - "10.0.0.2" + ports: + - name: http + port: 8080 + protocol: TCP diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-endpointoverride-loadbalancer.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-endpointoverride-loadbalancer.out.yaml new file mode 100644 index 00000000000..48da954c071 --- /dev/null +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-endpointoverride-loadbalancer.out.yaml @@ -0,0 +1,279 @@ +backendTrafficPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-header-override + namespace: default + spec: + loadBalancer: + endpointOverride: + extractFrom: + - header: x-custom-host + type: RoundRobin + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + sectionName: http + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: gateway.envoyproxy.io/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 2 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: /header-override + 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 +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-2 + namespace: default + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-2 + port: 8080 + matches: + - path: + value: /metadata-override + 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 +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + 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 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + json: + - path: /dev/stdout + globalResources: + proxyServiceCluster: + name: envoy-gateway/gateway-1 + settings: + - addressType: IP + endpoints: + - host: 7.6.5.4 + port: 8080 + zone: zone1 + metadata: + 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 + hostnames: + - '*' + isHTTP2: false + 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: HTTPRoute + name: httproute-2 + namespace: default + name: httproute/default/httproute-2/rule/0 + settings: + - addressType: IP + endpoints: + - host: 10.0.0.2 + port: 8080 + - host: 7.7.7.7 + port: 8080 + metadata: + name: service-2 + namespace: default + sectionName: "8080" + name: httproute/default/httproute-2/rule/0/backend/0 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + isHTTP2: false + metadata: + kind: HTTPRoute + name: httproute-2 + namespace: default + name: httproute/default/httproute-2/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: /metadata-override + - destination: + metadata: + kind: HTTPRoute + name: httproute-1 + namespace: default + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 10.0.0.1 + port: 8080 + - host: 7.7.7.7 + port: 8080 + metadata: + 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 + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: /header-override + traffic: + loadBalancer: + endpointOverride: + extractFrom: + - header: x-custom-host + roundRobin: {} + 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 53284390dce..24d68a9164f 100644 --- a/internal/ir/xds.go +++ b/internal/ir/xds.go @@ -2534,6 +2534,11 @@ type LoadBalancer struct { ConsistentHash *ConsistentHash `json:"consistentHash,omitempty" yaml:"consistentHash,omitempty"` // PreferLocal defines the configuration related to the distribution of requests between locality zones. PreferLocal *PreferLocalZone `json:"preferLocal,omitempty" yaml:"preferLocal,omitempty"` + // EndpointOverride defines the configuration for endpoint override. + // When specified, the load balancer will attempt to route requests to endpoints + // based on the override information extracted from request headers or metadata. + // If the override endpoints are not available, the configured load balancer policy will be used as fallback. + EndpointOverride *EndpointOverride `json:"endpointOverride,omitempty" yaml:"endpointOverride,omitempty"` } // Validate the fields within the LoadBalancer structure @@ -3232,3 +3237,31 @@ type ForceLocalZone struct { // override. This is useful for protecting zones with fewer endpoints. MinEndpointsInZoneThreshold *uint32 `json:"minEndpointsInZoneThreshold,omitempty" yaml:"minEndpointsInZoneThreshold,omitempty"` } + +// EndpointOverride defines the configuration for endpoint override. +// +k8s:deepcopy-gen=true +type EndpointOverride struct { + // ExtractFrom defines the sources to extract endpoint override information from. + ExtractFrom []EndpointOverrideExtractFrom `json:"extractFrom" yaml:"extractFrom"` +} + +// EndpointOverrideExtractFrom defines a source to extract endpoint override information from. +// +k8s:deepcopy-gen=true +type EndpointOverrideExtractFrom struct { + // Header defines the header to get the override endpoint addresses. + Header *string `json:"header,omitempty" yaml:"header,omitempty"` +} + +// LoadBalancerType defines the type of load balancer for IR. +type LoadBalancerType string + +const ( + // LeastRequestLoadBalancer is the least request load balancer type. + LeastRequestLoadBalancer LoadBalancerType = "LeastRequest" + // RoundRobinLoadBalancer is the round robin load balancer type. + RoundRobinLoadBalancer LoadBalancerType = "RoundRobin" + // RandomLoadBalancer is the random load balancer type. + RandomLoadBalancer LoadBalancerType = "Random" + // ConsistentHashLoadBalancer is the consistent hash load balancer type. + ConsistentHashLoadBalancer LoadBalancerType = "ConsistentHash" +) diff --git a/internal/ir/zz_generated.deepcopy.go b/internal/ir/zz_generated.deepcopy.go index f2c5fce176c..a6c28157ce9 100644 --- a/internal/ir/zz_generated.deepcopy.go +++ b/internal/ir/zz_generated.deepcopy.go @@ -982,6 +982,48 @@ func (in *DestinationSetting) DeepCopy() *DestinationSetting { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EndpointOverride) DeepCopyInto(out *EndpointOverride) { + *out = *in + if in.ExtractFrom != nil { + in, out := &in.ExtractFrom, &out.ExtractFrom + *out = make([]EndpointOverrideExtractFrom, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EndpointOverride. +func (in *EndpointOverride) DeepCopy() *EndpointOverride { + if in == nil { + return nil + } + out := new(EndpointOverride) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EndpointOverrideExtractFrom) DeepCopyInto(out *EndpointOverrideExtractFrom) { + *out = *in + if in.Header != nil { + in, out := &in.Header, &out.Header + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EndpointOverrideExtractFrom. +func (in *EndpointOverrideExtractFrom) DeepCopy() *EndpointOverrideExtractFrom { + if in == nil { + return nil + } + out := new(EndpointOverrideExtractFrom) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *EnvoyExtensionFeatures) DeepCopyInto(out *EnvoyExtensionFeatures) { *out = *in @@ -2325,6 +2367,11 @@ func (in *LoadBalancer) DeepCopyInto(out *LoadBalancer) { *out = new(PreferLocalZone) (*in).DeepCopyInto(*out) } + if in.EndpointOverride != nil { + in, out := &in.EndpointOverride, &out.EndpointOverride + *out = new(EndpointOverride) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LoadBalancer. diff --git a/internal/xds/translator/cluster.go b/internal/xds/translator/cluster.go index 565eface4d2..3aa5d495208 100644 --- a/internal/xds/translator/cluster.go +++ b/internal/xds/translator/cluster.go @@ -23,6 +23,7 @@ import ( commonv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/load_balancing_policies/common/v3" least_requestv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/load_balancing_policies/least_request/v3" maglevv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/load_balancing_policies/maglev/v3" + override_hostv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/load_balancing_policies/override_host/v3" randomv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/load_balancing_policies/random/v3" round_robinv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/load_balancing_policies/round_robin/v3" proxyprotocolv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/proxy_protocol/v3" @@ -232,7 +233,23 @@ func buildXdsCluster(args *xdsClusterArgs) (*buildClusterResult, error) { // Set Load Balancer policy //nolint:gocritic + // Use traditional load balancer policies switch { + case args.loadBalancer != nil && args.loadBalancer.EndpointOverride != nil: + // For EndpointOverride, we use LoadBalancingPolicy with HostOverride + // and the configured load balancer type as fallback policy + endpointOverridePolicy, err := buildEndpointOverrideLoadBalancingPolicy(args.loadBalancer) + if err != nil { + return nil, err + } + cluster.LbPolicy = clusterv3.Cluster_CLUSTER_PROVIDED + cluster.LoadBalancingPolicy = endpointOverridePolicy + // Clear CommonLbConfig fields that conflict with LoadBalancingPolicy + // This is required because Envoy doesn't allow both LoadBalancingPolicy and + // CommonLbConfig partial fields to be set simultaneously + if cluster.CommonLbConfig != nil { + cluster.CommonLbConfig.LocalityConfigSpecifier = nil + } case args.loadBalancer == nil: cluster.LbPolicy = clusterv3.Cluster_LEAST_REQUEST leastRequest := &least_requestv3.LeastRequest{ @@ -1195,3 +1212,129 @@ func buildHTTP2Settings(opts *ir.HTTP2Settings) *corev3.Http2ProtocolOptions { return out } + +// buildEndpointOverrideLoadBalancingPolicy builds the Envoy LoadBalancingPolicy for EndpointOverride +func buildEndpointOverrideLoadBalancingPolicy(loadBalancer *ir.LoadBalancer) (*clusterv3.LoadBalancingPolicy, error) { + // Build override host sources from EndpointOverride + var overrideHostSources []*override_hostv3.OverrideHost_OverrideHostSource + + for _, source := range loadBalancer.EndpointOverride.ExtractFrom { + overrideSource := &override_hostv3.OverrideHost_OverrideHostSource{} + + if source.Header != nil { + overrideSource.Header = *source.Header + } + + overrideHostSources = append(overrideHostSources, overrideSource) + } + + // Determine fallback policy based on the configured load balancer type + var fallbackType ir.LoadBalancerType + switch { + case loadBalancer.LeastRequest != nil: + fallbackType = ir.LeastRequestLoadBalancer + case loadBalancer.RoundRobin != nil: + fallbackType = ir.RoundRobinLoadBalancer + case loadBalancer.Random != nil: + fallbackType = ir.RandomLoadBalancer + case loadBalancer.ConsistentHash != nil: + fallbackType = ir.ConsistentHashLoadBalancer + default: + // Default to LeastRequest if no specific type is set + fallbackType = ir.LeastRequestLoadBalancer + } + + // Build fallback policy + fallbackPolicy, err := buildFallbackLoadBalancingPolicy(fallbackType) + if err != nil { + return nil, fmt.Errorf("failed to build fallback policy: %w", err) + } + + // Build override host policy + overrideHostPolicy := &override_hostv3.OverrideHost{ + OverrideHostSources: overrideHostSources, + FallbackPolicy: fallbackPolicy, + } + + typedOverrideHostPolicy, err := anypb.New(overrideHostPolicy) + if err != nil { + return nil, fmt.Errorf("failed to marshal override host policy: %w", err) + } + + return &clusterv3.LoadBalancingPolicy{ + Policies: []*clusterv3.LoadBalancingPolicy_Policy{{ + TypedExtensionConfig: &corev3.TypedExtensionConfig{ + Name: "envoy.load_balancing_policies.override_host", + TypedConfig: typedOverrideHostPolicy, + }, + }}, + }, nil +} + +// buildFallbackLoadBalancingPolicy builds a fallback LoadBalancingPolicy for HostOverride +func buildFallbackLoadBalancingPolicy(fallbackType ir.LoadBalancerType) (*clusterv3.LoadBalancingPolicy, error) { + switch fallbackType { + case ir.LeastRequestLoadBalancer: + fallbackPolicyAny, err := anypb.New(&least_requestv3.LeastRequest{}) + if err != nil { + return nil, fmt.Errorf("failed to marshal LeastRequest policy: %w", err) + } + return &clusterv3.LoadBalancingPolicy{ + Policies: []*clusterv3.LoadBalancingPolicy_Policy{ + { + TypedExtensionConfig: &corev3.TypedExtensionConfig{ + Name: "envoy.load_balancing_policies.least_request", + TypedConfig: fallbackPolicyAny, + }, + }, + }, + }, nil + case ir.RoundRobinLoadBalancer: + fallbackPolicyAny, err := anypb.New(&round_robinv3.RoundRobin{}) + if err != nil { + return nil, fmt.Errorf("failed to marshal RoundRobin policy: %w", err) + } + return &clusterv3.LoadBalancingPolicy{ + Policies: []*clusterv3.LoadBalancingPolicy_Policy{ + { + TypedExtensionConfig: &corev3.TypedExtensionConfig{ + Name: "envoy.load_balancing_policies.round_robin", + TypedConfig: fallbackPolicyAny, + }, + }, + }, + }, nil + case ir.RandomLoadBalancer: + fallbackPolicyAny, err := anypb.New(&randomv3.Random{}) + if err != nil { + return nil, fmt.Errorf("failed to marshal Random policy: %w", err) + } + return &clusterv3.LoadBalancingPolicy{ + Policies: []*clusterv3.LoadBalancingPolicy_Policy{ + { + TypedExtensionConfig: &corev3.TypedExtensionConfig{ + Name: "envoy.load_balancing_policies.random", + TypedConfig: fallbackPolicyAny, + }, + }, + }, + }, nil + case ir.ConsistentHashLoadBalancer: + fallbackPolicyAny, err := anypb.New(&maglevv3.Maglev{}) + if err != nil { + return nil, fmt.Errorf("failed to marshal Maglev policy: %w", err) + } + return &clusterv3.LoadBalancingPolicy{ + Policies: []*clusterv3.LoadBalancingPolicy_Policy{ + { + TypedExtensionConfig: &corev3.TypedExtensionConfig{ + Name: "envoy.load_balancing_policies.maglev", + TypedConfig: fallbackPolicyAny, + }, + }, + }, + }, nil + default: + return nil, fmt.Errorf("unsupported fallback policy: %s", fallbackType) + } +} diff --git a/internal/xds/translator/testdata/in/xds-ir/load-balancer.yaml b/internal/xds/translator/testdata/in/xds-ir/load-balancer.yaml index b7b9c6690ce..1b4b84e772b 100644 --- a/internal/xds/translator/testdata/in/xds-ir/load-balancer.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/load-balancer.yaml @@ -142,3 +142,48 @@ http: - host: "1.2.3.4" port: 50000 name: "tenth-route-dest/backend/0" + - name: "eleventh-route" + hostname: "*" + traffic: + loadBalancer: + roundRobin: {} + endpointOverride: + extractFrom: + - header: "x-custom-host" + destination: + name: "eleventh-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + name: "eleventh-route-dest/backend/0" + - name: "twelfth-route" + hostname: "*" + traffic: + loadBalancer: + leastRequest: {} + endpointOverride: + extractFrom: + - header: "x-fallback-host" + destination: + name: "twelfth-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + name: "twelfth-route-dest/backend/0" + - name: "thirteen-route" + hostname: "*" + traffic: + loadBalancer: + leastRequest: {} + endpointOverride: + extractFrom: + - header: "x-custom-host" + destination: + name: "thirteen-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + name: "thirteen-route-dest/backend/0" diff --git a/internal/xds/translator/testdata/out/xds-ir/load-balancer.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/load-balancer.clusters.yaml index 5edf1ebaf2f..f11b34d81bd 100644 --- a/internal/xds/translator/testdata/out/xds-ir/load-balancer.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/load-balancer.clusters.yaml @@ -231,3 +231,93 @@ name: tenth-route-dest perConnectionBufferLimitBytes: 32768 type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_PREFERRED + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: eleventh-route-dest + ignoreHealthOnHostRemoval: true + lbPolicy: CLUSTER_PROVIDED + loadBalancingPolicy: + policies: + - typedExtensionConfig: + name: envoy.load_balancing_policies.override_host + typedConfig: + '@type': type.googleapis.com/envoy.extensions.load_balancing_policies.override_host.v3.OverrideHost + fallbackPolicy: + policies: + - typedExtensionConfig: + name: envoy.load_balancing_policies.round_robin + typedConfig: + '@type': type.googleapis.com/envoy.extensions.load_balancing_policies.round_robin.v3.RoundRobin + overrideHostSources: + - header: x-custom-host + name: eleventh-route-dest + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_PREFERRED + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: twelfth-route-dest + ignoreHealthOnHostRemoval: true + lbPolicy: CLUSTER_PROVIDED + loadBalancingPolicy: + policies: + - typedExtensionConfig: + name: envoy.load_balancing_policies.override_host + typedConfig: + '@type': type.googleapis.com/envoy.extensions.load_balancing_policies.override_host.v3.OverrideHost + fallbackPolicy: + policies: + - typedExtensionConfig: + name: envoy.load_balancing_policies.least_request + typedConfig: + '@type': type.googleapis.com/envoy.extensions.load_balancing_policies.least_request.v3.LeastRequest + overrideHostSources: + - header: x-fallback-host + name: twelfth-route-dest + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_PREFERRED + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: thirteen-route-dest + ignoreHealthOnHostRemoval: true + lbPolicy: CLUSTER_PROVIDED + loadBalancingPolicy: + policies: + - typedExtensionConfig: + name: envoy.load_balancing_policies.override_host + typedConfig: + '@type': type.googleapis.com/envoy.extensions.load_balancing_policies.override_host.v3.OverrideHost + fallbackPolicy: + policies: + - typedExtensionConfig: + name: envoy.load_balancing_policies.least_request + typedConfig: + '@type': type.googleapis.com/envoy.extensions.load_balancing_policies.least_request.v3.LeastRequest + overrideHostSources: + - header: x-custom-host + name: thirteen-route-dest + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/load-balancer.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/load-balancer.endpoints.yaml index 0295b1c7174..ccc67bb12eb 100644 --- a/internal/xds/translator/testdata/out/xds-ir/load-balancer.endpoints.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/load-balancer.endpoints.yaml @@ -118,3 +118,39 @@ loadBalancingWeight: 1 locality: region: tenth-route-dest/backend/0 +- clusterName: eleventh-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: eleventh-route-dest/backend/0 +- clusterName: twelfth-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: twelfth-route-dest/backend/0 +- clusterName: thirteen-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: thirteen-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/load-balancer.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/load-balancer.routes.yaml index 07bf30df034..f439041c514 100644 --- a/internal/xds/translator/testdata/out/xds-ir/load-balancer.routes.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/load-balancer.routes.yaml @@ -90,3 +90,24 @@ name: test upgradeConfigs: - upgradeType: websocket + - match: + prefix: / + name: eleventh-route + route: + cluster: eleventh-route-dest + upgradeConfigs: + - upgradeType: websocket + - match: + prefix: / + name: twelfth-route + route: + cluster: twelfth-route-dest + upgradeConfigs: + - upgradeType: websocket + - match: + prefix: / + name: thirteen-route + route: + cluster: thirteen-route-dest + upgradeConfigs: + - upgradeType: websocket diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md index 701a6d75d95..28817b7df16 100644 --- a/site/content/en/latest/api/extension_types.md +++ b/site/content/en/latest/api/extension_types.md @@ -1116,6 +1116,37 @@ _Appears in:_ | `IPv4AndIPv6` | IPv4AndIPv6DNSLookupFamily mean the DNS resolver will perform a lookup for both IPv4 and IPv6 families, and return all resolved
addresses. When this is used, Happy Eyeballs will be enabled for upstream connections.
| +#### EndpointOverride + + + +EndpointOverride defines the configuration for endpoint override. +This allows endpoint picking to be implemented based on request headers or metadata. +It extracts selected override endpoints from the specified sources (request headers, metadata, etc.). +If no valid endpoint in the override list, then the configured load balancing policy is used as fallback. + +_Appears in:_ +- [LoadBalancer](#loadbalancer) + +| Field | Type | Required | Default | Description | +| --- | --- | --- | --- | --- | +| `extractFrom` | _[EndpointOverrideExtractFrom](#endpointoverrideextractfrom) array_ | true | | ExtractFrom defines the sources to extract endpoint override information from. | + + +#### EndpointOverrideExtractFrom + + + +EndpointOverrideExtractFrom defines a source to extract endpoint override information from. + +_Appears in:_ +- [EndpointOverride](#endpointoverride) + +| Field | Type | Required | Default | Description | +| --- | --- | --- | --- | --- | +| `header` | _string_ | false | | Header defines the header to get the override endpoint addresses.
The header value must specify at least one endpoint in `IP:Port` format or multiple endpoints in `IP:Port,IP:Port,...` format.
For example `10.0.0.5:8080` or `[2600:4040:5204::1574:24ae]:80`.
The IPv6 address is enclosed in square brackets. | + + #### EnvironmentCustomTag @@ -3090,6 +3121,7 @@ _Appears in:_ | --- | --- | --- | --- | --- | | `type` | _[LoadBalancerType](#loadbalancertype)_ | true | | Type decides the type of Load Balancer policy.
Valid LoadBalancerType values are
"ConsistentHash",
"LeastRequest",
"Random",
"RoundRobin". | | `consistentHash` | _[ConsistentHash](#consistenthash)_ | false | | ConsistentHash defines the configuration when the load balancer type is
set to ConsistentHash | +| `endpointOverride` | _[EndpointOverride](#endpointoverride)_ | false | | EndpointOverride defines the configuration for endpoint override.
When specified, the load balancer will attempt to route requests to endpoints
based on the override information extracted from request headers or metadata.
If the override endpoints are not available, the configured load balancer policy will be used as fallback. | | `slowStart` | _[SlowStart](#slowstart)_ | false | | SlowStart defines the configuration related to the slow start load balancer policy.
If set, during slow start window, traffic sent to the newly added hosts will gradually increase.
Currently this is only supported for RoundRobin and LeastRequest load balancers | diff --git a/site/content/en/latest/tasks/traffic/load-balancing.md b/site/content/en/latest/tasks/traffic/load-balancing.md index 3c9a78450b5..4dd1b9c24d7 100644 --- a/site/content/en/latest/tasks/traffic/load-balancing.md +++ b/site/content/en/latest/tasks/traffic/load-balancing.md @@ -12,6 +12,8 @@ Envoy Gateway supports the following load balancing policies: - **Least Request**: load balancer uses different algorithms depending on whether hosts have the same or different weights. - **Consistent Hash**: load balancer implements consistent hashing to upstream hosts. +Additionally, Envoy Gateway supports **Endpoint Override** functionality that allows endpoint selection based on headers or metadata, which can be used with any of the above load balancing policies as a fallback. + Envoy Gateway introduces a new CRD called [BackendTrafficPolicy][] that allows the user to describe their desired load balancing polices. This instantiated resource can be linked to a [Gateway][], [HTTPRoute][] or [GRPCRoute][] resource. If `loadBalancer` is not specified in [BackendTrafficPolicy][], the default load balancing policy is `Least Request`. @@ -913,6 +915,133 @@ curl -v --header "Host: www.example.com" http://${GATEWAY_HOST}/cookie "pod": "backend-69fcff487f-5dxz9" ``` +## Endpoint Override + +This example will create a Load Balancer with Endpoint Override functionality via [BackendTrafficPolicy][]. + +The Endpoint Override feature allows endpoint selection based on headers. It can derive the target endpoint from HTTP request headers. + +When the specified override endpoint is not available or invalid, the load balancer will fall back to the configured load balancing policy. + +### Header-based Endpoint Override + +This example will create a Load Balancer with Header-based Endpoint Override functionality. + +{{< tabpane text=true >}} +{{% tab header="Apply from stdin" %}} + +```shell +cat <}} + +First, get one of the backend pod IPs to use as the override endpoint: + +```shell +BACKEND_POD_IP=$(kubectl get pods -l app=backend -o jsonpath='{.items[0].status.podIP}') +echo "Backend Pod IP: $BACKEND_POD_IP" +``` + +Test with a valid pod IP in the header - all requests should go to the specific pod: + +```shell +for i in {1..10}; do + curl -s -H "Host: www.example.com" -H "x-custom-host: $BACKEND_POD_IP:3000" \ + http://${GATEWAY_HOST}/endpoint-override-header | jq -r '.pod' +done +``` + +All requests should return the same pod name, demonstrating that the endpoint override is working. + +Test with an invalid IP in the header - requests should fall back to round robin: + +```shell +for i in {1..10}; do + curl -s -H "Host: www.example.com" -H "x-custom-host: 192.168.1.100:3000" \ + http://${GATEWAY_HOST}/endpoint-override-header | jq -r '.pod' +done +``` + +You should see requests distributed across different pods using the round robin fallback policy. [Envoy load balancing]: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/upstream/load_balancing/overview [BackendTrafficPolicy]: ../../../api/extension_types#backendtrafficpolicy diff --git a/test/cel-validation/backendtrafficpolicy_test.go b/test/cel-validation/backendtrafficpolicy_test.go index 5770b378d93..f0259ad112e 100644 --- a/test/cel-validation/backendtrafficpolicy_test.go +++ b/test/cel-validation/backendtrafficpolicy_test.go @@ -1998,6 +1998,65 @@ func TestBackendTrafficPolicyTarget(t *testing.T) { }, wantErrors: []string{}, }, + { + desc: "endpointOverride field with valid configuration", + mutate: func(btp *egv1a1.BackendTrafficPolicy) { + btp.Spec = egv1a1.BackendTrafficPolicySpec{ + PolicyTargetReferences: egv1a1.PolicyTargetReferences{ + TargetRef: &gwapiv1a2.LocalPolicyTargetReferenceWithSectionName{ + LocalPolicyTargetReference: gwapiv1a2.LocalPolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("Gateway"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + }, + ClusterSettings: egv1a1.ClusterSettings{ + LoadBalancer: &egv1a1.LoadBalancer{ + Type: egv1a1.RoundRobinLoadBalancerType, + EndpointOverride: &egv1a1.EndpointOverride{ + ExtractFrom: []egv1a1.EndpointOverrideExtractFrom{ + { + Header: ptr.To("x-custom-host"), + }, + }, + }, + }, + }, + } + }, + wantErrors: []string{}, + }, + + { + desc: "endpointOverride source with valid header only", + mutate: func(btp *egv1a1.BackendTrafficPolicy) { + btp.Spec = egv1a1.BackendTrafficPolicySpec{ + PolicyTargetReferences: egv1a1.PolicyTargetReferences{ + TargetRef: &gwapiv1a2.LocalPolicyTargetReferenceWithSectionName{ + LocalPolicyTargetReference: gwapiv1a2.LocalPolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("Gateway"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + }, + ClusterSettings: egv1a1.ClusterSettings{ + LoadBalancer: &egv1a1.LoadBalancer{ + Type: egv1a1.RoundRobinLoadBalancerType, + EndpointOverride: &egv1a1.EndpointOverride{ + ExtractFrom: []egv1a1.EndpointOverrideExtractFrom{ + { + Header: ptr.To("x-custom-host"), + }, + }, + }, + }, + }, + } + }, + wantErrors: []string{}, + }, } for _, tc := range cases { diff --git a/test/e2e/testdata/load_balancing_endpoint_override.yaml b/test/e2e/testdata/load_balancing_endpoint_override.yaml new file mode 100644 index 00000000000..37350151070 --- /dev/null +++ b/test/e2e/testdata/load_balancing_endpoint_override.yaml @@ -0,0 +1,86 @@ +apiVersion: v1 +kind: Service +metadata: + name: lb-backend-endpointoverride + namespace: gateway-conformance-infra +spec: + selector: + app: lb-backend-endpointoverride + ports: + - protocol: TCP + port: 8080 + targetPort: 3000 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: lb-backend-endpointoverride + namespace: gateway-conformance-infra + labels: + app: lb-backend-endpointoverride +spec: + replicas: 3 + selector: + matchLabels: + app: lb-backend-endpointoverride + template: + metadata: + labels: + app: lb-backend-endpointoverride + spec: + containers: + - name: backend + image: gcr.io/k8s-staging-gateway-api/echo-basic:v20231214-v1.0.0-140-gf544a46e + imagePullPolicy: IfNotPresent + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: SERVICE_NAME + value: lb-backend-endpointoverride + resources: + requests: + cpu: 10m +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: BackendTrafficPolicy +metadata: + name: endpoint-override-header-lb-policy + namespace: gateway-conformance-infra +spec: + targetRefs: + - group: gateway.networking.k8s.io + kind: HTTPRoute + name: endpoint-override-header-lb-route + loadBalancer: + type: RoundRobin + endpointOverride: + extractFrom: + - header: "x-custom-host" +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: endpoint-override-header-lb-route + namespace: gateway-conformance-infra +spec: + parentRefs: + - name: same-namespace + rules: + - matches: + - path: + type: PathPrefix + value: /endpoint-override-header + backendRefs: + - name: lb-backend-endpointoverride + port: 8080 + diff --git a/test/e2e/tests/load_balancing.go b/test/e2e/tests/load_balancing.go index 462916f76c1..9efe638f43c 100644 --- a/test/e2e/tests/load_balancing.go +++ b/test/e2e/tests/load_balancing.go @@ -20,8 +20,10 @@ import ( "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/wait" + "sigs.k8s.io/controller-runtime/pkg/client" gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" "sigs.k8s.io/gateway-api/conformance/utils/http" @@ -40,6 +42,7 @@ func init() { ConsistentHashSourceIPLoadBalancingTest, ConsistentHashHeaderLoadBalancingTest, ConsistentHashCookieLoadBalancingTest, + EndpointOverrideLoadBalancingTest, ) } @@ -358,3 +361,163 @@ var ConsistentHashCookieLoadBalancingTest = suite.ConformanceTest{ }) }, } + +var EndpointOverrideLoadBalancingTest = suite.ConformanceTest{ + ShortName: "EndpointOverrideLoadBalancing", + Description: "Test for endpoint override load balancing functionality", + Manifests: []string{"testdata/load_balancing_endpoint_override.yaml"}, + Test: func(t *testing.T, suite *suite.ConformanceTestSuite) { + const sendRequests = 10 + + ns := "gateway-conformance-infra" + headerRouteNN := types.NamespacedName{Name: "endpoint-override-header-lb-route", Namespace: ns} + gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns} + + ancestorRef := gwapiv1a2.ParentReference{ + Group: gatewayapi.GroupPtr(gwapiv1.GroupName), + Kind: gatewayapi.KindPtr(resource.KindGateway), + Namespace: gatewayapi.NamespacePtr(gwNN.Namespace), + Name: gwapiv1.ObjectName(gwNN.Name), + } + BackendTrafficPolicyMustBeAccepted(t, suite.Client, types.NamespacedName{Name: "endpoint-override-header-lb-policy", Namespace: ns}, suite.ControllerName, ancestorRef) + WaitForPods(t, suite.Client, ns, map[string]string{"app": "lb-backend-endpointoverride"}, corev1.PodRunning, PodReady) + + gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), headerRouteNN) + + // Get pods associated with the service to find valid pod IPs and names + ctx, cancel := context.WithTimeout(context.Background(), suite.TimeoutConfig.GetTimeout) + defer cancel() + + // Get pods by label selector + podList := &corev1.PodList{} + err := suite.Client.List(ctx, podList, &client.ListOptions{ + Namespace: ns, + LabelSelector: labels.SelectorFromSet(map[string]string{"app": "lb-backend-endpointoverride"}), + }) + require.NoError(t, err, "failed to list pods") + require.NotEmpty(t, podList.Items, "should have pods") + + // Create mapping of pod IP to pod name + podIPToName := make(map[string]string) + var validPodIPs []string + for _, pod := range podList.Items { + if pod.Status.Phase == corev1.PodRunning && pod.Status.PodIP != "" { + podIPToName[pod.Status.PodIP] = pod.Name + validPodIPs = append(validPodIPs, pod.Status.PodIP) + } + } + require.NotEmpty(t, validPodIPs, "should have valid pod IPs") + + // Get service port + svc := &corev1.Service{} + err = suite.Client.Get(ctx, types.NamespacedName{Name: "lb-backend-endpointoverride", Namespace: ns}, svc) + require.NoError(t, err, "failed to get service") + require.NotEmpty(t, svc.Spec.Ports, "service should have ports") + servicePort := svc.Spec.Ports[0].TargetPort.IntValue() + + t.Logf("Found %d valid pods: %v, service port: %d", len(validPodIPs), podIPToName, servicePort) + + t.Run("header-based endpoint override with valid pod IP should route to specific pod", func(t *testing.T) { + // Use the first valid pod IP as override host + targetPodIP := validPodIPs[0] + format := "%s:%d" + if IPFamily == "ipv6" { + format = "[%s]:%d" + } + overrideHost := fmt.Sprintf(format, targetPodIP, servicePort) + + // Get the expected pod name from our mapping + expectPodName := podIPToName[targetPodIP] + require.NotEmpty(t, expectPodName, "failed to get expected pod name for IP %s", targetPodIP) + + t.Logf("Testing endpoint override with valid pod IP: %s, expecting pod: %s", overrideHost, expectPodName) + + expectedResponse := http.ExpectedResponse{ + Request: http.Request{ + Path: "/endpoint-override-header", + Headers: map[string]string{ + "x-custom-host": overrideHost, + }, + }, + Response: http.Response{ + StatusCode: 200, + }, + Namespace: ns, + } + + req := http.MakeRequest(t, &expectedResponse, gwAddr, "HTTP", "http") + + // Test that all requests go to the expected pod + for i := 0; i < sendRequests; i++ { + cReq, cResp, err := suite.RoundTripper.CaptureRoundTrip(req) + require.NoError(t, err, "failed to get expected response") + require.NoError(t, http.CompareRequest(t, &req, cReq, cResp, expectedResponse), "failed to compare request and response") + + actualPodName := cReq.Pod + require.Equal(t, expectPodName, actualPodName, "request %d: expected pod %s but got %s", i+1, expectPodName, actualPodName) + } + + t.Logf("All %d requests with valid override host %s routed to expected pod: %s", sendRequests, overrideHost, expectPodName) + }) + t.Run("header-based endpoint override with invalid pod IP should fallback to load balancer policy", func(t *testing.T) { + // Use an invalid pod IP that's not in the service endpoints + invalidOverrideHost := "192.168.99.99:8080" + + t.Logf("Testing endpoint override with invalid pod IP: %s (should fallback to load balancer policy)", invalidOverrideHost) + + expectedResponse := http.ExpectedResponse{ + Request: http.Request{ + Path: "/endpoint-override-header", + Headers: map[string]string{ + "x-custom-host": invalidOverrideHost, + }, + }, + Response: http.Response{ + StatusCode: 200, + }, + Namespace: ns, + } + + req := http.MakeRequest(t, &expectedResponse, gwAddr, "HTTP", "http") + + // Make multiple requests and verify fallback behavior (just check 200 response) + for i := 0; i < sendRequests; i++ { + cReq, cResp, err := suite.RoundTripper.CaptureRoundTrip(req) + require.NoError(t, err, "failed to get expected response") + require.NoError(t, http.CompareRequest(t, &req, cReq, cResp, expectedResponse), "failed to compare request and response") + + // For invalid override host, we just verify the response is 200 (fallback works) + // No need to check specific pod routing since it should use fallback policy + } + + t.Logf("All %d requests with invalid override host %s got 200 response (fallback working)", sendRequests, invalidOverrideHost) + }) + + t.Run("header-based endpoint override without header should fallback to load balancer policy", func(t *testing.T) { + expectedResponse := http.ExpectedResponse{ + Request: http.Request{ + Path: "/endpoint-override-header", + // No x-custom-host header + }, + Response: http.Response{ + StatusCode: 200, + }, + Namespace: ns, + } + + req := http.MakeRequest(t, &expectedResponse, gwAddr, "HTTP", "http") + + // Make multiple requests and verify fallback behavior (just check 200 response) + for i := 0; i < sendRequests; i++ { + cReq, cResp, err := suite.RoundTripper.CaptureRoundTrip(req) + require.NoError(t, err, "failed to get expected response") + require.NoError(t, http.CompareRequest(t, &req, cReq, cResp, expectedResponse), "failed to compare request and response") + + // For missing header, we just verify the response is 200 (fallback works) + // No need to check specific pod routing since it should use fallback policy + } + + t.Logf("All %d requests without override header got 200 response (fallback working)", sendRequests) + }) + }, +} diff --git a/test/helm/gateway-crds-helm/all.out.yaml b/test/helm/gateway-crds-helm/all.out.yaml index 85ab53652bf..3bc2a49e176 100644 --- a/test/helm/gateway-crds-helm/all.out.yaml +++ b/test/helm/gateway-crds-helm/all.out.yaml @@ -18311,6 +18311,34 @@ spec: - message: If consistent hash type is cookie, the cookie field must be set. rule: 'self.type == ''Cookie'' ? has(self.cookie) : !has(self.cookie)' + endpointOverride: + description: |- + EndpointOverride defines the configuration for endpoint override. + When specified, the load balancer will attempt to route requests to endpoints + based on the override information extracted from request headers or metadata. + If the override endpoints are not available, the configured load balancer policy will be used as fallback. + properties: + extractFrom: + description: ExtractFrom defines the sources to extract endpoint + override information from. + items: + description: EndpointOverrideExtractFrom defines a source + to extract endpoint override information from. + properties: + header: + description: |- + Header defines the header to get the override endpoint addresses. + The header value must specify at least one endpoint in `IP:Port` format or multiple endpoints in `IP:Port,IP:Port,...` format. + For example `10.0.0.5:8080` or `[2600:4040:5204::1574:24ae]:80`. + The IPv6 address is enclosed in square brackets. + type: string + type: object + maxItems: 10 + minItems: 1 + type: array + required: + - extractFrom + type: object slowStart: description: |- SlowStart defines the configuration related to the slow start load balancer policy. @@ -22158,6 +22186,35 @@ spec: field must be set. rule: 'self.type == ''Cookie'' ? has(self.cookie) : !has(self.cookie)' + endpointOverride: + description: |- + EndpointOverride defines the configuration for endpoint override. + When specified, the load balancer will attempt to route requests to endpoints + based on the override information extracted from request headers or metadata. + If the override endpoints are not available, the configured load balancer policy will be used as fallback. + properties: + extractFrom: + description: ExtractFrom defines the sources to + extract endpoint override information from. + items: + description: EndpointOverrideExtractFrom defines + a source to extract endpoint override information + from. + properties: + header: + description: |- + Header defines the header to get the override endpoint addresses. + The header value must specify at least one endpoint in `IP:Port` format or multiple endpoints in `IP:Port,IP:Port,...` format. + For example `10.0.0.5:8080` or `[2600:4040:5204::1574:24ae]:80`. + The IPv6 address is enclosed in square brackets. + type: string + type: object + maxItems: 10 + minItems: 1 + type: array + required: + - extractFrom + type: object slowStart: description: |- SlowStart defines the configuration related to the slow start load balancer policy. @@ -35208,6 +35265,37 @@ spec: be set. rule: 'self.type == ''Cookie'' ? has(self.cookie) : !has(self.cookie)' + endpointOverride: + description: |- + EndpointOverride defines the configuration for endpoint override. + When specified, the load balancer will attempt to route requests to endpoints + based on the override information extracted from request headers or metadata. + If the override endpoints are not available, the configured load balancer policy will be used as fallback. + properties: + extractFrom: + description: ExtractFrom defines + the sources to extract endpoint + override information from. + items: + description: EndpointOverrideExtractFrom + defines a source to extract + endpoint override information + from. + properties: + header: + description: |- + Header defines the header to get the override endpoint addresses. + The header value must specify at least one endpoint in `IP:Port` format or multiple endpoints in `IP:Port,IP:Port,...` format. + For example `10.0.0.5:8080` or `[2600:4040:5204::1574:24ae]:80`. + The IPv6 address is enclosed in square brackets. + type: string + type: object + maxItems: 10 + minItems: 1 + type: array + required: + - extractFrom + type: object slowStart: description: |- SlowStart defines the configuration related to the slow start load balancer policy. @@ -36288,6 +36376,37 @@ spec: be set. rule: 'self.type == ''Cookie'' ? has(self.cookie) : !has(self.cookie)' + endpointOverride: + description: |- + EndpointOverride defines the configuration for endpoint override. + When specified, the load balancer will attempt to route requests to endpoints + based on the override information extracted from request headers or metadata. + If the override endpoints are not available, the configured load balancer policy will be used as fallback. + properties: + extractFrom: + description: ExtractFrom defines + the sources to extract endpoint + override information from. + items: + description: EndpointOverrideExtractFrom + defines a source to extract + endpoint override information + from. + properties: + header: + description: |- + Header defines the header to get the override endpoint addresses. + The header value must specify at least one endpoint in `IP:Port` format or multiple endpoints in `IP:Port,IP:Port,...` format. + For example `10.0.0.5:8080` or `[2600:4040:5204::1574:24ae]:80`. + The IPv6 address is enclosed in square brackets. + type: string + type: object + maxItems: 10 + minItems: 1 + type: array + required: + - extractFrom + type: object slowStart: description: |- SlowStart defines the configuration related to the slow start load balancer policy. @@ -37453,6 +37572,36 @@ spec: the cookie field must be set. rule: 'self.type == ''Cookie'' ? has(self.cookie) : !has(self.cookie)' + endpointOverride: + description: |- + EndpointOverride defines the configuration for endpoint override. + When specified, the load balancer will attempt to route requests to endpoints + based on the override information extracted from request headers or metadata. + If the override endpoints are not available, the configured load balancer policy will be used as fallback. + properties: + extractFrom: + description: ExtractFrom defines the + sources to extract endpoint override + information from. + items: + description: EndpointOverrideExtractFrom + defines a source to extract endpoint + override information from. + properties: + header: + description: |- + Header defines the header to get the override endpoint addresses. + The header value must specify at least one endpoint in `IP:Port` format or multiple endpoints in `IP:Port,IP:Port,...` format. + For example `10.0.0.5:8080` or `[2600:4040:5204::1574:24ae]:80`. + The IPv6 address is enclosed in square brackets. + type: string + type: object + maxItems: 10 + minItems: 1 + type: array + required: + - extractFrom + type: object slowStart: description: |- SlowStart defines the configuration related to the slow start load balancer policy. @@ -38536,6 +38685,36 @@ spec: the cookie field must be set. rule: 'self.type == ''Cookie'' ? has(self.cookie) : !has(self.cookie)' + endpointOverride: + description: |- + EndpointOverride defines the configuration for endpoint override. + When specified, the load balancer will attempt to route requests to endpoints + based on the override information extracted from request headers or metadata. + If the override endpoints are not available, the configured load balancer policy will be used as fallback. + properties: + extractFrom: + description: ExtractFrom defines the sources + to extract endpoint override information + from. + items: + description: EndpointOverrideExtractFrom + defines a source to extract endpoint override + information from. + properties: + header: + description: |- + Header defines the header to get the override endpoint addresses. + The header value must specify at least one endpoint in `IP:Port` format or multiple endpoints in `IP:Port,IP:Port,...` format. + For example `10.0.0.5:8080` or `[2600:4040:5204::1574:24ae]:80`. + The IPv6 address is enclosed in square brackets. + type: string + type: object + maxItems: 10 + minItems: 1 + type: array + required: + - extractFrom + type: object slowStart: description: |- SlowStart defines the configuration related to the slow start load balancer policy. @@ -40453,6 +40632,35 @@ spec: cookie field must be set. rule: 'self.type == ''Cookie'' ? has(self.cookie) : !has(self.cookie)' + endpointOverride: + description: |- + EndpointOverride defines the configuration for endpoint override. + When specified, the load balancer will attempt to route requests to endpoints + based on the override information extracted from request headers or metadata. + If the override endpoints are not available, the configured load balancer policy will be used as fallback. + properties: + extractFrom: + description: ExtractFrom defines the sources to + extract endpoint override information from. + items: + description: EndpointOverrideExtractFrom defines + a source to extract endpoint override information + from. + properties: + header: + description: |- + Header defines the header to get the override endpoint addresses. + The header value must specify at least one endpoint in `IP:Port` format or multiple endpoints in `IP:Port,IP:Port,...` format. + For example `10.0.0.5:8080` or `[2600:4040:5204::1574:24ae]:80`. + The IPv6 address is enclosed in square brackets. + type: string + type: object + maxItems: 10 + minItems: 1 + type: array + required: + - extractFrom + type: object slowStart: description: |- SlowStart defines the configuration related to the slow start load balancer policy. @@ -41431,6 +41639,35 @@ spec: cookie field must be set. rule: 'self.type == ''Cookie'' ? has(self.cookie) : !has(self.cookie)' + endpointOverride: + description: |- + EndpointOverride defines the configuration for endpoint override. + When specified, the load balancer will attempt to route requests to endpoints + based on the override information extracted from request headers or metadata. + If the override endpoints are not available, the configured load balancer policy will be used as fallback. + properties: + extractFrom: + description: ExtractFrom defines the sources to + extract endpoint override information from. + items: + description: EndpointOverrideExtractFrom defines + a source to extract endpoint override information + from. + properties: + header: + description: |- + Header defines the header to get the override endpoint addresses. + The header value must specify at least one endpoint in `IP:Port` format or multiple endpoints in `IP:Port,IP:Port,...` format. + For example `10.0.0.5:8080` or `[2600:4040:5204::1574:24ae]:80`. + The IPv6 address is enclosed in square brackets. + type: string + type: object + maxItems: 10 + minItems: 1 + type: array + required: + - extractFrom + type: object slowStart: description: |- SlowStart defines the configuration related to the slow start load balancer policy. @@ -42615,6 +42852,36 @@ spec: the cookie field must be set. rule: 'self.type == ''Cookie'' ? has(self.cookie) : !has(self.cookie)' + endpointOverride: + description: |- + EndpointOverride defines the configuration for endpoint override. + When specified, the load balancer will attempt to route requests to endpoints + based on the override information extracted from request headers or metadata. + If the override endpoints are not available, the configured load balancer policy will be used as fallback. + properties: + extractFrom: + description: ExtractFrom defines the sources + to extract endpoint override information + from. + items: + description: EndpointOverrideExtractFrom + defines a source to extract endpoint + override information from. + properties: + header: + description: |- + Header defines the header to get the override endpoint addresses. + The header value must specify at least one endpoint in `IP:Port` format or multiple endpoints in `IP:Port,IP:Port,...` format. + For example `10.0.0.5:8080` or `[2600:4040:5204::1574:24ae]:80`. + The IPv6 address is enclosed in square brackets. + type: string + type: object + maxItems: 10 + minItems: 1 + type: array + required: + - extractFrom + type: object slowStart: description: |- SlowStart defines the configuration related to the slow start load balancer policy. @@ -43834,6 +44101,35 @@ spec: cookie field must be set. rule: 'self.type == ''Cookie'' ? has(self.cookie) : !has(self.cookie)' + endpointOverride: + description: |- + EndpointOverride defines the configuration for endpoint override. + When specified, the load balancer will attempt to route requests to endpoints + based on the override information extracted from request headers or metadata. + If the override endpoints are not available, the configured load balancer policy will be used as fallback. + properties: + extractFrom: + description: ExtractFrom defines the sources to + extract endpoint override information from. + items: + description: EndpointOverrideExtractFrom defines + a source to extract endpoint override information + from. + properties: + header: + description: |- + Header defines the header to get the override endpoint addresses. + The header value must specify at least one endpoint in `IP:Port` format or multiple endpoints in `IP:Port,IP:Port,...` format. + For example `10.0.0.5:8080` or `[2600:4040:5204::1574:24ae]:80`. + The IPv6 address is enclosed in square brackets. + type: string + type: object + maxItems: 10 + minItems: 1 + type: array + required: + - extractFrom + type: object slowStart: description: |- SlowStart defines the configuration related to the slow start load balancer policy. 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 5bb3c946f17..4ab5cb551c7 100644 --- a/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml +++ b/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml @@ -999,6 +999,34 @@ spec: - message: If consistent hash type is cookie, the cookie field must be set. rule: 'self.type == ''Cookie'' ? has(self.cookie) : !has(self.cookie)' + endpointOverride: + description: |- + EndpointOverride defines the configuration for endpoint override. + When specified, the load balancer will attempt to route requests to endpoints + based on the override information extracted from request headers or metadata. + If the override endpoints are not available, the configured load balancer policy will be used as fallback. + properties: + extractFrom: + description: ExtractFrom defines the sources to extract endpoint + override information from. + items: + description: EndpointOverrideExtractFrom defines a source + to extract endpoint override information from. + properties: + header: + description: |- + Header defines the header to get the override endpoint addresses. + The header value must specify at least one endpoint in `IP:Port` format or multiple endpoints in `IP:Port,IP:Port,...` format. + For example `10.0.0.5:8080` or `[2600:4040:5204::1574:24ae]:80`. + The IPv6 address is enclosed in square brackets. + type: string + type: object + maxItems: 10 + minItems: 1 + type: array + required: + - extractFrom + type: object slowStart: description: |- SlowStart defines the configuration related to the slow start load balancer policy. @@ -4846,6 +4874,35 @@ spec: field must be set. rule: 'self.type == ''Cookie'' ? has(self.cookie) : !has(self.cookie)' + endpointOverride: + description: |- + EndpointOverride defines the configuration for endpoint override. + When specified, the load balancer will attempt to route requests to endpoints + based on the override information extracted from request headers or metadata. + If the override endpoints are not available, the configured load balancer policy will be used as fallback. + properties: + extractFrom: + description: ExtractFrom defines the sources to + extract endpoint override information from. + items: + description: EndpointOverrideExtractFrom defines + a source to extract endpoint override information + from. + properties: + header: + description: |- + Header defines the header to get the override endpoint addresses. + The header value must specify at least one endpoint in `IP:Port` format or multiple endpoints in `IP:Port,IP:Port,...` format. + For example `10.0.0.5:8080` or `[2600:4040:5204::1574:24ae]:80`. + The IPv6 address is enclosed in square brackets. + type: string + type: object + maxItems: 10 + minItems: 1 + type: array + required: + - extractFrom + type: object slowStart: description: |- SlowStart defines the configuration related to the slow start load balancer policy. @@ -17896,6 +17953,37 @@ spec: be set. rule: 'self.type == ''Cookie'' ? has(self.cookie) : !has(self.cookie)' + endpointOverride: + description: |- + EndpointOverride defines the configuration for endpoint override. + When specified, the load balancer will attempt to route requests to endpoints + based on the override information extracted from request headers or metadata. + If the override endpoints are not available, the configured load balancer policy will be used as fallback. + properties: + extractFrom: + description: ExtractFrom defines + the sources to extract endpoint + override information from. + items: + description: EndpointOverrideExtractFrom + defines a source to extract + endpoint override information + from. + properties: + header: + description: |- + Header defines the header to get the override endpoint addresses. + The header value must specify at least one endpoint in `IP:Port` format or multiple endpoints in `IP:Port,IP:Port,...` format. + For example `10.0.0.5:8080` or `[2600:4040:5204::1574:24ae]:80`. + The IPv6 address is enclosed in square brackets. + type: string + type: object + maxItems: 10 + minItems: 1 + type: array + required: + - extractFrom + type: object slowStart: description: |- SlowStart defines the configuration related to the slow start load balancer policy. @@ -18976,6 +19064,37 @@ spec: be set. rule: 'self.type == ''Cookie'' ? has(self.cookie) : !has(self.cookie)' + endpointOverride: + description: |- + EndpointOverride defines the configuration for endpoint override. + When specified, the load balancer will attempt to route requests to endpoints + based on the override information extracted from request headers or metadata. + If the override endpoints are not available, the configured load balancer policy will be used as fallback. + properties: + extractFrom: + description: ExtractFrom defines + the sources to extract endpoint + override information from. + items: + description: EndpointOverrideExtractFrom + defines a source to extract + endpoint override information + from. + properties: + header: + description: |- + Header defines the header to get the override endpoint addresses. + The header value must specify at least one endpoint in `IP:Port` format or multiple endpoints in `IP:Port,IP:Port,...` format. + For example `10.0.0.5:8080` or `[2600:4040:5204::1574:24ae]:80`. + The IPv6 address is enclosed in square brackets. + type: string + type: object + maxItems: 10 + minItems: 1 + type: array + required: + - extractFrom + type: object slowStart: description: |- SlowStart defines the configuration related to the slow start load balancer policy. @@ -20141,6 +20260,36 @@ spec: the cookie field must be set. rule: 'self.type == ''Cookie'' ? has(self.cookie) : !has(self.cookie)' + endpointOverride: + description: |- + EndpointOverride defines the configuration for endpoint override. + When specified, the load balancer will attempt to route requests to endpoints + based on the override information extracted from request headers or metadata. + If the override endpoints are not available, the configured load balancer policy will be used as fallback. + properties: + extractFrom: + description: ExtractFrom defines the + sources to extract endpoint override + information from. + items: + description: EndpointOverrideExtractFrom + defines a source to extract endpoint + override information from. + properties: + header: + description: |- + Header defines the header to get the override endpoint addresses. + The header value must specify at least one endpoint in `IP:Port` format or multiple endpoints in `IP:Port,IP:Port,...` format. + For example `10.0.0.5:8080` or `[2600:4040:5204::1574:24ae]:80`. + The IPv6 address is enclosed in square brackets. + type: string + type: object + maxItems: 10 + minItems: 1 + type: array + required: + - extractFrom + type: object slowStart: description: |- SlowStart defines the configuration related to the slow start load balancer policy. @@ -21224,6 +21373,36 @@ spec: the cookie field must be set. rule: 'self.type == ''Cookie'' ? has(self.cookie) : !has(self.cookie)' + endpointOverride: + description: |- + EndpointOverride defines the configuration for endpoint override. + When specified, the load balancer will attempt to route requests to endpoints + based on the override information extracted from request headers or metadata. + If the override endpoints are not available, the configured load balancer policy will be used as fallback. + properties: + extractFrom: + description: ExtractFrom defines the sources + to extract endpoint override information + from. + items: + description: EndpointOverrideExtractFrom + defines a source to extract endpoint override + information from. + properties: + header: + description: |- + Header defines the header to get the override endpoint addresses. + The header value must specify at least one endpoint in `IP:Port` format or multiple endpoints in `IP:Port,IP:Port,...` format. + For example `10.0.0.5:8080` or `[2600:4040:5204::1574:24ae]:80`. + The IPv6 address is enclosed in square brackets. + type: string + type: object + maxItems: 10 + minItems: 1 + type: array + required: + - extractFrom + type: object slowStart: description: |- SlowStart defines the configuration related to the slow start load balancer policy. @@ -23141,6 +23320,35 @@ spec: cookie field must be set. rule: 'self.type == ''Cookie'' ? has(self.cookie) : !has(self.cookie)' + endpointOverride: + description: |- + EndpointOverride defines the configuration for endpoint override. + When specified, the load balancer will attempt to route requests to endpoints + based on the override information extracted from request headers or metadata. + If the override endpoints are not available, the configured load balancer policy will be used as fallback. + properties: + extractFrom: + description: ExtractFrom defines the sources to + extract endpoint override information from. + items: + description: EndpointOverrideExtractFrom defines + a source to extract endpoint override information + from. + properties: + header: + description: |- + Header defines the header to get the override endpoint addresses. + The header value must specify at least one endpoint in `IP:Port` format or multiple endpoints in `IP:Port,IP:Port,...` format. + For example `10.0.0.5:8080` or `[2600:4040:5204::1574:24ae]:80`. + The IPv6 address is enclosed in square brackets. + type: string + type: object + maxItems: 10 + minItems: 1 + type: array + required: + - extractFrom + type: object slowStart: description: |- SlowStart defines the configuration related to the slow start load balancer policy. @@ -24119,6 +24327,35 @@ spec: cookie field must be set. rule: 'self.type == ''Cookie'' ? has(self.cookie) : !has(self.cookie)' + endpointOverride: + description: |- + EndpointOverride defines the configuration for endpoint override. + When specified, the load balancer will attempt to route requests to endpoints + based on the override information extracted from request headers or metadata. + If the override endpoints are not available, the configured load balancer policy will be used as fallback. + properties: + extractFrom: + description: ExtractFrom defines the sources to + extract endpoint override information from. + items: + description: EndpointOverrideExtractFrom defines + a source to extract endpoint override information + from. + properties: + header: + description: |- + Header defines the header to get the override endpoint addresses. + The header value must specify at least one endpoint in `IP:Port` format or multiple endpoints in `IP:Port,IP:Port,...` format. + For example `10.0.0.5:8080` or `[2600:4040:5204::1574:24ae]:80`. + The IPv6 address is enclosed in square brackets. + type: string + type: object + maxItems: 10 + minItems: 1 + type: array + required: + - extractFrom + type: object slowStart: description: |- SlowStart defines the configuration related to the slow start load balancer policy. @@ -25303,6 +25540,36 @@ spec: the cookie field must be set. rule: 'self.type == ''Cookie'' ? has(self.cookie) : !has(self.cookie)' + endpointOverride: + description: |- + EndpointOverride defines the configuration for endpoint override. + When specified, the load balancer will attempt to route requests to endpoints + based on the override information extracted from request headers or metadata. + If the override endpoints are not available, the configured load balancer policy will be used as fallback. + properties: + extractFrom: + description: ExtractFrom defines the sources + to extract endpoint override information + from. + items: + description: EndpointOverrideExtractFrom + defines a source to extract endpoint + override information from. + properties: + header: + description: |- + Header defines the header to get the override endpoint addresses. + The header value must specify at least one endpoint in `IP:Port` format or multiple endpoints in `IP:Port,IP:Port,...` format. + For example `10.0.0.5:8080` or `[2600:4040:5204::1574:24ae]:80`. + The IPv6 address is enclosed in square brackets. + type: string + type: object + maxItems: 10 + minItems: 1 + type: array + required: + - extractFrom + type: object slowStart: description: |- SlowStart defines the configuration related to the slow start load balancer policy. @@ -26522,6 +26789,35 @@ spec: cookie field must be set. rule: 'self.type == ''Cookie'' ? has(self.cookie) : !has(self.cookie)' + endpointOverride: + description: |- + EndpointOverride defines the configuration for endpoint override. + When specified, the load balancer will attempt to route requests to endpoints + based on the override information extracted from request headers or metadata. + If the override endpoints are not available, the configured load balancer policy will be used as fallback. + properties: + extractFrom: + description: ExtractFrom defines the sources to + extract endpoint override information from. + items: + description: EndpointOverrideExtractFrom defines + a source to extract endpoint override information + from. + properties: + header: + description: |- + Header defines the header to get the override endpoint addresses. + The header value must specify at least one endpoint in `IP:Port` format or multiple endpoints in `IP:Port,IP:Port,...` format. + For example `10.0.0.5:8080` or `[2600:4040:5204::1574:24ae]:80`. + The IPv6 address is enclosed in square brackets. + type: string + type: object + maxItems: 10 + minItems: 1 + type: array + required: + - extractFrom + type: object slowStart: description: |- SlowStart defines the configuration related to the slow start load balancer policy.