Skip to content

Commit e1c6270

Browse files
Raul MarreroRulox
Raul Marrero
authored andcommitted
Add lb-method support in vs and vsr
1 parent 676b851 commit e1c6270

File tree

7 files changed

+178
-41
lines changed

7 files changed

+178
-41
lines changed

docs/virtualserver-and-virtualserverroute.md

+20-5
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,23 @@ This document is the reference documentation for the resources. To see additiona
77
**Feature Status**: The VirtualServer and VirtualServerRoute resources are available as a preview feature: it is suitable for experimenting and testing; however, it must be used with caution in production environments. Additionally, while the feature is in preview, we might introduce some backward-incompatible changes to the resources specification in the next releases.
88

99
## Contents
10-
* [Prerequisites](#Prerequisites)
11-
* [VirtualServer Specification](#VirtualServer-Specification)
12-
* [VirtualServerRoute Specification](#VirtualServerRoute-Specification)
13-
* [Using VirtualServer and VirtualServerRoute](#Using-VirtualServer-and-VirtualServerRoute)
14-
* [Customization via ConfigMap](#Customization-via-ConfigMap)
10+
- [VirtualServer and VirtualServerRoute Resources](#VirtualServer-and-VirtualServerRoute-Resources)
11+
- [Contents](#Contents)
12+
- [Prerequisites](#Prerequisites)
13+
- [VirtualServer Specification](#VirtualServer-Specification)
14+
- [VirtualServer.TLS](#VirtualServerTLS)
15+
- [VirtualServer.Route](#VirtualServerRoute)
16+
- [VirtualServerRoute Specification](#VirtualServerRoute-Specification)
17+
- [VirtualServerRoute.Subroute](#VirtualServerRouteSubroute)
18+
- [Common Parts of the VirtualServer and VirtualServerRoute](#Common-Parts-of-the-VirtualServer-and-VirtualServerRoute)
19+
- [Upstream](#Upstream)
20+
- [Split](#Split)
21+
- [Rules](#Rules)
22+
- [Condition](#Condition)
23+
- [Match](#Match)
24+
- [Using VirtualServer and VirtualServerRoute](#Using-VirtualServer-and-VirtualServerRoute)
25+
- [Validation](#Validation)
26+
- [Customization via ConfigMap](#Customization-via-ConfigMap)
1527

1628
## Prerequisites
1729

@@ -164,13 +176,16 @@ The upstream defines a destination for the routing configuration. For example:
164176
name: tea
165177
service: tea-svc
166178
port: 80
179+
lb-method: "round_robin"
167180
```
168181

169182
| Field | Description | Type | Required |
170183
| ----- | ----------- | ---- | -------- |
171184
| `name` | The name of the upstream. Must be a valid DNS label as defined in RFC 1035. For example, `hello` and `upstream-123` are valid. The name must be unique among all upstreams of the resource. | `string` | Yes |
172185
| `service` | The name of a [service](https://kubernetes.io/docs/concepts/services-networking/service/). The service must belong to the same namespace as the resource. If the service doesn't exist, NGINX will assume the service has zero endpoints and return a `502` response for requests for this upstream. | `string` | Yes |
173186
| `port` | The port of the service. If the service doesn't define that port, NGINX will assume the service has zero endpoints and return a `502` response for requests for this upstream. The port must fall into the range `1..65553`. | `uint16` | Yes |
187+
| `lb-method` | The load [balancing method](https://docs.nginx.com/nginx/admin-guide/load-balancer/http-load-balancer/#choosing-a-load-balancing-method). To use the round-robin method, specify `round_robin`. The default is specified in the `lb-method` ConfigMap key. | `string` | No |
188+
174189

175190
### Split
176191

internal/configs/virtualserver.go

+14-4
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ func generateVirtualServerConfig(virtualServerEx *VirtualServerEx, tlsPemFileNam
9292
for _, u := range virtualServerEx.VirtualServer.Spec.Upstreams {
9393
upstreamName := virtualServerUpstreamNamer.GetNameForUpstream(u.Name)
9494
endpointsKey := GenerateEndpointsKey(virtualServerEx.VirtualServer.Namespace, u.Service, u.Port)
95-
ups := generateUpstream(upstreamName, virtualServerEx.Endpoints[endpointsKey], isPlus, baseCfgParams)
95+
ups := generateUpstream(upstreamName, u.LBMethod, virtualServerEx.Endpoints[endpointsKey], isPlus, baseCfgParams)
9696
upstreams = append(upstreams, ups)
9797
}
9898
// generate upstreams for each VirtualServerRoute
@@ -101,7 +101,7 @@ func generateVirtualServerConfig(virtualServerEx *VirtualServerEx, tlsPemFileNam
101101
for _, u := range vsr.Spec.Upstreams {
102102
upstreamName := upstreamNamer.GetNameForUpstream(u.Name)
103103
endpointsKey := GenerateEndpointsKey(vsr.Namespace, u.Service, u.Port)
104-
ups := generateUpstream(upstreamName, virtualServerEx.Endpoints[endpointsKey], isPlus, baseCfgParams)
104+
ups := generateUpstream(upstreamName, u.LBMethod, virtualServerEx.Endpoints[endpointsKey], isPlus, baseCfgParams)
105105
upstreams = append(upstreams, ups)
106106
}
107107
}
@@ -195,7 +195,7 @@ func generateVirtualServerConfig(virtualServerEx *VirtualServerEx, tlsPemFileNam
195195
}
196196
}
197197

198-
func generateUpstream(upstreamName string, endpoints []string, isPlus bool, cfgParams *ConfigParams) version2.Upstream {
198+
func generateUpstream(upstreamName string, lBMethod string, endpoints []string, isPlus bool, cfgParams *ConfigParams) version2.Upstream {
199199
var upsServers []version2.UpstreamServer
200200

201201
for _, e := range endpoints {
@@ -219,12 +219,22 @@ func generateUpstream(upstreamName string, endpoints []string, isPlus bool, cfgP
219219
ups := version2.Upstream{
220220
Name: upstreamName,
221221
Servers: upsServers,
222-
LBMethod: cfgParams.LBMethod,
222+
LBMethod: generateLBMethod(lBMethod, cfgParams.LBMethod),
223223
}
224224

225225
return ups
226226
}
227227

228+
func generateLBMethod(method string, defaultMethod string) string {
229+
if method == "" {
230+
return defaultMethod
231+
} else if method == "round_robin" {
232+
return ""
233+
}
234+
235+
return method
236+
}
237+
228238
func generateLocation(path string, upstreamName string, cfgParams *ConfigParams) version2.Location {
229239
loc := version2.Location{
230240
Path: path,

internal/configs/virtualserver_test.go

+31-3
Original file line numberDiff line numberDiff line change
@@ -754,7 +754,7 @@ func TestGenerateUpstream(t *testing.T) {
754754
LBMethod: "random",
755755
}
756756

757-
result := generateUpstream(name, endpoints, isPlus, &cfgParams)
757+
result := generateUpstream(name, "", endpoints, isPlus, &cfgParams)
758758
if !reflect.DeepEqual(result, expected) {
759759
t.Errorf("generateUpstream() returned %v but expected %v", result, expected)
760760
}
@@ -780,7 +780,7 @@ func TestGenerateUpstreamForZeroEndpoints(t *testing.T) {
780780
},
781781
}
782782

783-
result := generateUpstream(name, endpoints, isPlus, &cfgParams)
783+
result := generateUpstream(name, "", endpoints, isPlus, &cfgParams)
784784
if !reflect.DeepEqual(result, expectedForNGINX) {
785785
t.Errorf("generateUpstream(isPlus=%v) returned %v but expected %v", isPlus, result, expectedForNGINX)
786786
}
@@ -791,7 +791,7 @@ func TestGenerateUpstreamForZeroEndpoints(t *testing.T) {
791791
Servers: nil,
792792
}
793793

794-
result = generateUpstream(name, endpoints, isPlus, &cfgParams)
794+
result = generateUpstream(name, "", endpoints, isPlus, &cfgParams)
795795
if !reflect.DeepEqual(result, expectedForNGINXPlus) {
796796
t.Errorf("generateUpstream(isPlus=%v) returned %v but expected %v", isPlus, result, expectedForNGINXPlus)
797797
}
@@ -1436,3 +1436,31 @@ func TestGetNameForSourceForRulesRouteMapFromCondition(t *testing.T) {
14361436
}
14371437
}
14381438
}
1439+
1440+
func TestGenerateLBMethod(t *testing.T) {
1441+
defaultMethod := "random two least_conn"
1442+
1443+
tests := []struct {
1444+
input string
1445+
expected string
1446+
}{
1447+
{
1448+
input: "",
1449+
expected: defaultMethod,
1450+
},
1451+
{
1452+
input: "round_robin",
1453+
expected: "",
1454+
},
1455+
{
1456+
input: "random",
1457+
expected: "random",
1458+
},
1459+
}
1460+
for _, test := range tests {
1461+
result := generateLBMethod(test.input, defaultMethod)
1462+
if result != test.expected {
1463+
t.Errorf("generateLBMethod() returned %q but expected %q for input '%v'", result, test.expected, test.input)
1464+
}
1465+
}
1466+
}

internal/k8s/controller.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -590,7 +590,7 @@ func (lbc *LoadBalancerController) syncVirtualServer(task task) {
590590

591591
vs := obj.(*conf_v1alpha1.VirtualServer)
592592

593-
validationErr := validation.ValidateVirtualServer(vs)
593+
validationErr := validation.ValidateVirtualServer(vs, lbc.isNginxPlus)
594594
if validationErr != nil {
595595
err := lbc.configurator.DeleteVirtualServer(key)
596596
if err != nil {
@@ -648,7 +648,7 @@ func (lbc *LoadBalancerController) syncVirtualServerRoute(task task) {
648648

649649
vsr := obj.(*conf_v1alpha1.VirtualServerRoute)
650650

651-
validationErr := validation.ValidateVirtualServerRoute(vsr)
651+
validationErr := validation.ValidateVirtualServerRoute(vsr, lbc.isNginxPlus)
652652
if validationErr != nil {
653653
lbc.recorder.Eventf(vsr, api_v1.EventTypeWarning, "Rejected", "VirtualServerRoute %s is invalid and was rejected: %v", key, validationErr)
654654
}
@@ -1271,7 +1271,7 @@ func (lbc *LoadBalancerController) getVirtualServers() []*conf_v1alpha1.VirtualS
12711271
for _, obj := range lbc.virtualServerLister.List() {
12721272
vs := obj.(*conf_v1alpha1.VirtualServer)
12731273

1274-
err := validation.ValidateVirtualServer(vs)
1274+
err := validation.ValidateVirtualServer(vs, lbc.isNginxPlus)
12751275
if err != nil {
12761276
glog.V(3).Infof("Skipping invalid VirtualServer %s/%s: %v", vs.Namespace, vs.Name, err)
12771277
continue
@@ -1289,7 +1289,7 @@ func (lbc *LoadBalancerController) getVirtualServerRoutes() []*conf_v1alpha1.Vir
12891289
for _, obj := range lbc.virtualServerRouteLister.List() {
12901290
vsr := obj.(*conf_v1alpha1.VirtualServerRoute)
12911291

1292-
err := validation.ValidateVirtualServerRoute(vsr)
1292+
err := validation.ValidateVirtualServerRoute(vsr, lbc.isNginxPlus)
12931293
if err != nil {
12941294
glog.V(3).Infof("Skipping invalid VirtualServerRoute %s/%s: %v", vsr.Namespace, vsr.Name, err)
12951295
continue
@@ -1541,7 +1541,7 @@ func (lbc *LoadBalancerController) createVirtualServer(virtualServer *conf_v1alp
15411541

15421542
vsr := obj.(*conf_v1alpha1.VirtualServerRoute)
15431543

1544-
err = validation.ValidateVirtualServerRouteForVirtualServer(vsr, virtualServer.Spec.Host, r.Path)
1544+
err = validation.ValidateVirtualServerRouteForVirtualServer(vsr, virtualServer.Spec.Host, r.Path, lbc.isNginxPlus)
15451545
if err != nil {
15461546
glog.Warningf("VirtualServer %s/%s references invalid VirtualServerRoute %s: %v", virtualServer.Name, virtualServer.Namespace, vsrKey, err)
15471547
virtualServerRouteErrors = append(virtualServerRouteErrors, newVirtualServerRouteErrorFromVSR(vsr, err))

pkg/apis/configuration/v1alpha1/types.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,10 @@ type VirtualServerSpec struct {
2525

2626
// Upstream defines an upstream.
2727
type Upstream struct {
28-
Name string `json:"name"`
29-
Service string `json:"service"`
30-
Port uint16 `json:"port"`
28+
Name string `json:"name"`
29+
Service string `json:"service"`
30+
Port uint16 `json:"port"`
31+
LBMethod string `json:"lb-method"`
3132
}
3233

3334
// Route defines a route.

pkg/apis/configuration/validation/validation.go

+35-11
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,28 @@ import (
55
"regexp"
66
"strings"
77

8+
"github.com/nginxinc/kubernetes-ingress/internal/configs"
9+
810
"github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1alpha1"
911
"k8s.io/apimachinery/pkg/util/sets"
1012
"k8s.io/apimachinery/pkg/util/validation"
1113
"k8s.io/apimachinery/pkg/util/validation/field"
1214
)
1315

1416
// ValidateVirtualServer validates a VirtualServer.
15-
func ValidateVirtualServer(virtualServer *v1alpha1.VirtualServer) error {
16-
allErrs := validateVirtualServerSpec(&virtualServer.Spec, field.NewPath("spec"))
17+
func ValidateVirtualServer(virtualServer *v1alpha1.VirtualServer, isPlus bool) error {
18+
allErrs := validateVirtualServerSpec(&virtualServer.Spec, field.NewPath("spec"), isPlus)
1719
return allErrs.ToAggregate()
1820
}
1921

2022
// validateVirtualServerSpec validates a VirtualServerSpec.
21-
func validateVirtualServerSpec(spec *v1alpha1.VirtualServerSpec, fieldPath *field.Path) field.ErrorList {
23+
func validateVirtualServerSpec(spec *v1alpha1.VirtualServerSpec, fieldPath *field.Path, isPlus bool) field.ErrorList {
2224
allErrs := field.ErrorList{}
2325

2426
allErrs = append(allErrs, validateHost(spec.Host, fieldPath.Child("host"))...)
2527
allErrs = append(allErrs, validateTLS(spec.TLS, fieldPath.Child("tls"))...)
2628

27-
upstreamErrs, upstreamNames := validateUpstreams(spec.Upstreams, fieldPath.Child("upstreams"))
29+
upstreamErrs, upstreamNames := validateUpstreams(spec.Upstreams, fieldPath.Child("upstreams"), isPlus)
2830
allErrs = append(allErrs, upstreamErrs...)
2931

3032
allErrs = append(allErrs, validateVirtualServerRoutes(spec.Routes, fieldPath.Child("routes"), upstreamNames)...)
@@ -55,6 +57,27 @@ func validateTLS(tls *v1alpha1.TLS, fieldPath *field.Path) field.ErrorList {
5557
return validateSecretName(tls.Secret, fieldPath.Child("secret"))
5658
}
5759

60+
func validateUpstreamLBMethod(lBMethod string, fieldPath *field.Path, isPlus bool) field.ErrorList {
61+
allErrs := field.ErrorList{}
62+
if lBMethod == "" {
63+
return allErrs
64+
}
65+
66+
if isPlus {
67+
_, err := configs.ParseLBMethodForPlus(lBMethod)
68+
if err != nil {
69+
return append(allErrs, field.Invalid(fieldPath, lBMethod, err.Error()))
70+
}
71+
} else {
72+
_, err := configs.ParseLBMethod(lBMethod)
73+
if err != nil {
74+
return append(allErrs, field.Invalid(fieldPath, lBMethod, err.Error()))
75+
}
76+
}
77+
78+
return allErrs
79+
}
80+
5881
// validateSecretName checks if a secret name is valid.
5982
// It performs the same validation as ValidateSecretName from k8s.io/kubernetes/pkg/apis/core/validation/validation.go.
6083
func validateSecretName(name string, fieldPath *field.Path) field.ErrorList {
@@ -71,7 +94,7 @@ func validateSecretName(name string, fieldPath *field.Path) field.ErrorList {
7194
return allErrs
7295
}
7396

74-
func validateUpstreams(upstreams []v1alpha1.Upstream, fieldPath *field.Path) (allErrs field.ErrorList, upstreamNames sets.String) {
97+
func validateUpstreams(upstreams []v1alpha1.Upstream, fieldPath *field.Path, isPlus bool) (allErrs field.ErrorList, upstreamNames sets.String) {
7598
allErrs = field.ErrorList{}
7699
upstreamNames = sets.String{}
77100

@@ -88,6 +111,7 @@ func validateUpstreams(upstreams []v1alpha1.Upstream, fieldPath *field.Path) (al
88111
}
89112

90113
allErrs = append(allErrs, validateServiceName(u.Service, idxPath.Child("service"))...)
114+
allErrs = append(allErrs, validateUpstreamLBMethod(u.LBMethod, idxPath.Child("lb-method"), isPlus)...)
91115

92116
for _, msg := range validation.IsValidPortNum(int(u.Port)) {
93117
allErrs = append(allErrs, field.Invalid(idxPath.Child("port"), u.Port, msg))
@@ -410,23 +434,23 @@ func isValidMatchValue(value string) []string {
410434
}
411435

412436
// ValidateVirtualServerRoute validates a VirtualServerRoute.
413-
func ValidateVirtualServerRoute(virtualServerRoute *v1alpha1.VirtualServerRoute) error {
414-
allErrs := validateVirtualServerRouteSpec(&virtualServerRoute.Spec, field.NewPath("spec"), "", "/")
437+
func ValidateVirtualServerRoute(virtualServerRoute *v1alpha1.VirtualServerRoute, isPlus bool) error {
438+
allErrs := validateVirtualServerRouteSpec(&virtualServerRoute.Spec, field.NewPath("spec"), "", "/", isPlus)
415439
return allErrs.ToAggregate()
416440
}
417441

418442
// ValidateVirtualServerRouteForVirtualServer validates a VirtualServerRoute for a VirtualServer represented by its host and path prefix.
419-
func ValidateVirtualServerRouteForVirtualServer(virtualServerRoute *v1alpha1.VirtualServerRoute, virtualServerHost string, pathPrefix string) error {
420-
allErrs := validateVirtualServerRouteSpec(&virtualServerRoute.Spec, field.NewPath("spec"), virtualServerHost, pathPrefix)
443+
func ValidateVirtualServerRouteForVirtualServer(virtualServerRoute *v1alpha1.VirtualServerRoute, virtualServerHost string, pathPrefix string, isPlus bool) error {
444+
allErrs := validateVirtualServerRouteSpec(&virtualServerRoute.Spec, field.NewPath("spec"), virtualServerHost, pathPrefix, isPlus)
421445
return allErrs.ToAggregate()
422446
}
423447

424-
func validateVirtualServerRouteSpec(spec *v1alpha1.VirtualServerRouteSpec, fieldPath *field.Path, virtualServerHost string, pathPrefix string) field.ErrorList {
448+
func validateVirtualServerRouteSpec(spec *v1alpha1.VirtualServerRouteSpec, fieldPath *field.Path, virtualServerHost string, pathPrefix string, isPlus bool) field.ErrorList {
425449
allErrs := field.ErrorList{}
426450

427451
allErrs = append(allErrs, validateVirtualServerRouteHost(spec.Host, virtualServerHost, fieldPath.Child("host"))...)
428452

429-
upstreamErrs, upstreamNames := validateUpstreams(spec.Upstreams, fieldPath.Child("upstreams"))
453+
upstreamErrs, upstreamNames := validateUpstreams(spec.Upstreams, fieldPath.Child("upstreams"), isPlus)
430454
allErrs = append(allErrs, upstreamErrs...)
431455

432456
allErrs = append(allErrs, validateVirtualServerRouteSubroutes(spec.Subroutes, fieldPath.Child("subroutes"), upstreamNames, pathPrefix)...)

0 commit comments

Comments
 (0)