Skip to content

Commit 698f6ee

Browse files
committed
add support for ip_hash lb method
1 parent 2d2539b commit 698f6ee

File tree

8 files changed

+203
-32
lines changed

8 files changed

+203
-32
lines changed

internal/controller/nginx/config/http/config.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -119,11 +119,12 @@ const (
119119

120120
// Upstream holds all configuration for an HTTP upstream.
121121
type Upstream struct {
122-
Name string
123-
ZoneSize string // format: 512k, 1m
124-
StateFile string
125-
KeepAlive UpstreamKeepAlive
126-
Servers []UpstreamServer
122+
Name string
123+
ZoneSize string // format: 512k, 1m
124+
StateFile string
125+
KeepAlive UpstreamKeepAlive
126+
LoadBalancingMethod string
127+
Servers []UpstreamServer
127128
}
128129

129130
// UpstreamKeepAlive holds the keepalive configuration for an HTTP upstream.

internal/controller/nginx/config/policies/upstreamsettings/processor.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ type Processor struct{}
1313
type UpstreamSettings struct {
1414
// ZoneSize is the zone size setting.
1515
ZoneSize string
16+
// LoadBalancingMethod is the load balancing method setting.
17+
LoadBalancingMethod string
1618
// KeepAlive contains the keepalive settings.
1719
KeepAlive http.UpstreamKeepAlive
1820
}
@@ -61,6 +63,10 @@ func processPolicies(pols []policies.Policy) UpstreamSettings {
6163
upstreamSettings.KeepAlive.Timeout = string(*usp.Spec.KeepAlive.Timeout)
6264
}
6365
}
66+
67+
if usp.Spec.LoadBalancingMethod != nil {
68+
upstreamSettings.LoadBalancingMethod = string(*usp.Spec.LoadBalancingMethod)
69+
}
6470
}
6571

6672
return upstreamSettings

internal/controller/nginx/config/policies/upstreamsettings/processor_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ func TestProcess(t *testing.T) {
3737
Time: helpers.GetPointer[ngfAPIv1alpha1.Duration]("5s"),
3838
Timeout: helpers.GetPointer[ngfAPIv1alpha1.Duration]("10s"),
3939
}),
40+
LoadBalancingMethod: helpers.GetPointer(ngfAPIv1alpha1.LoadBalancingTypeIPHash),
4041
},
4142
},
4243
},
@@ -48,6 +49,24 @@ func TestProcess(t *testing.T) {
4849
Time: "5s",
4950
Timeout: "10s",
5051
},
52+
LoadBalancingMethod: string(ngfAPIv1alpha1.LoadBalancingTypeIPHash),
53+
},
54+
},
55+
{
56+
name: "load balancing method set",
57+
policies: []policies.Policy{
58+
&ngfAPIv1alpha1.UpstreamSettingsPolicy{
59+
ObjectMeta: metav1.ObjectMeta{
60+
Name: "usp",
61+
Namespace: "test",
62+
},
63+
Spec: ngfAPIv1alpha1.UpstreamSettingsPolicySpec{
64+
LoadBalancingMethod: helpers.GetPointer(ngfAPIv1alpha1.LoadBalancingTypeRandomTwoLeastConnection),
65+
},
66+
},
67+
},
68+
expUpstreamSettings: UpstreamSettings{
69+
LoadBalancingMethod: string(ngfAPIv1alpha1.LoadBalancingTypeRandomTwoLeastConnection),
5170
},
5271
},
5372
{
@@ -220,6 +239,15 @@ func TestProcess(t *testing.T) {
220239
}),
221240
},
222241
},
242+
&ngfAPIv1alpha1.UpstreamSettingsPolicy{
243+
ObjectMeta: metav1.ObjectMeta{
244+
Name: "usp-loadBalancingMethod",
245+
Namespace: "test",
246+
},
247+
Spec: ngfAPIv1alpha1.UpstreamSettingsPolicySpec{
248+
LoadBalancingMethod: helpers.GetPointer(ngfAPIv1alpha1.LoadBalancingTypeIPHash),
249+
},
250+
},
223251
},
224252
expUpstreamSettings: UpstreamSettings{
225253
ZoneSize: "2m",
@@ -229,6 +257,7 @@ func TestProcess(t *testing.T) {
229257
Time: "5s",
230258
Timeout: "10s",
231259
},
260+
LoadBalancingMethod: string(ngfAPIv1alpha1.LoadBalancingTypeIPHash),
232261
},
233262
},
234263
{
@@ -310,6 +339,15 @@ func TestProcess(t *testing.T) {
310339
},
311340
},
312341
},
342+
&ngfAPIv1alpha1.UpstreamSettingsPolicy{
343+
ObjectMeta: metav1.ObjectMeta{
344+
Name: "usp-lb-method",
345+
Namespace: "test",
346+
},
347+
Spec: ngfAPIv1alpha1.UpstreamSettingsPolicySpec{
348+
LoadBalancingMethod: helpers.GetPointer(ngfAPIv1alpha1.LoadBalancingTypeIPHash),
349+
},
350+
},
313351
},
314352
expUpstreamSettings: UpstreamSettings{
315353
ZoneSize: "2m",
@@ -319,6 +357,7 @@ func TestProcess(t *testing.T) {
319357
Time: "5s",
320358
Timeout: "10s",
321359
},
360+
LoadBalancingMethod: string(ngfAPIv1alpha1.LoadBalancingTypeIPHash),
322361
},
323362
},
324363
}

