Skip to content

Commit

Permalink
feat(translator): Implement header hash policy for consistent hash lo…
Browse files Browse the repository at this point in the history
…ad balancers (#3357)

* Map consistent hash configuration

Signed-off-by: Harry Turton <harryt@canva.com>

* Implement mapping and add tests

Signed-off-by: Harry Turton <harryt@canva.com>

* Small refactors, add yaml keys

Signed-off-by: Harry Turton <harryt@canva.com>

* Add documentation and lints

Signed-off-by: Harry Turton <harryt@canva.com>

* Add additional tests

Signed-off-by: Harry Turton <harryt@canva.com>

---------

Signed-off-by: Harry Turton <harryt@canva.com>
  • Loading branch information
harrisonturton authored May 9, 2024
1 parent bb348dc commit 7863f81
Show file tree
Hide file tree
Showing 14 changed files with 199 additions and 16 deletions.
3 changes: 1 addition & 2 deletions api/v1alpha1/loadbalancer_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,14 @@ const (
//
// +kubebuilder:validation:XValidation:rule="self.type == 'Header' ? has(self.header) : !has(self.header)",message="If consistent hash type is header, the header field must be set."
type ConsistentHash struct {
// Valid Type values are "SourceIP".
// ConsistentHashType defines the type of input to hash on. Valid Type values are "SourceIP" or "Header".
//
// +unionDiscriminator
Type ConsistentHashType `json:"type"`

// Header configures the header hash policy when the consistent hash type is set to Header.
//
// +optional
// +notImplementedHide
Header *Header `json:"header,omitempty"`
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,8 @@ spec:
- name
type: object
type:
description: Valid Type values are "SourceIP".
description: ConsistentHashType defines the type of input
to hash on. Valid Type values are "SourceIP" or "Header".
enum:
- SourceIP
- Header
Expand Down
23 changes: 18 additions & 5 deletions internal/gatewayapi/backendtrafficpolicy.go
Original file line number Diff line number Diff line change
Expand Up @@ -764,11 +764,7 @@ func (t *Translator) buildLoadBalancer(policy *egv1a1.BackendTrafficPolicy) *ir.
switch policy.Spec.LoadBalancer.Type {
case egv1a1.ConsistentHashLoadBalancerType:
lb = &ir.LoadBalancer{
ConsistentHash: &ir.ConsistentHash{},
}
if policy.Spec.LoadBalancer.ConsistentHash != nil &&
policy.Spec.LoadBalancer.ConsistentHash.Type == egv1a1.SourceIPConsistentHashType {
lb.ConsistentHash.SourceIP = ptr.To(true)
ConsistentHash: t.buildConsistentHashLoadBalancer(policy),
}
case egv1a1.LeastRequestLoadBalancerType:
lb = &ir.LoadBalancer{}
Expand Down Expand Up @@ -805,6 +801,23 @@ func (t *Translator) buildLoadBalancer(policy *egv1a1.BackendTrafficPolicy) *ir.
return lb
}

func (t *Translator) buildConsistentHashLoadBalancer(policy *egv1a1.BackendTrafficPolicy) *ir.ConsistentHash {
switch policy.Spec.LoadBalancer.ConsistentHash.Type {
case egv1a1.SourceIPConsistentHashType:
return &ir.ConsistentHash{
SourceIP: ptr.To(true),
}
case egv1a1.HeaderConsistentHashType:
return &ir.ConsistentHash{
Header: &ir.Header{
Name: policy.Spec.LoadBalancer.ConsistentHash.Header.Name,
},
}
default:
return &ir.ConsistentHash{}
}
}

func (t *Translator) buildProxyProtocol(policy *egv1a1.BackendTrafficPolicy) *ir.ProxyProtocol {
var pp *ir.ProxyProtocol
switch policy.Spec.ProxyProtocol.Version {
Expand Down
8 changes: 7 additions & 1 deletion internal/ir/xds.go
Original file line number Diff line number Diff line change
Expand Up @@ -1575,7 +1575,13 @@ type Random struct{}
// +k8s:deepcopy-gen=true
type ConsistentHash struct {
// Hash based on the Source IP Address
SourceIP *bool `json:"sourceIP,omitempty" yaml:"sourceIP,omitempty"`
SourceIP *bool `json:"sourceIP,omitempty" yaml:"sourceIP,omitempty"`
Header *Header `json:"header,omitempty" yaml:"header,omitempty"`
}

// Header consistent hash type settings
type Header struct {
Name string `json:"name" yaml:"name"`
}

type ProxyProtocolVersion string
Expand Down
14 changes: 12 additions & 2 deletions internal/ir/xds_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1243,15 +1243,25 @@ func TestValidateLoadBalancer(t *testing.T) {
want: nil,
},
{
name: "consistent hash",
name: "consistent hash with source IP hash policy",
input: LoadBalancer{
ConsistentHash: &ConsistentHash{
SourceIP: ptr.To(true),
},
},
want: nil,
},

{
name: "consistent hash with header hash policy",
input: LoadBalancer{
ConsistentHash: &ConsistentHash{
Header: &Header{
Name: "name",
},
},
},
want: nil,
},
{
name: "least request and random set",
input: LoadBalancer{
Expand Down
5 changes: 5 additions & 0 deletions internal/ir/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion internal/xds/translator/listener_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func Test_buildTCPProxyHashPolicy(t *testing.T) {
want: nil,
},
{
name: "ConsistentHash without SourceIP",
name: "ConsistentHash without hash policy",
lb: &ir.LoadBalancer{ConsistentHash: &ir.ConsistentHash{}},
want: nil,
},
Expand Down
19 changes: 16 additions & 3 deletions internal/xds/translator/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,20 @@ func buildHashPolicy(httpRoute *ir.HTTPRoute) []*routev3.RouteAction_HashPolicy
return nil
}

if httpRoute.LoadBalancer.ConsistentHash.SourceIP != nil && *httpRoute.LoadBalancer.ConsistentHash.SourceIP {
switch {
case httpRoute.LoadBalancer.ConsistentHash.Header != nil:
hashPolicy := &routev3.RouteAction_HashPolicy{
PolicySpecifier: &routev3.RouteAction_HashPolicy_Header_{
Header: &routev3.RouteAction_HashPolicy_Header{
HeaderName: httpRoute.LoadBalancer.ConsistentHash.Header.Name,
},
},
}
return []*routev3.RouteAction_HashPolicy{hashPolicy}
case httpRoute.LoadBalancer.ConsistentHash.SourceIP != nil:
if !*httpRoute.LoadBalancer.ConsistentHash.SourceIP {
return nil
}
hashPolicy := &routev3.RouteAction_HashPolicy{
PolicySpecifier: &routev3.RouteAction_HashPolicy_ConnectionProperties_{
ConnectionProperties: &routev3.RouteAction_HashPolicy_ConnectionProperties{
Expand All @@ -449,9 +462,9 @@ func buildHashPolicy(httpRoute *ir.HTTPRoute) []*routev3.RouteAction_HashPolicy
},
}
return []*routev3.RouteAction_HashPolicy{hashPolicy}
default:
return nil
}

return nil
}

func buildRetryPolicy(route *ir.HTTPRoute) (*routev3.RetryPolicy, error) {
Expand Down
84 changes: 84 additions & 0 deletions internal/xds/translator/route_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright Envoy Gateway Authors
// SPDX-License-Identifier: Apache-2.0
// The full text of the Apache license is available in the LICENSE file at
// the root of the repo.

package translator

import (
"reflect"
"testing"

routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
"k8s.io/utils/ptr"

"github.com/envoyproxy/gateway/internal/ir"
)

func Test_buildHashPolicy(t *testing.T) {
tests := []struct {
name string
httpRoute *ir.HTTPRoute
want []*routev3.RouteAction_HashPolicy
}{
{
name: "Nil HttpRoute",
httpRoute: nil,
want: nil,
},
{
name: "Nil LoadBalancer in HttpRoute",
httpRoute: &ir.HTTPRoute{},
want: nil,
},
{
name: "Nil ConsistentHash in LoadBalancer",
httpRoute: &ir.HTTPRoute{LoadBalancer: &ir.LoadBalancer{}},
want: nil,
},
{
name: "ConsistentHash with nil SourceIP and Header",
httpRoute: &ir.HTTPRoute{LoadBalancer: &ir.LoadBalancer{ConsistentHash: &ir.ConsistentHash{}}},
want: nil,
},
{
name: "ConsistentHash with SourceIP set to false",
httpRoute: &ir.HTTPRoute{LoadBalancer: &ir.LoadBalancer{ConsistentHash: &ir.ConsistentHash{SourceIP: ptr.To(false)}}},
want: nil,
},
{
name: "ConsistentHash with SourceIP set to true",
httpRoute: &ir.HTTPRoute{LoadBalancer: &ir.LoadBalancer{ConsistentHash: &ir.ConsistentHash{SourceIP: ptr.To(true)}}},
want: []*routev3.RouteAction_HashPolicy{
{
PolicySpecifier: &routev3.RouteAction_HashPolicy_ConnectionProperties_{
ConnectionProperties: &routev3.RouteAction_HashPolicy_ConnectionProperties{
SourceIp: true,
},
},
},
},
},
{
name: "ConsistentHash with Header",
httpRoute: &ir.HTTPRoute{LoadBalancer: &ir.LoadBalancer{ConsistentHash: &ir.ConsistentHash{Header: &ir.Header{Name: "name"}}}},
want: []*routev3.RouteAction_HashPolicy{
{
PolicySpecifier: &routev3.RouteAction_HashPolicy_Header_{
Header: &routev3.RouteAction_HashPolicy_Header{
HeaderName: "name",
},
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := buildHashPolicy(tt.httpRoute)
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("buildHashPolicy() got = %v, want %v", got, tt.want)
}
})
}
}
12 changes: 12 additions & 0 deletions internal/xds/translator/testdata/in/xds-ir/load-balancer.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,15 @@ http:
- endpoints:
- host: "1.2.3.4"
port: 50000
- name: "seventh-route"
hostname: "*"
loadBalancer:
consistentHash:
header:
name: name
destination:
name: "seventh-route-dest"
settings:
- endpoints:
- host: "1.2.3.4"
port: 50000
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,20 @@
slowStartConfig:
slowStartWindow: 300s
type: EDS
- circuitBreakers:
thresholds:
- maxRetries: 1024
commonLbConfig:
localityWeightedLbConfig: {}
connectTimeout: 10s
dnsLookupFamily: V4_ONLY
edsClusterConfig:
edsConfig:
ads: {}
resourceApiVersion: V3
serviceName: seventh-route-dest
lbPolicy: MAGLEV
name: seventh-route-dest
outlierDetection: {}
perConnectionBufferLimitBytes: 32768
type: EDS
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,15 @@
loadBalancingWeight: 1
locality:
region: sixth-route-dest/backend/0
- clusterName: seventh-route-dest
endpoints:
- lbEndpoints:
- endpoint:
address:
socketAddress:
address: 1.2.3.4
portValue: 50000
loadBalancingWeight: 1
loadBalancingWeight: 1
locality:
region: seventh-route-dest/backend/0
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,13 @@
cluster: sixth-route-dest
upgradeConfigs:
- upgradeType: websocket
- match:
prefix: /
name: seventh-route
route:
cluster: seventh-route-dest
hashPolicy:
- header:
headerName: name
upgradeConfigs:
- upgradeType: websocket
3 changes: 2 additions & 1 deletion site/content/en/latest/api/extension_types.md
Original file line number Diff line number Diff line change
Expand Up @@ -562,7 +562,8 @@ _Appears in:_

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `type` | _[ConsistentHashType](#consistenthashtype)_ | true | Valid Type values are "SourceIP". |
| `type` | _[ConsistentHashType](#consistenthashtype)_ | true | ConsistentHashType defines the type of input to hash on. Valid Type values are "SourceIP" or "Header". |
| `header` | _[Header](#header)_ | false | Header configures the header hash policy when the consistent hash type is set to Header. |


#### ConsistentHashType
Expand Down

0 comments on commit 7863f81

Please sign in to comment.