Skip to content

Commit a7d8cef

Browse files
committed
support consistent hash for non-route backends
Signed-off-by: Huabing (Robin) Zhao <zhaohuabing@gmail.com>
1 parent 00320fb commit a7d8cef

9 files changed

Lines changed: 87 additions & 6 deletions

internal/xds/translator/cluster.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
clusterv3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
1616
corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
1717
endpointv3 "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3"
18+
routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
1819
dfpv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/clusters/dynamic_forward_proxy/v3"
1920
commondfpv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/common/dynamic_forward_proxy/v3"
2021
codecv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/upstream_codec/v3"
@@ -1050,13 +1051,22 @@ func buildTypedExtensionProtocolOptions(args *xdsClusterArgs, requiresAutoHTTPCo
10501051
requiresHTTPFilters := (len(args.settings) > 0 && args.settings[0].Filters != nil && args.settings[0].Filters.CredentialInjection != nil) ||
10511052
args.admissionControl != nil
10521053

1054+
var clusterHashPolicy []*routev3.RouteAction_HashPolicy
1055+
if !args.isRoute && args.loadBalancer != nil {
1056+
clusterHashPolicy = buildConsistentHashPolicy(args.loadBalancer.ConsistentHash)
1057+
}
1058+
10531059
requiredHTTPProtocolOptions := args.useClientProtocol || requiresAutoHTTPConfig ||
1054-
requiresCommonHTTPOptions || requiresHTTP1Options || requiresHTTP2Options || requiresHTTPFilters || requiresAutoSNI || forceHTTP1UpstreamProtocol
1060+
requiresCommonHTTPOptions || requiresHTTP1Options || requiresHTTP2Options || requiresHTTPFilters || requiresAutoSNI ||
1061+
forceHTTP1UpstreamProtocol || len(clusterHashPolicy) > 0
10551062

10561063
if !requiredHTTPProtocolOptions {
10571064
return nil, nil, nil
10581065
}
10591066
protocolOptions := httpv3.HttpProtocolOptions{}
1067+
if len(clusterHashPolicy) > 0 {
1068+
protocolOptions.HashPolicy = clusterHashPolicy
1069+
}
10601070
if requiresCommonHTTPOptions {
10611071
protocolOptions.CommonHttpProtocolOptions = &corev3.HttpProtocolOptions{}
10621072
if args.timeout != nil && args.timeout.HTTP != nil {

internal/xds/translator/cluster_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
cswrrv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/load_balancing_policies/client_side_weighted_round_robin/v3"
1616
override_hostv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/load_balancing_policies/override_host/v3"
1717
wrr_localityv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/load_balancing_policies/wrr_locality/v3"
18+
httpv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/v3"
1819
"github.com/google/go-cmp/cmp"
1920
"github.com/stretchr/testify/require"
2021
"google.golang.org/protobuf/encoding/protojson"
@@ -251,6 +252,53 @@ func TestBuildClusterWithBackendUtilization(t *testing.T) {
251252
require.Equal(t, "type.googleapis.com/envoy.extensions.load_balancing_policies.client_side_weighted_round_robin.v3.ClientSideWeightedRoundRobin", policy.TypedExtensionConfig.TypedConfig.TypeUrl)
252253
}
253254

255+
func TestBuildXdsClusterWithClusterLevelHashPolicy(t *testing.T) {
256+
sourceIP := true
257+
args := &xdsClusterArgs{
258+
name: "test-cluster-consistent-hash",
259+
endpointType: EndpointTypeStatic,
260+
settings: []*ir.DestinationSetting{{
261+
Endpoints: []*ir.DestinationEndpoint{{Host: "127.0.0.1", Port: 8080}},
262+
}},
263+
loadBalancer: &ir.LoadBalancer{
264+
ConsistentHash: &ir.ConsistentHash{
265+
SourceIP: &sourceIP,
266+
},
267+
},
268+
}
269+
270+
result, err := buildXdsCluster(args)
271+
require.NoError(t, err)
272+
273+
options := &httpv3.HttpProtocolOptions{}
274+
require.NotNil(t, result.cluster.TypedExtensionProtocolOptions)
275+
require.NoError(t, result.cluster.TypedExtensionProtocolOptions[extensionOptionsKey].UnmarshalTo(options))
276+
require.Len(t, options.HashPolicy, 1)
277+
require.True(t, options.HashPolicy[0].GetConnectionProperties().GetSourceIp())
278+
}
279+
280+
func TestBuildXdsRouteClusterWithoutClusterLevelHashPolicy(t *testing.T) {
281+
sourceIP := true
282+
args := &xdsClusterArgs{
283+
name: "test-route-cluster-consistent-hash",
284+
endpointType: EndpointTypeStatic,
285+
settings: []*ir.DestinationSetting{{
286+
Endpoints: []*ir.DestinationEndpoint{{Host: "127.0.0.1", Port: 8080}},
287+
}},
288+
loadBalancer: &ir.LoadBalancer{
289+
ConsistentHash: &ir.ConsistentHash{
290+
SourceIP: &sourceIP,
291+
},
292+
},
293+
isRoute: true,
294+
}
295+
296+
result, err := buildXdsCluster(args)
297+
require.NoError(t, err)
298+
299+
require.Nil(t, result.cluster.TypedExtensionProtocolOptions)
300+
}
301+
254302
func TestBuildClusterWithBackendUtilizationSlowStart(t *testing.T) {
255303
window := 5 * time.Second
256304
args := &xdsClusterArgs{

internal/xds/translator/route.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -708,7 +708,13 @@ func buildHashPolicy(httpRoute *ir.HTTPRoute) []*routev3.RouteAction_HashPolicy
708708
return nil
709709
}
710710

711-
ch := httpRoute.Traffic.LoadBalancer.ConsistentHash
711+
return buildConsistentHashPolicy(httpRoute.Traffic.LoadBalancer.ConsistentHash)
712+
}
713+
714+
func buildConsistentHashPolicy(ch *ir.ConsistentHash) []*routev3.RouteAction_HashPolicy {
715+
if ch == nil {
716+
return nil
717+
}
712718

713719
switch {
714720
case ch.Headers != nil:

internal/xds/translator/testdata/in/xds-ir/ext-auth-retry.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ http:
2929
extAuth:
3030
name: securitypolicy/default/policy-for-http-route-1
3131
traffic:
32+
loadBalancer:
33+
consistentHash:
34+
sourceIP: true
3235
retry:
3336
numRetries: 2
3437
perRetry:
@@ -86,6 +89,9 @@ http:
8689
extAuth:
8790
name: securitypolicy/default/policy-for-http-route-1
8891
traffic:
92+
loadBalancer:
93+
consistentHash:
94+
sourceIP: true
8995
retry:
9096
numRetries: 2
9197
perRetry:

internal/xds/translator/testdata/out/xds-ir/ext-auth-retry.clusters.yaml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,9 @@
8282
loadBalancingPolicy:
8383
policies:
8484
- typedExtensionConfig:
85-
name: envoy.load_balancing_policies.least_request
85+
name: envoy.load_balancing_policies.maglev
8686
typedConfig:
87-
'@type': type.googleapis.com/envoy.extensions.load_balancing_policies.least_request.v3.LeastRequest
88-
localityLbConfig:
89-
localityWeightedLbConfig: {}
87+
'@type': type.googleapis.com/envoy.extensions.load_balancing_policies.maglev.v3.Maglev
9088
name: securitypolicy/default/policy-for-http-route-1/default/grpc-backend
9189
perConnectionBufferLimitBytes: 32768
9290
type: EDS
@@ -97,6 +95,9 @@
9795
http2ProtocolOptions:
9896
initialConnectionWindowSize: 1048576
9997
initialStreamWindowSize: 65536
98+
hashPolicy:
99+
- connectionProperties:
100+
sourceIp: true
100101
- circuitBreakers:
101102
thresholds:
102103
- maxRetries: 1024

internal/xds/translator/testdata/out/xds-ir/jwt-with-backend-tls-retry.clusters.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,9 @@
125125
idleTimeout: 16s
126126
maxConnectionDuration: 17s
127127
maxRequestsPerConnection: 1
128+
hashPolicy:
129+
- connectionProperties:
130+
sourceIp: true
128131
upstreamConnectionOptions:
129132
tcpKeepalive:
130133
keepaliveInterval: 60

internal/xds/translator/testdata/out/xds-ir/oidc-backend-cluster-provider.clusters.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,9 @@
125125
idleTimeout: 16s
126126
maxConnectionDuration: 17s
127127
maxRequestsPerConnection: 1
128+
hashPolicy:
129+
- connectionProperties:
130+
sourceIp: true
128131
upstreamConnectionOptions:
129132
tcpKeepalive:
130133
keepaliveInterval: 60

internal/xds/translator/testdata/out/xds-ir/oidc-provider-traffic-features.clusters.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,9 @@
103103
maxRequestsPerConnection: 1
104104
explicitHttpConfig:
105105
httpProtocolOptions: {}
106+
hashPolicy:
107+
- connectionProperties:
108+
sourceIp: true
106109
upstreamConnectionOptions:
107110
tcpKeepalive:
108111
keepaliveInterval: 60

release-notes/current.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ bug fixes: |
3030
Fixed Kubernetes provider namespace-scoped watches to always include the controller namespace so Envoy Gateway can read its own infrastructure resources.
3131
Fixed TLS secrets with non-canonical PEM formatting (e.g. unusual line endings) being passed verbatim to Envoy, which could cause BoringSSL errors such as `BAD_END_LINE`. Cert and key PEM data is now re-encoded to a canonical form before being delivered as xDS resources.
3232
Fixed `MaxStreamDuration` not being set on `CommonHttpProtocolOptions` for non-route cluster.
33+
Fixed `ConsistentHash` load balancer settings on non-route service clusters, such as SecurityPolicy extAuth backends, by emitting cluster-level HTTP hash policies.
3334
Fixed `egctl x status all`/`xroute`/`xpolicy` failing when a Gateway API CRD (e.g. TCPRoute) is not installed in the cluster; missing CRDs are now skipped silently, or reported on stderr with `-v`.
3435
Fixed Kubernetes Service and ServiceImport `appProtocol` values `kubernetes.io/ws` and `kubernetes.io/wss` to force HTTP/1.1 upstream connections instead of negotiating HTTP/2, avoiding compatibility issues with WebSocket backends that do not support RFC 8441 extended CONNECT.
3536
Fixed an `ExternalName` Service referenced as a route backend producing an invalid xDS cluster (with an empty address) that failed IR validation and stalled config delivery for the whole snapshot. `ExternalName` Services are now explicitly rejected as backends with a `ResolvedRefs: False` route condition; use an Envoy Gateway `Backend` resource with an FQDN endpoint instead.

0 commit comments

Comments
 (0)