internal/controller/nginx/config/policies/upstreamsettings/validator.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ func conflicts(a, b ngfAPI.UpstreamSettingsPolicySpec) bool {
8383
}
8484
}
8585

86+
if a.LoadBalancingMethod != nil && b.LoadBalancingMethod != nil {
87+
return true
88+
}
89+
8690
return false
8791
}
8892

@@ -103,6 +107,13 @@ func (v Validator) validateSettings(spec ngfAPI.UpstreamSettingsPolicySpec) erro
103107
allErrs = append(allErrs, v.validateUpstreamKeepAlive(*spec.KeepAlive, fieldPath.Child("keepAlive"))...)
104108
}
105109

110+
if spec.LoadBalancingMethod != nil {
111+
allErrs = append(
112+
allErrs,
113+
v.validateLoadBalancingMethod(*spec.LoadBalancingMethod, fieldPath.Child("loadBalancingMethod"))...,
114+
)
115+
}
116+
106117
return allErrs.ToAggregate()
107118
}
108119

@@ -130,3 +141,25 @@ func (v Validator) validateUpstreamKeepAlive(
130141

131142
return allErrs
132143
}
144+
145+
func (v Validator) validateLoadBalancingMethod(
146+
method ngfAPI.LoadBalancingType,
147+
fieldPath *field.Path,
148+
) field.ErrorList {
149+
var allErrs field.ErrorList
150+
151+
switch method {
152+
case ngfAPI.LoadBalancingTypeIPHash, ngfAPI.LoadBalancingTypeRandomTwoLeastConnection:
153+
default:
154+
allErrs = append(allErrs, field.NotSupported(
155+
fieldPath,
156+
method,
157+
[]string{
158+
string(ngfAPI.LoadBalancingTypeIPHash),
159+
string(ngfAPI.LoadBalancingTypeRandomTwoLeastConnection),
160+
},
161+
))
162+
}
163+
164+
return allErrs
165+
}

internal/controller/nginx/config/policies/upstreamsettings/validator_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ func createValidPolicy() *ngfAPI.UpstreamSettingsPolicy {
3838
Timeout: helpers.GetPointer[ngfAPI.Duration]("30s"),
3939
Connections: helpers.GetPointer[int32](100),
4040
},
41+
LoadBalancingMethod: helpers.GetPointer(ngfAPI.LoadBalancingTypeRandomTwoLeastConnection),
4142
},
4243
Status: v1.PolicyStatus{},
4344
}
@@ -117,6 +118,17 @@ func TestValidator_Validate(t *testing.T) {
117118
"'must contain an, at most, four digit number followed by 'ms', 's', 'm', or 'h'')]"),
118119
},
119120
},
121+
{
122+
name: "invalid load balancing method",
123+
policy: createModifiedPolicy(func(p *ngfAPI.UpstreamSettingsPolicy) *ngfAPI.UpstreamSettingsPolicy {
124+
p.Spec.LoadBalancingMethod = helpers.GetPointer[ngfAPI.LoadBalancingType]("invalid-lb-method")
125+
return p
126+
}),
127+
expConditions: []conditions.Condition{
128+
conditions.NewPolicyInvalid("spec.loadBalancingMethod: Unsupported value: \"invalid-lb-method\": " +
129+
"supported values: \"ip_hash\", \"random two least_conn\""),
130+
},
131+
},
120132
{
121133
name: "valid",
122134
policy: createValidPolicy(),
@@ -176,6 +188,7 @@ func TestValidator_Conflicts(t *testing.T) {
176188
Requests: helpers.GetPointer[int32](900),
177189
Time: helpers.GetPointer[ngfAPI.Duration]("50s"),
178190
},
191+
LoadBalancingMethod: helpers.GetPointer(ngfAPI.LoadBalancingTypeRandomTwoLeastConnection),
179192
},
180193
},
181194
polB: &ngfAPI.UpstreamSettingsPolicy{
@@ -246,6 +259,16 @@ func TestValidator_Conflicts(t *testing.T) {
246259
},
247260
conflicts: true,
248261
},
262+
{
263+
name: "load balancing method conflicts",
264+
polA: createValidPolicy(),
265+
polB: &ngfAPI.UpstreamSettingsPolicy{
266+
Spec: ngfAPI.UpstreamSettingsPolicySpec{
267+
LoadBalancingMethod: helpers.GetPointer(ngfAPI.LoadBalancingTypeIPHash),
268+
},
269+
},
270+
conflicts: true,
271+
},
249272
}
250273

251274
v := upstreamsettings.NewValidator(nil)

internal/controller/nginx/config/upstreams.go

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ const (
3232
plusZoneSizeStream = "1m"
3333
// stateDir is the directory for storing state files.
3434
stateDir = "/var/lib/nginx/state"
35+
// default load balancing method.
36+
randomTwoLeastConnLB = "random two least_conn"
3537
)
3638

3739
// keepAliveChecker takes an upstream name and returns if it has keep alive settings enabled.
@@ -185,12 +187,18 @@ func (g GeneratorImpl) createUpstream(
185187
}
186188
}
187189

190+
chosenLBMethod := randomTwoLeastConnLB
191+
if upstreamPolicySettings.LoadBalancingMethod != "" {
192+
chosenLBMethod = upstreamPolicySettings.LoadBalancingMethod
193+
}
194+
188195
return http.Upstream{
189-
Name: up.Name,
190-
ZoneSize: zoneSize,
191-
StateFile: stateFile,
192-
Servers: upstreamServers,
193-
KeepAlive: upstreamPolicySettings.KeepAlive,
196+
Name: up.Name,
197+
ZoneSize: zoneSize,
198+
StateFile: stateFile,
199+
Servers: upstreamServers,
200+
KeepAlive: upstreamPolicySettings.KeepAlive,
201+
LoadBalancingMethod: chosenLBMethod,
194202
}
195203
}
196204

internal/controller/nginx/config/upstreams_template.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ package config
1010
const upstreamsTemplateText = `
1111
{{ range $u := . }}
1212
upstream {{ $u.Name }} {
13-
random two least_conn;
13+
{{ if $u.LoadBalancingMethod -}}
14+
{{ $u.LoadBalancingMethod }};
15+
{{- end }}
1416
{{ if $u.ZoneSize -}}
1517
zone {{ $u.Name }} {{ $u.ZoneSize }};
1618
{{ end -}}

0 commit comments

Comments
 (0)