diff --git a/docs/content/migration/v3.md b/docs/content/migration/v3.md index 4f2f1d6e6..f38081fa7 100644 --- a/docs/content/migration/v3.md +++ b/docs/content/migration/v3.md @@ -187,3 +187,18 @@ and will be removed in the next major version. In `v3.3.4`, the OpenTelemetry Request Duration metric (named `traefik_(entrypoint|router|service)_request_duration_seconds`) unit has been changed from milliseconds to seconds. To be consistent with the naming and other metrics providers, the metric now reports the duration in seconds. + +## v3.3 to v3.4 + +### Kubernetes CRD Provider + +In `v3.4`, the HTTP service definition has been updated. +The strategy field now supports two new values: `wrr` and `p2c` (please refer to the [HTTP Services Load Balancing documentation](../../routing/services/#load-balancing-strategy) for more details). + +CRDs can be updated with this command: + +```shell +kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.4/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml +``` + +Please note that the `RoundRobin` strategy value is now deprecated, but still supported and equivalent to `wrr`, and will be removed in the next major release. diff --git a/docs/content/reference/dynamic-configuration/docker-labels.yml b/docs/content/reference/dynamic-configuration/docker-labels.yml index bc8b268f9..d6534790b 100644 --- a/docs/content/reference/dynamic-configuration/docker-labels.yml +++ b/docs/content/reference/dynamic-configuration/docker-labels.yml @@ -221,6 +221,7 @@ - "traefik.http.services.service02.loadbalancer.sticky.cookie.path=foobar" - "traefik.http.services.service02.loadbalancer.sticky.cookie.samesite=foobar" - "traefik.http.services.service02.loadbalancer.sticky.cookie.secure=true" +- "traefik.http.services.service02.loadbalancer.strategy=foobar" - "traefik.http.services.service02.loadbalancer.server.port=foobar" - "traefik.http.services.service02.loadbalancer.server.preservepath=true" - "traefik.http.services.service02.loadbalancer.server.scheme=foobar" diff --git a/docs/content/reference/dynamic-configuration/file.toml b/docs/content/reference/dynamic-configuration/file.toml index 0c5ae3bbd..1c54defe8 100644 --- a/docs/content/reference/dynamic-configuration/file.toml +++ b/docs/content/reference/dynamic-configuration/file.toml @@ -54,6 +54,7 @@ [http.services.Service01.failover.healthCheck] [http.services.Service02] [http.services.Service02.loadBalancer] + strategy = "foobar" passHostHeader = true serversTransport = "foobar" [http.services.Service02.loadBalancer.sticky] diff --git a/docs/content/reference/dynamic-configuration/file.yaml b/docs/content/reference/dynamic-configuration/file.yaml index 7c0766810..2a8956bbc 100644 --- a/docs/content/reference/dynamic-configuration/file.yaml +++ b/docs/content/reference/dynamic-configuration/file.yaml @@ -80,6 +80,7 @@ http: - url: foobar weight: 42 preservePath: true + strategy: foobar healthCheck: scheme: foobar mode: foobar diff --git a/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml b/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml index 0b5982e20..76f596493 100644 --- a/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml +++ b/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml @@ -290,10 +290,14 @@ spec: type: object type: object strategy: + default: wrr description: |- Strategy defines the load balancing strategy between the servers. - RoundRobin is the only supported value at the moment. + Supported values are: wrr (Weighed round-robin) and p2c (Power of two choices). + RoundRobin value is deprecated and supported for backward compatibility. enum: + - wrr + - p2c - RoundRobin type: string weight: @@ -1217,10 +1221,14 @@ spec: type: object type: object strategy: + default: wrr description: |- Strategy defines the load balancing strategy between the servers. - RoundRobin is the only supported value at the moment. + Supported values are: wrr (Weighed round-robin) and p2c (Power of two choices). + RoundRobin value is deprecated and supported for backward compatibility. enum: + - wrr + - p2c - RoundRobin type: string weight: @@ -2924,10 +2932,14 @@ spec: type: object type: object strategy: + default: wrr description: |- Strategy defines the load balancing strategy between the servers. - RoundRobin is the only supported value at the moment. + Supported values are: wrr (Weighed round-robin) and p2c (Power of two choices). + RoundRobin value is deprecated and supported for backward compatibility. enum: + - wrr + - p2c - RoundRobin type: string weight: @@ -3048,10 +3060,14 @@ spec: type: object type: object strategy: + default: wrr description: |- Strategy defines the load balancing strategy between the servers. - RoundRobin is the only supported value at the moment. + Supported values are: wrr (Weighed round-robin) and p2c (Power of two choices). + RoundRobin value is deprecated and supported for backward compatibility. enum: + - wrr + - p2c - RoundRobin type: string weight: @@ -3250,10 +3266,14 @@ spec: type: object type: object strategy: + default: wrr description: |- Strategy defines the load balancing strategy between the servers. - RoundRobin is the only supported value at the moment. + Supported values are: wrr (Weighed round-robin) and p2c (Power of two choices). + RoundRobin value is deprecated and supported for backward compatibility. enum: + - wrr + - p2c - RoundRobin type: string weight: diff --git a/docs/content/reference/dynamic-configuration/kv-ref.md b/docs/content/reference/dynamic-configuration/kv-ref.md index c1a080925..4d218e599 100644 --- a/docs/content/reference/dynamic-configuration/kv-ref.md +++ b/docs/content/reference/dynamic-configuration/kv-ref.md @@ -297,6 +297,7 @@ THIS FILE MUST NOT BE EDITED BY HAND | `traefik/http/services/Service02/loadBalancer/sticky/cookie/path` | `foobar` | | `traefik/http/services/Service02/loadBalancer/sticky/cookie/sameSite` | `foobar` | | `traefik/http/services/Service02/loadBalancer/sticky/cookie/secure` | `true` | +| `traefik/http/services/Service02/loadBalancer/strategy` | `foobar` | | `traefik/http/services/Service03/mirroring/healthCheck` | `` | | `traefik/http/services/Service03/mirroring/maxBodySize` | `42` | | `traefik/http/services/Service03/mirroring/mirrorBody` | `true` | diff --git a/docs/content/reference/dynamic-configuration/traefik.io_ingressroutes.yaml b/docs/content/reference/dynamic-configuration/traefik.io_ingressroutes.yaml index c1cb4024d..8fde2b818 100644 --- a/docs/content/reference/dynamic-configuration/traefik.io_ingressroutes.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.io_ingressroutes.yaml @@ -290,10 +290,14 @@ spec: type: object type: object strategy: + default: wrr description: |- Strategy defines the load balancing strategy between the servers. - RoundRobin is the only supported value at the moment. + Supported values are: wrr (Weighed round-robin) and p2c (Power of two choices). + RoundRobin value is deprecated and supported for backward compatibility. enum: + - wrr + - p2c - RoundRobin type: string weight: diff --git a/docs/content/reference/dynamic-configuration/traefik.io_middlewares.yaml b/docs/content/reference/dynamic-configuration/traefik.io_middlewares.yaml index cf0da0719..5ae821eb7 100644 --- a/docs/content/reference/dynamic-configuration/traefik.io_middlewares.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.io_middlewares.yaml @@ -454,10 +454,14 @@ spec: type: object type: object strategy: + default: wrr description: |- Strategy defines the load balancing strategy between the servers. - RoundRobin is the only supported value at the moment. + Supported values are: wrr (Weighed round-robin) and p2c (Power of two choices). + RoundRobin value is deprecated and supported for backward compatibility. enum: + - wrr + - p2c - RoundRobin type: string weight: diff --git a/docs/content/reference/dynamic-configuration/traefik.io_traefikservices.yaml b/docs/content/reference/dynamic-configuration/traefik.io_traefikservices.yaml index 1e87f2f25..de7661131 100644 --- a/docs/content/reference/dynamic-configuration/traefik.io_traefikservices.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.io_traefikservices.yaml @@ -314,10 +314,14 @@ spec: type: object type: object strategy: + default: wrr description: |- Strategy defines the load balancing strategy between the servers. - RoundRobin is the only supported value at the moment. + Supported values are: wrr (Weighed round-robin) and p2c (Power of two choices). + RoundRobin value is deprecated and supported for backward compatibility. enum: + - wrr + - p2c - RoundRobin type: string weight: @@ -438,10 +442,14 @@ spec: type: object type: object strategy: + default: wrr description: |- Strategy defines the load balancing strategy between the servers. - RoundRobin is the only supported value at the moment. + Supported values are: wrr (Weighed round-robin) and p2c (Power of two choices). + RoundRobin value is deprecated and supported for backward compatibility. enum: + - wrr + - p2c - RoundRobin type: string weight: @@ -640,10 +648,14 @@ spec: type: object type: object strategy: + default: wrr description: |- Strategy defines the load balancing strategy between the servers. - RoundRobin is the only supported value at the moment. + Supported values are: wrr (Weighed round-robin) and p2c (Power of two choices). + RoundRobin value is deprecated and supported for backward compatibility. enum: + - wrr + - p2c - RoundRobin type: string weight: diff --git a/docs/content/routing/providers/consul-catalog.md b/docs/content/routing/providers/consul-catalog.md index f539a6868..76b326b55 100644 --- a/docs/content/routing/providers/consul-catalog.md +++ b/docs/content/routing/providers/consul-catalog.md @@ -346,6 +346,14 @@ you'd add the tag `traefik.http.services.{name-of-your-choice}.loadbalancer.pass traefik.http.services.myservice.loadbalancer.responseforwarding.flushinterval=10 ``` +??? info "`traefik.http.services..loadbalancer.strategy`" + + See [load balancing strategy](../services/index.md#load-balancing-strategy) for more information. + + ```yaml + traefik.http.services.myservice.loadbalancer.strategy=p2c + ``` + ### Middleware You can declare pieces of middleware using tags starting with `traefik.http.middlewares.{name-of-your-choice}.`, followed by the middleware type/options. diff --git a/docs/content/routing/providers/docker.md b/docs/content/routing/providers/docker.md index 928fafef1..aedbfe09f 100644 --- a/docs/content/routing/providers/docker.md +++ b/docs/content/routing/providers/docker.md @@ -461,6 +461,14 @@ you'd add the label `traefik.http.services..loadbalancer.pa - "traefik.http.services.myservice.loadbalancer.responseforwarding.flushinterval=10" ``` +??? info "`traefik.http.services..loadbalancer.strategy`" + + See [load balancing strategy](../services/index.md#load-balancing-strategy) for more information. + + ```yaml + - "traefik.http.services.myservice.loadbalancer.strategy=p2c" + ``` + ### Middleware You can declare pieces of middleware using labels starting with `traefik.http.middlewares..`, diff --git a/docs/content/routing/providers/ecs.md b/docs/content/routing/providers/ecs.md index 6fed9e9ae..e85975409 100644 --- a/docs/content/routing/providers/ecs.md +++ b/docs/content/routing/providers/ecs.md @@ -350,6 +350,14 @@ you'd add the label `traefik.http.services.{name-of-your-choice}.loadbalancer.pa traefik.http.services.myservice.loadbalancer.responseforwarding.flushinterval=10 ``` +??? info "`traefik.http.services..loadbalancer.strategy`" + + See [load balancing strategy](../services/index.md#load-balancing-strategy) for more information. + + ```yaml + traefik.http.services.myservice.loadbalancer.strategy=p2c + ``` + ### Middleware You can declare pieces of middleware using labels starting with `traefik.http.middlewares.{name-of-your-choice}.`, followed by the middleware type/options. diff --git a/docs/content/routing/providers/kubernetes-crd.md b/docs/content/routing/providers/kubernetes-crd.md index 9188837df..be1e038d2 100644 --- a/docs/content/routing/providers/kubernetes-crd.md +++ b/docs/content/routing/providers/kubernetes-crd.md @@ -358,19 +358,19 @@ Register the `IngressRoute` [kind](../../reference/dynamic-configuration/kuberne maxAge: 42 path: /foo domain: foo.com - strategy: RoundRobin + strategy: wrr # [16] weight: 10 - nativeLB: true # [16] - nodePortLB: true # [17] - tls: # [18] - secretName: supersecret # [19] - options: # [20] - name: opt # [21] - namespace: default # [22] - certResolver: foo # [23] - domains: # [24] - - main: example.net # [25] - sans: # [26] + nativeLB: true # [17] + nodePortLB: true # [18] + tls: # [19] + secretName: supersecret # [20] + options: # [21] + name: opt # [22] + namespace: default # [23] + certResolver: foo # [24] + domains: # [25] + - main: example.net # [26] + sans: # [27] - a.example.net - b.example.net ``` @@ -392,17 +392,18 @@ Register the `IngressRoute` [kind](../../reference/dynamic-configuration/kuberne | [13] | `services[n].port` | Defines the port of a [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/). This can be a reference to a named port. | | [14] | `services[n].serversTransport` | Defines the reference to a [ServersTransport](#kind-serverstransport). The ServersTransport namespace is assumed to be the [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) namespace (see [ServersTransport reference](#serverstransport-reference)). | | [15] | `services[n].healthCheck` | Defines the HealthCheck when service references a [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type ExternalName. | -| [16] | `services[n].nativeLB` | Controls, when creating the load-balancer, whether the LB's children are directly the pods IPs or if the only child is the Kubernetes Service clusterIP. | -| [17] | `services[n].nodePortLB` | Controls, when creating the load-balancer, whether the LB's children are directly the nodes internal IPs using the nodePort when the service type is NodePort. | -| [18] | `tls` | Defines [TLS](../routers/index.md#tls) certificate configuration | -| [19] | `tls.secretName` | Defines the [secret](https://kubernetes.io/docs/concepts/configuration/secret/) name used to store the certificate (in the `IngressRoute` namespace) | -| [20] | `tls.options` | Defines the reference to a [TLSOption](#kind-tlsoption) | -| [21] | `options.name` | Defines the [TLSOption](#kind-tlsoption) name | -| [22] | `options.namespace` | Defines the [TLSOption](#kind-tlsoption) namespace | -| [23] | `tls.certResolver` | Defines the reference to a [CertResolver](../routers/index.md#certresolver) | -| [24] | `tls.domains` | List of [domains](../routers/index.md#domains) | -| [25] | `domains[n].main` | Defines the main domain name | -| [26] | `domains[n].sans` | List of SANs (alternative domains) | +| [16] | `services[n].strategy` | Defines the load-balancing strategy for the load-balancer. Supported values are `wrr` and `p2c`, please refer to the [Load Balancing documentation](../routing/services/#load-balancing-strategy) for more information. | +| [17] | `services[n].nativeLB` | Controls, when creating the load-balancer, whether the LB's children are directly the pods IPs or if the only child is the Kubernetes Service clusterIP. | +| [18] | `services[n].nodePortLB` | Controls, when creating the load-balancer, whether the LB's children are directly the nodes internal IPs using the nodePort when the service type is NodePort. | +| [19] | `tls` | Defines [TLS](../routers/index.md#tls) certificate configuration | +| [20] | `tls.secretName` | Defines the [secret](https://kubernetes.io/docs/concepts/configuration/secret/) name used to store the certificate (in the `IngressRoute` namespace) | +| [21] | `tls.options` | Defines the reference to a [TLSOption](#kind-tlsoption) | +| [22] | `options.name` | Defines the [TLSOption](#kind-tlsoption) name | +| [23] | `options.namespace` | Defines the [TLSOption](#kind-tlsoption) namespace | +| [24] | `tls.certResolver` | Defines the reference to a [CertResolver](../routers/index.md#certresolver) | +| [25] | `tls.domains` | List of [domains](../routers/index.md#domains) | +| [26] | `domains[n].main` | Defines the main domain name | +| [27] | `domains[n].sans` | List of SANs (alternative domains) | ??? example "Declaring an IngressRoute" @@ -605,7 +606,7 @@ Register the `IngressRoute` [kind](../../reference/dynamic-configuration/kuberne #### Load Balancing -More information in the dedicated server [load balancing](../services/index.md#load-balancing) section. +More information in the dedicated server [load balancing](../services/index.md#load-balancing-strategy) section. !!! info "Declaring and using Kubernetes Service Load Balancing" diff --git a/docs/content/routing/providers/kv.md b/docs/content/routing/providers/kv.md index 1234d1357..37e6148f5 100644 --- a/docs/content/routing/providers/kv.md +++ b/docs/content/routing/providers/kv.md @@ -300,6 +300,14 @@ A Story of key & values |---------------------------------------------------------------------------------|-------| | `traefik/http/services/myservice/loadbalancer/responseforwarding/flushinterval` | `10` | +??? info "`traefik/http/services//loadbalancer/strategy`" + + See [load balancing strategy](../services/index.md#load-balancing-strategy) for more information. + + | Key (Path) | Value | + |---------------------------------------------------------|-------| + | `traefik/http/services/myservice/loadbalancer/strategy` | `p2c` | + ??? info "`traefik/http/services//mirroring/service`" | Key (Path) | Value | diff --git a/docs/content/routing/providers/nomad.md b/docs/content/routing/providers/nomad.md index 175f1d61c..613f35a7b 100644 --- a/docs/content/routing/providers/nomad.md +++ b/docs/content/routing/providers/nomad.md @@ -338,6 +338,14 @@ you'd add the tag `traefik.http.services.{name-of-your-choice}.loadbalancer.pass traefik.http.services.myservice.loadbalancer.responseforwarding.flushinterval=10 ``` +??? info "`traefik.http.services..loadbalancer.strategy`" + + See [load balancing strategy](../services/index.md#load-balancing-strategy) for more information. + + ```yaml + traefik.http.services.myservice.loadbalancer.strategy=p2c + ``` + ### Middleware You can declare pieces of middleware using tags starting with `traefik.http.middlewares.{name-of-your-choice}.`, followed by the middleware type/options. diff --git a/docs/content/routing/providers/swarm.md b/docs/content/routing/providers/swarm.md index 754931a71..bd9685ef6 100644 --- a/docs/content/routing/providers/swarm.md +++ b/docs/content/routing/providers/swarm.md @@ -467,6 +467,14 @@ you'd add the label `traefik.http.services..loadbalancer.pa - "traefik.http.services.myservice.loadbalancer.responseforwarding.flushinterval=10" ``` +??? info "`traefik.http.services..loadbalancer.strategy`" + + See [load balancing strategy](../services/index.md#load-balancing-strategy) for more information. + + ```yaml + - "traefik.http.services.myservice.loadbalancer.strategy=p2c" + ``` + ### Middleware You can declare pieces of middleware using labels starting with `traefik.http.middlewares..`, diff --git a/docs/content/routing/services/index.md b/docs/content/routing/services/index.md index 9cc266acc..199fb81a3 100644 --- a/docs/content/routing/services/index.md +++ b/docs/content/routing/services/index.md @@ -139,6 +139,47 @@ The `url` option point to a specific instance. url = "http://private-ip-server-1/" ``` +The `preservePath` option allows to preserve the URL path. + +!!! info "Health Check" + + When a [health check](#health-check) is configured for the server, the path is not preserved. + +??? example "A Service with One Server and PreservePath -- Using the [File Provider](../../providers/file.md)" + + ```yaml tab="YAML" + ## Dynamic configuration + http: + services: + my-service: + loadBalancer: + servers: + - url: "http://private-ip-server-1/base" + preservePath: true + ``` + + ```toml tab="TOML" + ## Dynamic configuration + [http.services] + [http.services.my-service.loadBalancer] + [[http.services.my-service.loadBalancer.servers]] + url = "http://private-ip-server-1/base" + preservePath = true + ``` + +#### Load Balancing Strategy + +The `strategy` option allows to choose the load balancing algorithm. + +Two load balancing algorithms are supported: + +- Weighed round-robin (wrr) +- Power of two choices (p2c) + +##### WRR + +Weighed round-robin is the default strategy (and does not need to be specified). + The `weight` option allows for weighted load balancing on the servers. ??? example "A Service with Two Servers with Weight -- Using the [File Provider](../../providers/file.md)" @@ -169,39 +210,11 @@ The `weight` option allows for weighted load balancing on the servers. weight = 1 ``` -The `preservePath` option allows to preserve the URL path. +##### P2C -!!! info "Health Check" +Power of two choices algorithm is a load balancing strategy that selects two servers at random and chooses the one with the least number of active requests. - When a [health check](#health-check) is configured for the server, the path is not preserved. - -??? example "A Service with One Server and PreservePath -- Using the [File Provider](../../providers/file.md)" - - ```yaml tab="YAML" - ## Dynamic configuration - http: - services: - my-service: - loadBalancer: - servers: - - url: "http://private-ip-server-1/base" - preservePath: true - ``` - - ```toml tab="TOML" - ## Dynamic configuration - [http.services] - [http.services.my-service.loadBalancer] - [[http.services.my-service.loadBalancer.servers]] - url = "http://private-ip-server-1/base" - preservePath = true - ``` - -#### Load-balancing - -For now, only round robin load balancing is supported: - -??? example "Load Balancing -- Using the [File Provider](../../providers/file.md)" +??? example "P2C Load Balancing -- Using the [File Provider](../../providers/file.md)" ```yaml tab="YAML" ## Dynamic configuration @@ -209,19 +222,24 @@ For now, only round robin load balancing is supported: services: my-service: loadBalancer: + strategy: "p2c" servers: - url: "http://private-ip-server-1/" - url: "http://private-ip-server-2/" + - url: "http://private-ip-server-3/" ``` ```toml tab="TOML" ## Dynamic configuration [http.services] [http.services.my-service.loadBalancer] + strategy = "p2c" [[http.services.my-service.loadBalancer.servers]] url = "http://private-ip-server-1/" [[http.services.my-service.loadBalancer.servers]] - url = "http://private-ip-server-2/" + url = "http://private-ip-server-2/" + [[http.services.my-service.loadBalancer.servers]] + url = "http://private-ip-server-3/" ``` #### Sticky sessions diff --git a/integration/fixtures/k8s/01-traefik-crd.yml b/integration/fixtures/k8s/01-traefik-crd.yml index 0b5982e20..76f596493 100644 --- a/integration/fixtures/k8s/01-traefik-crd.yml +++ b/integration/fixtures/k8s/01-traefik-crd.yml @@ -290,10 +290,14 @@ spec: type: object type: object strategy: + default: wrr description: |- Strategy defines the load balancing strategy between the servers. - RoundRobin is the only supported value at the moment. + Supported values are: wrr (Weighed round-robin) and p2c (Power of two choices). + RoundRobin value is deprecated and supported for backward compatibility. enum: + - wrr + - p2c - RoundRobin type: string weight: @@ -1217,10 +1221,14 @@ spec: type: object type: object strategy: + default: wrr description: |- Strategy defines the load balancing strategy between the servers. - RoundRobin is the only supported value at the moment. + Supported values are: wrr (Weighed round-robin) and p2c (Power of two choices). + RoundRobin value is deprecated and supported for backward compatibility. enum: + - wrr + - p2c - RoundRobin type: string weight: @@ -2924,10 +2932,14 @@ spec: type: object type: object strategy: + default: wrr description: |- Strategy defines the load balancing strategy between the servers. - RoundRobin is the only supported value at the moment. + Supported values are: wrr (Weighed round-robin) and p2c (Power of two choices). + RoundRobin value is deprecated and supported for backward compatibility. enum: + - wrr + - p2c - RoundRobin type: string weight: @@ -3048,10 +3060,14 @@ spec: type: object type: object strategy: + default: wrr description: |- Strategy defines the load balancing strategy between the servers. - RoundRobin is the only supported value at the moment. + Supported values are: wrr (Weighed round-robin) and p2c (Power of two choices). + RoundRobin value is deprecated and supported for backward compatibility. enum: + - wrr + - p2c - RoundRobin type: string weight: @@ -3250,10 +3266,14 @@ spec: type: object type: object strategy: + default: wrr description: |- Strategy defines the load balancing strategy between the servers. - RoundRobin is the only supported value at the moment. + Supported values are: wrr (Weighed round-robin) and p2c (Power of two choices). + RoundRobin value is deprecated and supported for backward compatibility. enum: + - wrr + - p2c - RoundRobin type: string weight: diff --git a/integration/testdata/rawdata-consul.json b/integration/testdata/rawdata-consul.json index 9b652ab75..a30995823 100644 --- a/integration/testdata/rawdata-consul.json +++ b/integration/testdata/rawdata-consul.json @@ -200,6 +200,7 @@ "url": "http://10.0.1.1:8889" } ], + "strategy": "wrr", "passHostHeader": true, "responseForwarding": { "flushInterval": "100ms" @@ -225,6 +226,7 @@ "url": "http://10.0.1.2:8889" } ], + "strategy": "wrr", "passHostHeader": true, "responseForwarding": { "flushInterval": "100ms" @@ -242,6 +244,7 @@ "url": "http://10.0.1.3:8889" } ], + "strategy": "wrr", "passHostHeader": true, "responseForwarding": { "flushInterval": "100ms" diff --git a/integration/testdata/rawdata-crd-label-selector.json b/integration/testdata/rawdata-crd-label-selector.json index 63cfd1a4c..8ff23a25a 100644 --- a/integration/testdata/rawdata-crd-label-selector.json +++ b/integration/testdata/rawdata-crd-label-selector.json @@ -58,6 +58,7 @@ "url": "http://10.42.0.5:80" } ], + "strategy": "wrr", "passHostHeader": true, "responseForwarding": { "flushInterval": "100ms" diff --git a/integration/testdata/rawdata-crd.json b/integration/testdata/rawdata-crd.json index 580ca3622..334b6c698 100644 --- a/integration/testdata/rawdata-crd.json +++ b/integration/testdata/rawdata-crd.json @@ -172,6 +172,7 @@ "url": "http://10.42.0.5:80" } ], + "strategy": "wrr", "passHostHeader": true, "responseForwarding": { "flushInterval": "100ms" @@ -196,6 +197,7 @@ "url": "http://10.42.0.5:80" } ], + "strategy": "wrr", "passHostHeader": true, "responseForwarding": { "flushInterval": "100ms" @@ -220,6 +222,7 @@ "url": "http://10.42.0.5:80" } ], + "strategy": "wrr", "passHostHeader": true, "responseForwarding": { "flushInterval": "100ms" @@ -245,6 +248,7 @@ "url": "http://10.42.0.5:80" } ], + "strategy": "wrr", "passHostHeader": true, "responseForwarding": { "flushInterval": "100ms" diff --git a/integration/testdata/rawdata-etcd.json b/integration/testdata/rawdata-etcd.json index 8aa27be8d..b8097ad24 100644 --- a/integration/testdata/rawdata-etcd.json +++ b/integration/testdata/rawdata-etcd.json @@ -200,6 +200,7 @@ "url": "http://10.0.1.1:8889" } ], + "strategy": "wrr", "passHostHeader": true, "responseForwarding": { "flushInterval": "100ms" @@ -225,6 +226,7 @@ "url": "http://10.0.1.2:8889" } ], + "strategy": "wrr", "passHostHeader": true, "responseForwarding": { "flushInterval": "100ms" @@ -242,6 +244,7 @@ "url": "http://10.0.1.3:8889" } ], + "strategy": "wrr", "passHostHeader": true, "responseForwarding": { "flushInterval": "100ms" diff --git a/integration/testdata/rawdata-gateway.json b/integration/testdata/rawdata-gateway.json index 7fcce3680..343e5e597 100644 --- a/integration/testdata/rawdata-gateway.json +++ b/integration/testdata/rawdata-gateway.json @@ -126,6 +126,7 @@ "url": "http://10.42.0.6:80" } ], + "strategy": "wrr", "passHostHeader": true, "responseForwarding": { "flushInterval": "100ms" diff --git a/integration/testdata/rawdata-ingress-label-selector.json b/integration/testdata/rawdata-ingress-label-selector.json index 0ec80080d..3b4203860 100644 --- a/integration/testdata/rawdata-ingress-label-selector.json +++ b/integration/testdata/rawdata-ingress-label-selector.json @@ -106,6 +106,7 @@ "url": "http://10.42.0.5:80" } ], + "strategy": "wrr", "passHostHeader": true, "responseForwarding": { "flushInterval": "100ms" diff --git a/integration/testdata/rawdata-ingress.json b/integration/testdata/rawdata-ingress.json index 115e3530d..a53ecd9cc 100644 --- a/integration/testdata/rawdata-ingress.json +++ b/integration/testdata/rawdata-ingress.json @@ -157,6 +157,7 @@ "url": "http://10.42.0.5:80" } ], + "strategy": "wrr", "passHostHeader": true, "responseForwarding": { "flushInterval": "100ms" @@ -182,6 +183,7 @@ "url": "http://10.42.0.5:80" } ], + "strategy": "wrr", "passHostHeader": true, "responseForwarding": { "flushInterval": "100ms" diff --git a/integration/testdata/rawdata-ingressclass.json b/integration/testdata/rawdata-ingressclass.json index dd7d8de68..6c28ccd53 100644 --- a/integration/testdata/rawdata-ingressclass.json +++ b/integration/testdata/rawdata-ingressclass.json @@ -106,6 +106,7 @@ "url": "http://10.42.0.5:80" } ], + "strategy": "wrr", "passHostHeader": true, "responseForwarding": { "flushInterval": "100ms" diff --git a/integration/testdata/rawdata-redis.json b/integration/testdata/rawdata-redis.json index ef34971d1..08cdac871 100644 --- a/integration/testdata/rawdata-redis.json +++ b/integration/testdata/rawdata-redis.json @@ -200,6 +200,7 @@ "url": "http://10.0.1.1:8889" } ], + "strategy": "wrr", "passHostHeader": true, "responseForwarding": { "flushInterval": "100ms" @@ -225,6 +226,7 @@ "url": "http://10.0.1.2:8889" } ], + "strategy": "wrr", "passHostHeader": true, "responseForwarding": { "flushInterval": "100ms" @@ -242,6 +244,7 @@ "url": "http://10.0.1.3:8889" } ], + "strategy": "wrr", "passHostHeader": true, "responseForwarding": { "flushInterval": "100ms" diff --git a/integration/testdata/rawdata-zk.json b/integration/testdata/rawdata-zk.json index 064036472..c4754c215 100644 --- a/integration/testdata/rawdata-zk.json +++ b/integration/testdata/rawdata-zk.json @@ -200,6 +200,7 @@ "url": "http://10.0.1.1:8889" } ], + "strategy": "wrr", "passHostHeader": true, "responseForwarding": { "flushInterval": "100ms" @@ -225,6 +226,7 @@ "url": "http://10.0.1.2:8889" } ], + "strategy": "wrr", "passHostHeader": true, "responseForwarding": { "flushInterval": "100ms" @@ -242,6 +244,7 @@ "url": "http://10.0.1.3:8889" } ], + "strategy": "wrr", "passHostHeader": true, "responseForwarding": { "flushInterval": "100ms" diff --git a/pkg/config/dynamic/http_config.go b/pkg/config/dynamic/http_config.go index 571fe501f..deb2abeda 100644 --- a/pkg/config/dynamic/http_config.go +++ b/pkg/config/dynamic/http_config.go @@ -211,12 +211,22 @@ func (c *Cookie) SetDefaults() { c.Path = &defaultPath } +type BalancerStrategy string + +const ( + // BalancerStrategyWRR is the weighted round-robin strategy. + BalancerStrategyWRR BalancerStrategy = "wrr" + // BalancerStrategyP2C is the power of two choices strategy. + BalancerStrategyP2C BalancerStrategy = "p2c" +) + // +k8s:deepcopy-gen=true // ServersLoadBalancer holds the ServersLoadBalancer configuration. type ServersLoadBalancer struct { - Sticky *Sticky `json:"sticky,omitempty" toml:"sticky,omitempty" yaml:"sticky,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"` - Servers []Server `json:"servers,omitempty" toml:"servers,omitempty" yaml:"servers,omitempty" label-slice-as-struct:"server" export:"true"` + Sticky *Sticky `json:"sticky,omitempty" toml:"sticky,omitempty" yaml:"sticky,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"` + Servers []Server `json:"servers,omitempty" toml:"servers,omitempty" yaml:"servers,omitempty" label-slice-as-struct:"server" export:"true"` + Strategy BalancerStrategy `json:"strategy,omitempty" toml:"strategy,omitempty" yaml:"strategy,omitempty" export:"true"` // HealthCheck enables regular active checks of the responsiveness of the // children servers of this load-balancer. To propagate status changes (e.g. all // servers of this service are down) upwards, HealthCheck must also be enabled on @@ -249,6 +259,7 @@ func (l *ServersLoadBalancer) SetDefaults() { defaultPassHostHeader := DefaultPassHostHeader l.PassHostHeader = &defaultPassHostHeader + l.Strategy = BalancerStrategyWRR l.ResponseForwarding = &ResponseForwarding{} l.ResponseForwarding.SetDefaults() } diff --git a/pkg/config/label/label_test.go b/pkg/config/label/label_test.go index 8c3e883b7..bd763ba08 100644 --- a/pkg/config/label/label_test.go +++ b/pkg/config/label/label_test.go @@ -172,6 +172,7 @@ func TestDecodeConfiguration(t *testing.T) { "traefik.http.services.Service0.loadbalancer.healthcheck.followredirects": "true", "traefik.http.services.Service0.loadbalancer.passhostheader": "true", "traefik.http.services.Service0.loadbalancer.responseforwarding.flushinterval": "1s", + "traefik.http.services.Service0.loadbalancer.strategy": "foobar", "traefik.http.services.Service0.loadbalancer.server.url": "foobar", "traefik.http.services.Service0.loadbalancer.server.preservepath": "true", "traefik.http.services.Service0.loadbalancer.server.scheme": "foobar", @@ -195,6 +196,7 @@ func TestDecodeConfiguration(t *testing.T) { "traefik.http.services.Service1.loadbalancer.healthcheck.followredirects": "true", "traefik.http.services.Service1.loadbalancer.passhostheader": "true", "traefik.http.services.Service1.loadbalancer.responseforwarding.flushinterval": "1s", + "traefik.http.services.Service1.loadbalancer.strategy": "foobar", "traefik.http.services.Service1.loadbalancer.server.url": "foobar", "traefik.http.services.Service1.loadbalancer.server.preservepath": "true", "traefik.http.services.Service1.loadbalancer.server.scheme": "foobar", @@ -680,6 +682,7 @@ func TestDecodeConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Service0": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: "foobar", Sticky: &dynamic.Sticky{ Cookie: &dynamic.Cookie{ Name: "foobar", @@ -722,6 +725,7 @@ func TestDecodeConfiguration(t *testing.T) { }, "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: "foobar", Servers: []dynamic.Server{ { URL: "foobar", @@ -1222,6 +1226,7 @@ func TestEncodeConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Service0": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: "foobar", Sticky: &dynamic.Sticky{ Cookie: &dynamic.Cookie{ Name: "foobar", @@ -1261,6 +1266,7 @@ func TestEncodeConfiguration(t *testing.T) { }, "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: "foobar", Servers: []dynamic.Server{ { URL: "foobar", @@ -1473,6 +1479,7 @@ func TestEncodeConfiguration(t *testing.T) { "traefik.HTTP.Services.Service0.LoadBalancer.HealthCheck.Timeout": "1000000000", "traefik.HTTP.Services.Service0.LoadBalancer.PassHostHeader": "true", "traefik.HTTP.Services.Service0.LoadBalancer.ResponseForwarding.FlushInterval": "1000000000", + "traefik.HTTP.Services.Service0.LoadBalancer.Strategy": "foobar", "traefik.HTTP.Services.Service0.LoadBalancer.server.URL": "foobar", "traefik.HTTP.Services.Service0.LoadBalancer.server.PreservePath": "true", "traefik.HTTP.Services.Service0.LoadBalancer.server.Port": "8080", @@ -1496,6 +1503,7 @@ func TestEncodeConfiguration(t *testing.T) { "traefik.HTTP.Services.Service1.LoadBalancer.HealthCheck.Timeout": "1000000000", "traefik.HTTP.Services.Service1.LoadBalancer.PassHostHeader": "true", "traefik.HTTP.Services.Service1.LoadBalancer.ResponseForwarding.FlushInterval": "1000000000", + "traefik.HTTP.Services.Service1.LoadBalancer.Strategy": "foobar", "traefik.HTTP.Services.Service1.LoadBalancer.server.URL": "foobar", "traefik.HTTP.Services.Service1.LoadBalancer.server.PreservePath": "true", "traefik.HTTP.Services.Service1.LoadBalancer.server.Port": "8080", diff --git a/pkg/provider/consulcatalog/config_test.go b/pkg/provider/consulcatalog/config_test.go index 89e40db43..227dd4838 100644 --- a/pkg/provider/consulcatalog/config_test.go +++ b/pkg/provider/consulcatalog/config_test.go @@ -61,6 +61,7 @@ func TestDefaultRule(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -119,6 +120,7 @@ func TestDefaultRule(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -169,6 +171,7 @@ func TestDefaultRule(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -219,6 +222,7 @@ func TestDefaultRule(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -275,6 +279,7 @@ func TestDefaultRule(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -369,6 +374,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "dev-Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -430,6 +436,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "dev-Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "https://127.0.0.1:443", @@ -521,6 +528,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "dev-Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "https://127.0.0.1:443", @@ -609,6 +617,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -622,6 +631,7 @@ func Test_buildConfiguration(t *testing.T) { }, "Test2": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.2:80", @@ -686,6 +696,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -753,6 +764,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.2:80", @@ -817,6 +829,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -876,6 +889,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -933,6 +947,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -982,6 +997,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -1044,6 +1060,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -1096,6 +1113,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -1109,6 +1127,7 @@ func Test_buildConfiguration(t *testing.T) { }, "Service2": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -1288,6 +1307,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -1346,6 +1366,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -1424,6 +1445,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -1492,6 +1514,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -1573,6 +1596,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -1640,6 +1664,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -1715,6 +1740,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -1788,6 +1814,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -1847,6 +1874,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -1904,6 +1932,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "h2c://127.0.0.1:8080", @@ -1960,6 +1989,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://1.2.3.4:5678", @@ -2017,6 +2047,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://1.2.3.4:5678", @@ -2143,6 +2174,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -2156,6 +2188,7 @@ func Test_buildConfiguration(t *testing.T) { }, "Service2": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:8080", @@ -2389,6 +2422,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -2456,6 +2490,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -2889,6 +2924,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -2980,6 +3016,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -3201,6 +3238,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "https://127.0.0.1:80", @@ -3215,6 +3253,7 @@ func Test_buildConfiguration(t *testing.T) { }, "Test-97077516270503695": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "https://127.0.0.2:80", @@ -3501,6 +3540,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "dev-Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -3677,6 +3717,7 @@ func TestFilterHealthStatuses(t *testing.T) { Services: map[string]*dynamic.Service{ "Test1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -3690,6 +3731,7 @@ func TestFilterHealthStatuses(t *testing.T) { }, "Test2": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:81", @@ -3793,6 +3835,7 @@ func TestFilterHealthStatuses(t *testing.T) { Services: map[string]*dynamic.Service{ "Test2": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:81", @@ -3873,6 +3916,7 @@ func TestFilterHealthStatuses(t *testing.T) { Services: map[string]*dynamic.Service{ "Test1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -3886,6 +3930,7 @@ func TestFilterHealthStatuses(t *testing.T) { }, "Test2": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:81", @@ -3985,6 +4030,7 @@ func TestFilterHealthStatuses(t *testing.T) { Services: map[string]*dynamic.Service{ "Test1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -3998,6 +4044,7 @@ func TestFilterHealthStatuses(t *testing.T) { }, "Test2": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:81", @@ -4011,6 +4058,7 @@ func TestFilterHealthStatuses(t *testing.T) { }, "Test3": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:82", @@ -4024,6 +4072,7 @@ func TestFilterHealthStatuses(t *testing.T) { }, "Test4": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:83", diff --git a/pkg/provider/docker/config_test.go b/pkg/provider/docker/config_test.go index 718568c55..c203fc18a 100644 --- a/pkg/provider/docker/config_test.go +++ b/pkg/provider/docker/config_test.go @@ -69,6 +69,7 @@ func TestDynConfBuilder_DefaultRule(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -132,6 +133,7 @@ func TestDynConfBuilder_DefaultRule(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -197,6 +199,7 @@ func TestDynConfBuilder_DefaultRule(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -254,6 +257,7 @@ func TestDynConfBuilder_DefaultRule(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -311,6 +315,7 @@ func TestDynConfBuilder_DefaultRule(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -374,6 +379,7 @@ func TestDynConfBuilder_DefaultRule(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -606,6 +612,7 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -689,6 +696,7 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -702,6 +710,7 @@ func TestDynConfBuilder_build(t *testing.T) { }, "Test2": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.2:80", @@ -782,6 +791,7 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -849,6 +859,7 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -914,6 +925,7 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -971,6 +983,7 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -1041,6 +1054,7 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -1101,6 +1115,7 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -1114,6 +1129,7 @@ func TestDynConfBuilder_build(t *testing.T) { }, "Service2": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -1178,6 +1194,7 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -1421,6 +1438,7 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -1487,6 +1505,7 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -1584,6 +1603,7 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -1671,6 +1691,7 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -1777,6 +1798,7 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -1861,6 +1883,7 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -1961,6 +1984,7 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -2050,6 +2074,7 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -2129,6 +2154,7 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -2142,6 +2168,7 @@ func TestDynConfBuilder_build(t *testing.T) { }, "Test2": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.2:80", @@ -2206,6 +2233,7 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -2271,6 +2299,7 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "h2c://127.0.0.1:8080", @@ -2330,6 +2359,7 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -2343,6 +2373,7 @@ func TestDynConfBuilder_build(t *testing.T) { }, "Service2": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:8080", @@ -2407,6 +2438,7 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://1.2.3.4:5678", @@ -2472,6 +2504,7 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://1.2.3.4:5678", @@ -2772,6 +2805,7 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -3032,6 +3066,7 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -3107,6 +3142,7 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -3578,6 +3614,7 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -3762,6 +3799,7 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://192.168.0.1:8081", @@ -3825,6 +3863,7 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:79", diff --git a/pkg/provider/ecs/config_test.go b/pkg/provider/ecs/config_test.go index 8c2a735ee..4e679d574 100644 --- a/pkg/provider/ecs/config_test.go +++ b/pkg/provider/ecs/config_test.go @@ -63,6 +63,7 @@ func TestDefaultRule(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.0.0.1:1337", @@ -121,6 +122,7 @@ func TestDefaultRule(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -181,6 +183,7 @@ func TestDefaultRule(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -233,6 +236,7 @@ func TestDefaultRule(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -285,6 +289,7 @@ func TestDefaultRule(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -343,6 +348,7 @@ func TestDefaultRule(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -552,6 +558,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -625,6 +632,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -638,6 +646,7 @@ func Test_buildConfiguration(t *testing.T) { }, "Test2": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.2:80", @@ -708,6 +717,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -770,6 +780,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -830,6 +841,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -882,6 +894,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -947,6 +960,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -1002,6 +1016,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -1015,6 +1030,7 @@ func Test_buildConfiguration(t *testing.T) { }, "Service2": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -1074,6 +1090,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -1282,6 +1299,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -1343,6 +1361,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -1430,6 +1449,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -1507,6 +1527,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -1598,6 +1619,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -1672,6 +1694,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -1757,6 +1780,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -1837,6 +1861,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -1906,6 +1931,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -1919,6 +1945,7 @@ func Test_buildConfiguration(t *testing.T) { }, "Test2": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.2:80", @@ -1978,6 +2005,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -2038,6 +2066,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "h2c://127.0.0.1:8080", @@ -2097,6 +2126,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://1.2.3.4:5678", @@ -2157,6 +2187,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://1.2.3.4:5678", @@ -2298,6 +2329,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "h2c://127.0.0.1:8040", @@ -2366,6 +2398,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:32124", @@ -2379,6 +2412,7 @@ func Test_buildConfiguration(t *testing.T) { }, "Service2": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:32123", @@ -2433,6 +2467,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -2446,6 +2481,7 @@ func Test_buildConfiguration(t *testing.T) { }, "Service2": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:8080", @@ -2736,6 +2772,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -2806,6 +2843,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -3237,6 +3275,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -3400,6 +3439,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", diff --git a/pkg/provider/kubernetes/crd/kubernetes_http.go b/pkg/provider/kubernetes/crd/kubernetes_http.go index 2f7afe328..a2d89dd42 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_http.go +++ b/pkg/provider/kubernetes/crd/kubernetes_http.go @@ -21,9 +21,8 @@ import ( ) const ( - roundRobinStrategy = "RoundRobin" - httpsProtocol = "https" - httpProtocol = "http" + httpsProtocol = "https" + httpProtocol = "http" ) func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Client, tlsConfigs map[string]*tls.CertAndStores) *dynamic.HTTPConfiguration { @@ -322,13 +321,33 @@ func (c configBuilder) buildMirroring(ctx context.Context, tService *traefikv1al // buildServersLB creates the configuration for the load-balancer of servers defined by svc. func (c configBuilder) buildServersLB(namespace string, svc traefikv1alpha1.LoadBalancerSpec) (*dynamic.Service, error) { + lb := &dynamic.ServersLoadBalancer{} + lb.SetDefaults() + + // This is required by the tests as the fake client does not apply default values. + // TODO: remove this when the fake client apply default values. + if svc.Strategy != "" { + switch svc.Strategy { + case dynamic.BalancerStrategyWRR, dynamic.BalancerStrategyP2C: + lb.Strategy = svc.Strategy + + // Here we are just logging a warning as the default value is already applied. + case "RoundRobin": + log.Warn(). + Str("namespace", namespace). + Str("service", svc.Name). + Msgf("RoundRobin strategy value is deprecated, please use %s value instead", dynamic.BalancerStrategyWRR) + + default: + return nil, fmt.Errorf("load-balancer strategy %s is not supported", svc.Strategy) + } + } + servers, err := c.loadServers(namespace, svc) if err != nil { return nil, err } - lb := &dynamic.ServersLoadBalancer{} - lb.SetDefaults() lb.Servers = servers if svc.HealthCheck != nil { @@ -421,14 +440,6 @@ func (c configBuilder) makeServersTransportKey(parentNamespace string, serversTr } func (c configBuilder) loadServers(parentNamespace string, svc traefikv1alpha1.LoadBalancerSpec) ([]dynamic.Server, error) { - strategy := svc.Strategy - if strategy == "" { - strategy = roundRobinStrategy - } - if strategy != roundRobinStrategy { - return nil, fmt.Errorf("load balancing strategy %s is not supported", strategy) - } - namespace := namespaceOrFallback(svc, parentNamespace) if !isNamespaceAllowed(c.allowCrossNamespace, parentNamespace, namespace) { diff --git a/pkg/provider/kubernetes/crd/kubernetes_test.go b/pkg/provider/kubernetes/crd/kubernetes_test.go index 70b4779dc..0f7e86d03 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_test.go +++ b/pkg/provider/kubernetes/crd/kubernetes_test.go @@ -1387,6 +1387,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) { Services: map[string]*dynamic.Service{ "default-whoami-ipv6-8080": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://[2001:db8:85a3:8d3:1319:8a2e:370:7348]:8080", @@ -1400,6 +1401,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) { }, "default-external-svc-with-ipv6-8080": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://[2001:db8:85a3:8d3:1319:8a2e:370:7347]:8080", @@ -1743,6 +1745,7 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.Service{ "default-test-route-6b204d94623b3df4370c": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -1815,6 +1818,7 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.Service{ "default-test2-route-23c7f4c450289ee29016": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -1893,6 +1897,7 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.Service{ "default-test2-route-3c9bf014491ebdba74f7": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -1948,6 +1953,7 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.Service{ "default-test2-route-23c7f4c450289ee29016": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -2009,6 +2015,7 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.Service{ "default-test2-route-23c7f4c450289ee29016": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -2061,6 +2068,7 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.Service{ "default-test-route-6b204d94623b3df4370c": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -2077,6 +2085,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, "default-test-route-77c62dfe9517144aeeaa": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -2139,6 +2148,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, "default-whoami-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -2155,6 +2165,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, "default-whoami2-8080": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.3:8080", @@ -2212,6 +2223,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, "default-whoami5-8080": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.3:8080", @@ -2262,6 +2274,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, "default-whoami5-8080": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.3:8080", @@ -2337,6 +2350,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, "default-whoami4-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -2353,6 +2367,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, "default-whoami5-8080": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.3:8080", @@ -2383,6 +2398,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, "default-whoami6-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.5:80", @@ -2399,6 +2415,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, "default-whoami7-8080": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.7:8080", @@ -2470,6 +2487,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, "default-whoami5-8080": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.3:8080", @@ -2553,6 +2571,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, "default-whoami4-8080": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:8080", @@ -2569,6 +2588,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, "default-whoami5-8080": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.3:8080", @@ -2615,6 +2635,7 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.Service{ "default-test-route-77c62dfe9517144aeeaa": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "https://external.domain:443", @@ -2679,6 +2700,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, "default-external-svc-443": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "https://external.domain:443", @@ -2698,6 +2720,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, "default-external-svc-with-https-443": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "https://external.domain:443", @@ -2741,6 +2764,7 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.Service{ "default-external-svc-443": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "https://external.domain:443", @@ -2819,6 +2843,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, "baz-whoami6-8080": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.5:8080", @@ -2857,6 +2882,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, "foo-whoami4-8080": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:8080", @@ -2883,6 +2909,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, "foo-whoami5-8080": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.3:8080", @@ -2989,6 +3016,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, "default-whoami4-8080": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:8080", @@ -3005,6 +3033,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, "default-whoami5-8080": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.3:8080", @@ -3080,6 +3109,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, "default-whoami4-8080": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:8080", @@ -3096,6 +3126,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, "default-whoami5-8080": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.3:8080", @@ -3157,6 +3188,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, "default-whoami-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -3173,6 +3205,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, "default-whoami2-8080": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.3:8080", @@ -3267,6 +3300,7 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.Service{ "default-test-route-02719a68b11e915a4b23": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -3324,6 +3358,7 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.Service{ "default-test-route-6b204d94623b3df4370c": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -3397,6 +3432,7 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.Service{ "default-test-route-6b204d94623b3df4370c": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -3458,6 +3494,7 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.Service{ "default-test-route-6b204d94623b3df4370c": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -3474,6 +3511,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, "default-test-route-default-6b204d94623b3df4370c": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -3547,6 +3585,7 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.Service{ "default-test-route-6b204d94623b3df4370c": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -3621,6 +3660,7 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.Service{ "default-test-route-6b204d94623b3df4370c": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -3693,6 +3733,7 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.Service{ "default-test-route-6b204d94623b3df4370c": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -3769,6 +3810,7 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.Service{ "default-test-route-6b204d94623b3df4370c": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -3846,6 +3888,7 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.Service{ "default-test-route-6b204d94623b3df4370c": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -3894,6 +3937,7 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.Service{ "default-test-route-6b204d94623b3df4370c": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -3941,6 +3985,7 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.Service{ "default-test-route-6b204d94623b3df4370c": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "https://10.10.0.5:8443", @@ -3988,6 +4033,7 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.Service{ "default-test-route-6b204d94623b3df4370c": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "https://10.10.0.7:8443", @@ -4250,6 +4296,7 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.Service{ "default-errorpage-errorpage-service": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -4296,6 +4343,7 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.Service{ "default-test-route-6b204d94623b3df4370c": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -4352,6 +4400,7 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.Service{ "default-test-route-6b204d94623b3df4370c": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -4413,6 +4462,7 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.Service{ "default-test-route-6b204d94623b3df4370c": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -4470,6 +4520,7 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.Service{ "default-test-route-6b204d94623b3df4370c": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -4486,6 +4537,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, "default-test-route-default-6b204d94623b3df4370c": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -4534,6 +4586,7 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.Service{ "default-test-route-6f97418635c7e18853da": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://external.domain:80", @@ -4577,6 +4630,7 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.Service{ "default-test-route-6f97418635c7e18853da": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://external.domain:80", @@ -4620,6 +4674,7 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.Service{ "default-test-route-6f97418635c7e18853da": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "https://external.domain:443", @@ -4723,6 +4778,7 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.Service{ "default-external-svc-with-https-443": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "https://external.domain:443", @@ -4737,6 +4793,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, "default-whoamitls-443": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "https://10.10.0.5:8443", @@ -4822,6 +4879,7 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.Service{ "default-test-route-6b204d94623b3df4370c": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -4862,6 +4920,7 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.Service{ "default-test-route-6b204d94623b3df4370c": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -4918,6 +4977,7 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.Service{ "default-test-route-6b204d94623b3df4370c": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:8080", @@ -4993,6 +5053,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, "default-test-errorpage-errorpage-service": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -5024,6 +5085,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, "default-whoami-without-endpointslice-endpoints-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -5136,6 +5198,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, "default-whoami2-8080": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Sticky: &dynamic.Sticky{ Cookie: &dynamic.Cookie{ Name: "cookie", @@ -5162,6 +5225,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, "default-whoami-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Sticky: &dynamic.Sticky{ Cookie: &dynamic.Cookie{ Name: "cookie", @@ -5188,6 +5252,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, "default-whoami3-8443": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.7:8443", @@ -5273,6 +5338,7 @@ func TestLoadIngressRoutes_multipleEndpointAddresses(t *testing.T) { Services: map[string]*dynamic.Service{ "default-test-route-6b204d94623b3df4370c": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -6199,6 +6265,7 @@ func TestCrossNamespace(t *testing.T) { Services: map[string]*dynamic.Service{ "default-test-crossnamespace-route-9313b71dbe6a649d5049": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -6277,6 +6344,7 @@ func TestCrossNamespace(t *testing.T) { Services: map[string]*dynamic.Service{ "default-test-crossnamespace-route-6b204d94623b3df4370c": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -6293,6 +6361,7 @@ func TestCrossNamespace(t *testing.T) { }, "default-test-crossnamespace-route-9313b71dbe6a649d5049": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -6309,6 +6378,7 @@ func TestCrossNamespace(t *testing.T) { }, "default-test-errorpage-errorpage-service": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -6325,6 +6395,7 @@ func TestCrossNamespace(t *testing.T) { }, "default-test-crossnamespace-route-a1963878aac7331b7950": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -6404,6 +6475,7 @@ func TestCrossNamespace(t *testing.T) { }, "default-cross-ns-route-1bc3efa892379bb93c6e": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -6421,6 +6493,7 @@ func TestCrossNamespace(t *testing.T) { }, "cross-ns-whoami-svc-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -6479,6 +6552,7 @@ func TestCrossNamespace(t *testing.T) { }, "default-whoami-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -6529,6 +6603,7 @@ func TestCrossNamespace(t *testing.T) { }, "cross-ns-whoami-svc-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -6556,6 +6631,7 @@ func TestCrossNamespace(t *testing.T) { }, "default-whoami-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -6604,6 +6680,7 @@ func TestCrossNamespace(t *testing.T) { Services: map[string]*dynamic.Service{ "default-test-route-6b204d94623b3df4370c": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -6701,6 +6778,7 @@ func TestCrossNamespace(t *testing.T) { Services: map[string]*dynamic.Service{ "default-test-route-6b204d94623b3df4370c": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -6764,6 +6842,7 @@ func TestCrossNamespace(t *testing.T) { Services: map[string]*dynamic.Service{ "default-test-route-6b204d94623b3df4370c": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -7382,6 +7461,7 @@ func TestExternalNameService(t *testing.T) { Services: map[string]*dynamic.Service{ "default-test-route-6f97418635c7e18853da": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://external.domain:80", @@ -7649,6 +7729,7 @@ func TestNativeLB(t *testing.T) { Services: map[string]*dynamic.Service{ "default-test-route-6f97418635c7e18853da": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, ResponseForwarding: &dynamic.ResponseForwarding{FlushInterval: dynamic.DefaultFlushInterval}, Servers: []dynamic.Server{ { @@ -7830,6 +7911,7 @@ func TestNodePortLB(t *testing.T) { Services: map[string]*dynamic.Service{ "default-test-route-6f97418635c7e18853da": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, ResponseForwarding: &dynamic.ResponseForwarding{FlushInterval: dynamic.DefaultFlushInterval}, Servers: []dynamic.Server{ { @@ -8273,6 +8355,7 @@ func TestGlobalNativeLB(t *testing.T) { Services: map[string]*dynamic.Service{ "default-global-native-lb-6f97418635c7e18853da": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, ResponseForwarding: &dynamic.ResponseForwarding{FlushInterval: dynamic.DefaultFlushInterval}, Servers: []dynamic.Server{ { @@ -8315,6 +8398,7 @@ func TestGlobalNativeLB(t *testing.T) { Services: map[string]*dynamic.Service{ "default-test-route-6f97418635c7e18853da": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, ResponseForwarding: &dynamic.ResponseForwarding{FlushInterval: dynamic.DefaultFlushInterval}, Servers: []dynamic.Server{ { diff --git a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressroute.go b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressroute.go index 164d1d39d..3fa3e5ef8 100644 --- a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressroute.go +++ b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressroute.go @@ -113,9 +113,11 @@ type LoadBalancerSpec struct { // It defaults to https when Kubernetes Service port is 443, http otherwise. Scheme string `json:"scheme,omitempty"` // Strategy defines the load balancing strategy between the servers. - // RoundRobin is the only supported value at the moment. - // +kubebuilder:validation:Enum=RoundRobin - Strategy string `json:"strategy,omitempty"` + // Supported values are: wrr (Weighed round-robin) and p2c (Power of two choices). + // RoundRobin value is deprecated and supported for backward compatibility. + // +kubebuilder:validation:Enum=wrr;p2c;RoundRobin + // +kubebuilder:default:=wrr + Strategy dynamic.BalancerStrategy `json:"strategy,omitempty"` // PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. // By default, passHostHeader is true. PassHostHeader *bool `json:"passHostHeader,omitempty"` diff --git a/pkg/provider/kubernetes/gateway/kubernetes_test.go b/pkg/provider/kubernetes/gateway/kubernetes_test.go index 183fe4dae..16d7a2bf2 100644 --- a/pkg/provider/kubernetes/gateway/kubernetes_test.go +++ b/pkg/provider/kubernetes/gateway/kubernetes_test.go @@ -637,6 +637,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, "default-whoami-http-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -733,6 +734,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, "default-whoami-http-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -803,6 +805,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, "default-whoami-http-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -864,6 +867,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, "default-whoami-http-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -925,6 +929,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, "default-whoami-http-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -1003,6 +1008,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, "default-whoami-http-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -1019,6 +1025,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, "default-whoami2-http-8080": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.3:8080", @@ -1084,6 +1091,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, "default-whoami-http-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -1100,6 +1108,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, "default-whoami2-http-8080": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.3:8080", @@ -1184,6 +1193,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, "default-whoami-http-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -1277,6 +1287,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, "default-whoami-http-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -1381,6 +1392,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, "default-whoami-http-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -1442,6 +1454,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, "default-whoami-http-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -1503,6 +1516,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, "default-whoami-http-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -1564,6 +1578,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, "default-whoami-http-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -1642,6 +1657,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, "default-whoami-http-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -1658,6 +1674,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, "bar-whoami-bar-http-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.11:80", @@ -1719,6 +1736,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, "bar-whoami-bar-http-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.11:80", @@ -1789,6 +1807,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, "default-whoami-http-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -1859,6 +1878,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, "default-whoami-http-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -2021,6 +2041,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, "default-whoami-http-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -2089,6 +2110,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, "default-whoami-http-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -2159,6 +2181,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, "default-whoami-http-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -2220,6 +2243,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, "default-whoami-http-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -2282,6 +2306,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, "default-whoami-http-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -2353,6 +2378,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, "default-whoami-http-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -2420,6 +2446,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, "default-whoami-http-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.10.1:80", @@ -2478,6 +2505,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, "default-whoami-native-http-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.10.1:80", @@ -2597,7 +2625,7 @@ func TestLoadHTTPRoutes_backendExtensionRef(t *testing.T) { paths: []string{"services.yml", "httproute/simple_with_TraefikService.yml"}, groupKindBackendFuncs: map[string]map[string]BuildBackendFunc{ traefikv1alpha1.GroupName: {"TraefikService": func(name, namespace string) (string, *dynamic.Service, error) { - return name, &dynamic.Service{LoadBalancer: &dynamic.ServersLoadBalancer{Servers: []dynamic.Server{{URL: "foobar"}}}}, nil + return name, &dynamic.Service{LoadBalancer: &dynamic.ServersLoadBalancer{Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{{URL: "foobar"}}}}, nil }}, }, entryPoints: map[string]Entrypoint{"web": { @@ -2638,6 +2666,7 @@ func TestLoadHTTPRoutes_backendExtensionRef(t *testing.T) { }, "whoami": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ {URL: "foobar"}, }, @@ -2797,6 +2826,7 @@ func TestLoadHTTPRoutes_backendExtensionRef(t *testing.T) { }, "default-whoami-http-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -2872,6 +2902,7 @@ func TestLoadHTTPRoutes_backendExtensionRef(t *testing.T) { }, "default-whoami-h2c-http-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "h2c://10.10.0.13:80", @@ -2885,6 +2916,7 @@ func TestLoadHTTPRoutes_backendExtensionRef(t *testing.T) { }, "default-whoami-ws-http-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.14:80", @@ -2898,6 +2930,7 @@ func TestLoadHTTPRoutes_backendExtensionRef(t *testing.T) { }, "default-whoami-wss-http-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "https://10.10.0.15:80", @@ -3012,6 +3045,7 @@ func TestLoadHTTPRoutes_filterExtensionRef(t *testing.T) { }, "default-whoami-http-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -3084,6 +3118,7 @@ func TestLoadHTTPRoutes_filterExtensionRef(t *testing.T) { }, "default-whoami-http-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -3296,6 +3331,7 @@ func TestLoadGRPCRoutes_filterExtensionRef(t *testing.T) { }, "default-whoami-80-grpc": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "h2c://10.10.0.1:80", @@ -3368,6 +3404,7 @@ func TestLoadGRPCRoutes_filterExtensionRef(t *testing.T) { }, "default-whoami-80-grpc": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "h2c://10.10.0.1:80", @@ -5951,6 +5988,7 @@ func TestLoadMixedRoutes(t *testing.T) { }, "default-whoami-http-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -6139,6 +6177,7 @@ func TestLoadMixedRoutes(t *testing.T) { }, "default-whoami-http-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -6359,6 +6398,7 @@ func TestLoadMixedRoutes(t *testing.T) { }, "default-whoami-http-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", @@ -6375,6 +6415,7 @@ func TestLoadMixedRoutes(t *testing.T) { }, "bar-whoami-bar-http-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.11:80", @@ -6535,6 +6576,7 @@ func TestLoadMixedRoutes(t *testing.T) { Services: map[string]*dynamic.Service{ "bar-whoami-bar-http-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.11:80", @@ -6694,6 +6736,7 @@ func TestLoadMixedRoutes(t *testing.T) { }, "default-whoami-http-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://10.10.0.1:80", diff --git a/pkg/provider/kubernetes/ingress/kubernetes_test.go b/pkg/provider/kubernetes/ingress/kubernetes_test.go index f8bf37037..16304b42c 100644 --- a/pkg/provider/kubernetes/ingress/kubernetes_test.go +++ b/pkg/provider/kubernetes/ingress/kubernetes_test.go @@ -73,6 +73,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-service1-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -128,6 +129,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-service1-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -173,6 +175,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-service1-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -209,6 +212,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-service1-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -245,6 +249,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-service1-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -281,6 +286,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-service1-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -313,6 +319,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-service1-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -345,6 +352,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-example-com-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -378,6 +386,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-service1-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -414,6 +423,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-service1-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -450,6 +460,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-service1-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -466,6 +477,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { }, "testing-service2-8082": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -499,6 +511,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-service1-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -535,6 +548,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Services: map[string]*dynamic.Service{ "default-backend": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -567,6 +581,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-service1-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -599,6 +614,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-service1-tchouk": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -631,6 +647,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-service1-tchouk": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -667,6 +684,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-service1-tchouk": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -683,6 +701,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { }, "testing-service1-carotte": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -715,6 +734,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-service1-tchouk": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -751,6 +771,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-service1-tchouk": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -767,6 +788,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { }, "toto-service1-tchouk": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -819,6 +841,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-service1-8080": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -849,6 +872,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-example-com-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -888,6 +912,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-service1-443": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -920,6 +945,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-service1-8443": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -953,6 +979,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-service1-8443": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -987,6 +1014,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Services: map[string]*dynamic.Service{ "default-backend": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -1019,6 +1047,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-service1-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -1091,6 +1120,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-service1-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -1123,6 +1153,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-service1-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -1158,6 +1189,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-service1-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -1188,6 +1220,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-service1-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -1217,6 +1250,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-service1-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -1246,6 +1280,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-service1-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -1275,6 +1310,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-service1-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -1304,6 +1340,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-service1-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -1333,6 +1370,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-service1-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -1365,6 +1403,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-service1-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -1397,6 +1436,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-service1-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -1426,6 +1466,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-service1-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -1481,6 +1522,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-service1-foobar": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -1522,6 +1564,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Services: map[string]*dynamic.Service{ "default-backend": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -1551,6 +1594,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-service1-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -1630,6 +1674,7 @@ func TestLoadConfigurationFromIngressesWithExternalNameServices(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-service1-8080": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ FlushInterval: ptypes.Duration(100 * time.Millisecond), @@ -1659,6 +1704,7 @@ func TestLoadConfigurationFromIngressesWithExternalNameServices(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-service-bar-8080": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://[2001:0db8:3c4d:0015:0000:0000:1a2f:1a2b]:8080", @@ -1689,6 +1735,7 @@ func TestLoadConfigurationFromIngressesWithExternalNameServices(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-service-foo-8080": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://[2001:0db8:3c4d:0015:0000:0000:1a2f:2a3b]:8080", @@ -1741,6 +1788,7 @@ func TestLoadConfigurationFromIngressesWithNativeLB(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-service1-8080": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, ResponseForwarding: &dynamic.ResponseForwarding{FlushInterval: dynamic.DefaultFlushInterval}, PassHostHeader: pointer(true), Servers: []dynamic.Server{ @@ -1790,6 +1838,7 @@ func TestLoadConfigurationFromIngressesWithNodePortLB(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-service1-8080": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, ResponseForwarding: &dynamic.ResponseForwarding{FlushInterval: dynamic.DefaultFlushInterval}, PassHostHeader: pointer(true), Servers: []dynamic.Server{ @@ -2027,6 +2076,7 @@ func TestLoadConfigurationFromIngressesWithNativeLBByDefault(t *testing.T) { Services: map[string]*dynamic.Service{ "testing-service1-8080": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, ResponseForwarding: &dynamic.ResponseForwarding{FlushInterval: dynamic.DefaultFlushInterval}, PassHostHeader: pointer(true), Servers: []dynamic.Server{ @@ -2054,6 +2104,7 @@ func TestLoadConfigurationFromIngressesWithNativeLBByDefault(t *testing.T) { Services: map[string]*dynamic.Service{ "default-service1-8080": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, ResponseForwarding: &dynamic.ResponseForwarding{FlushInterval: dynamic.DefaultFlushInterval}, PassHostHeader: pointer(true), Servers: []dynamic.Server{ diff --git a/pkg/provider/kv/kv_test.go b/pkg/provider/kv/kv_test.go index 1e8acdca7..b439f38ae 100644 --- a/pkg/provider/kv/kv_test.go +++ b/pkg/provider/kv/kv_test.go @@ -58,6 +58,7 @@ func Test_buildConfiguration(t *testing.T) { "traefik/http/services/Service01/loadBalancer/sticky/cookie/secure": "true", "traefik/http/services/Service01/loadBalancer/sticky/cookie/httpOnly": "true", "traefik/http/services/Service01/loadBalancer/sticky/cookie/path": "foobar", + "traefik/http/services/Service01/loadBalancer/strategy": "foobar", "traefik/http/services/Service01/loadBalancer/servers/0/url": "foobar", "traefik/http/services/Service01/loadBalancer/servers/1/url": "foobar", "traefik/http/services/Service02/mirroring/service": "foobar", @@ -646,6 +647,7 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{ "Service01": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: "foobar", Sticky: &dynamic.Sticky{ Cookie: &dynamic.Cookie{ Name: "foobar", diff --git a/pkg/provider/nomad/config_test.go b/pkg/provider/nomad/config_test.go index fb6bb0458..e74ccddb5 100644 --- a/pkg/provider/nomad/config_test.go +++ b/pkg/provider/nomad/config_test.go @@ -56,6 +56,7 @@ func Test_defaultRule(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:9999", @@ -114,6 +115,7 @@ func Test_defaultRule(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:9999", @@ -163,6 +165,7 @@ func Test_defaultRule(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:9999", @@ -218,6 +221,7 @@ func Test_defaultRule(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:9999", @@ -296,6 +300,7 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{ "dev-Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:9999", @@ -363,6 +368,7 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{ "Test1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://192.168.1.101:9999", @@ -376,6 +382,7 @@ func Test_buildConfig(t *testing.T) { }, "Test2": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://192.168.1.102:9999", @@ -440,6 +447,7 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:9999", @@ -507,6 +515,7 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.2:9999", @@ -571,6 +580,7 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:9999", @@ -630,6 +640,7 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:9999", @@ -687,6 +698,7 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:9999", @@ -772,6 +784,7 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:9999", @@ -834,6 +847,7 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:9999", @@ -886,6 +900,7 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:9999", @@ -899,6 +914,7 @@ func Test_buildConfig(t *testing.T) { }, "Service2": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:9999", @@ -1079,6 +1095,7 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:9999", @@ -1137,6 +1154,7 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:9999", @@ -1216,6 +1234,7 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:9999", @@ -1285,6 +1304,7 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:9999", @@ -1348,6 +1368,7 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:9999", @@ -1416,6 +1437,7 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:9999", @@ -1475,6 +1497,7 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:9999", @@ -1532,6 +1555,7 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "h2c://127.0.0.1:8080", @@ -1588,6 +1612,7 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://1.2.3.4:5678", @@ -1645,6 +1670,7 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://1.2.3.4:5678", @@ -1771,6 +1797,7 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:9999", @@ -1784,6 +1811,7 @@ func Test_buildConfig(t *testing.T) { }, "Service2": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:8080", @@ -1983,6 +2011,7 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:9999", @@ -2050,6 +2079,7 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:9999", @@ -2459,6 +2489,7 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:9999", @@ -2550,6 +2581,7 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{ "Service1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:9999", @@ -2770,6 +2802,7 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:80", @@ -2783,6 +2816,7 @@ func Test_buildConfig(t *testing.T) { }, "Test-1234154071633021619": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.2:80", @@ -3002,6 +3036,7 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{ "dev-Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:9999", @@ -3093,6 +3128,7 @@ func Test_buildConfigAllowEmptyServicesTrue(t *testing.T) { Services: map[string]*dynamic.Service{ "Test": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: nil, PassHostHeader: pointer(true), ResponseForwarding: &dynamic.ResponseForwarding{ diff --git a/pkg/server/router/router_test.go b/pkg/server/router/router_test.go index 70808e9c6..35a58bfb8 100644 --- a/pkg/server/router/router_test.go +++ b/pkg/server/router/router_test.go @@ -53,6 +53,7 @@ func TestRouterManager_Get(t *testing.T) { serviceConfig: map[string]*dynamic.Service{ "foo-service": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: server.URL, @@ -76,6 +77,7 @@ func TestRouterManager_Get(t *testing.T) { serviceConfig: map[string]*dynamic.Service{ "foo-service": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: server.URL, @@ -114,6 +116,7 @@ func TestRouterManager_Get(t *testing.T) { serviceConfig: map[string]*dynamic.Service{ "foo-service": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: server.URL, @@ -138,6 +141,7 @@ func TestRouterManager_Get(t *testing.T) { serviceConfig: map[string]*dynamic.Service{ "foo-service": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: server.URL, @@ -179,6 +183,7 @@ func TestRouterManager_Get(t *testing.T) { serviceConfig: map[string]*dynamic.Service{ "foo-service": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: server.URL, @@ -219,6 +224,7 @@ func TestRouterManager_Get(t *testing.T) { serviceConfig: map[string]*dynamic.Service{ "foo-service@provider-1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: server.URL, @@ -242,6 +248,7 @@ func TestRouterManager_Get(t *testing.T) { serviceConfig: map[string]*dynamic.Service{ "foo-service@provider-2": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: server.URL, @@ -266,6 +273,7 @@ func TestRouterManager_Get(t *testing.T) { serviceConfig: map[string]*dynamic.Service{ "foo-service@provider-1": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: server.URL, @@ -351,6 +359,7 @@ func TestRuntimeConfiguration(t *testing.T) { serviceConfig: map[string]*dynamic.Service{ "foo-service": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1:8085", @@ -385,6 +394,7 @@ func TestRuntimeConfiguration(t *testing.T) { serviceConfig: map[string]*dynamic.Service{ "foo-service": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1", @@ -412,6 +422,7 @@ func TestRuntimeConfiguration(t *testing.T) { serviceConfig: map[string]*dynamic.Service{ "foo-service": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1", @@ -439,6 +450,7 @@ func TestRuntimeConfiguration(t *testing.T) { serviceConfig: map[string]*dynamic.Service{ "foo-service": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1", @@ -482,6 +494,7 @@ func TestRuntimeConfiguration(t *testing.T) { serviceConfig: map[string]*dynamic.Service{ "foo-service": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1", @@ -522,6 +535,7 @@ func TestRuntimeConfiguration(t *testing.T) { serviceConfig: map[string]*dynamic.Service{ "foo-service": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1", @@ -552,6 +566,7 @@ func TestRuntimeConfiguration(t *testing.T) { serviceConfig: map[string]*dynamic.Service{ "foo-service": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1", @@ -582,6 +597,7 @@ func TestRuntimeConfiguration(t *testing.T) { serviceConfig: map[string]*dynamic.Service{ "foo-service": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1", @@ -608,6 +624,7 @@ func TestRuntimeConfiguration(t *testing.T) { serviceConfig: map[string]*dynamic.Service{ "foo-service": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1", @@ -641,6 +658,7 @@ func TestRuntimeConfiguration(t *testing.T) { serviceConfig: map[string]*dynamic.Service{ "foo-service": { LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://127.0.0.1", @@ -730,7 +748,8 @@ func TestProviderOnMiddlewares(t *testing.T) { Services: map[string]*dynamic.Service{ "test@file": { LoadBalancer: &dynamic.ServersLoadBalancer{ - Servers: []dynamic.Server{}, + Strategy: dynamic.BalancerStrategyWRR, + Servers: []dynamic.Server{}, }, }, }, diff --git a/pkg/server/service/loadbalancer/p2c/p2c.go b/pkg/server/service/loadbalancer/p2c/p2c.go new file mode 100644 index 000000000..994df35bc --- /dev/null +++ b/pkg/server/service/loadbalancer/p2c/p2c.go @@ -0,0 +1,227 @@ +package p2c + +import ( + "context" + "errors" + "math/rand" + "net/http" + "sync" + "sync/atomic" + "time" + + "github.com/rs/zerolog/log" + "github.com/traefik/traefik/v3/pkg/config/dynamic" + "github.com/traefik/traefik/v3/pkg/server/service/loadbalancer" +) + +type namedHandler struct { + http.Handler + + // name is the handler name. + name string + // inflight is the number of inflight requests. + // It is used to implement the "power-of-two-random-choices" algorithm. + inflight atomic.Int64 +} + +func (h *namedHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { + h.inflight.Add(1) + defer h.inflight.Add(-1) + + h.Handler.ServeHTTP(rw, req) +} + +type rnd interface { + Intn(n int) int +} + +// Balancer implements the power-of-two-random-choices algorithm for load balancing. +// The idea is to randomly select two of the available backends and choose the one with the fewest in-flight requests. +// This algorithm balances the load more effectively than a round-robin approach, while maintaining a constant time for the selection: +// The strategy also has more advantageous "herd" behavior than the "fewest connections" algorithm, especially when the load balancer +// doesn't have perfect knowledge of the global number of connections to the backend, for example, when running in a distributed fashion. +type Balancer struct { + wantsHealthCheck bool + + handlersMu sync.RWMutex + handlers []*namedHandler + // status is a record of which child services of the Balancer are healthy, keyed + // by name of child service. A service is initially added to the map when it is + // created via Add, and it is later removed or added to the map as needed, + // through the SetStatus method. + status map[string]struct{} + // updaters is the list of hooks that are run (to update the Balancer + // parent(s)), whenever the Balancer status changes. + updaters []func(bool) + // fenced is the list of terminating yet still serving child services. + fenced map[string]struct{} + + sticky *loadbalancer.Sticky + + rand rnd +} + +// New creates a new power-of-two-random-choices load balancer. +func New(stickyConfig *dynamic.Sticky, wantsHealthCheck bool) *Balancer { + balancer := &Balancer{ + status: make(map[string]struct{}), + fenced: make(map[string]struct{}), + wantsHealthCheck: wantsHealthCheck, + rand: rand.New(rand.NewSource(time.Now().UnixNano())), + } + if stickyConfig != nil && stickyConfig.Cookie != nil { + balancer.sticky = loadbalancer.NewSticky(*stickyConfig.Cookie) + } + + return balancer +} + +// SetStatus sets on the balancer that its given child is now of the given +// status. balancerName is only needed for logging purposes. +func (b *Balancer) SetStatus(ctx context.Context, childName string, up bool) { + b.handlersMu.Lock() + defer b.handlersMu.Unlock() + + upBefore := len(b.status) > 0 + + status := "DOWN" + if up { + status = "UP" + } + + log.Ctx(ctx).Debug().Msgf("Setting status of %s to %v", childName, status) + + if up { + b.status[childName] = struct{}{} + } else { + delete(b.status, childName) + } + + upAfter := len(b.status) > 0 + status = "DOWN" + if upAfter { + status = "UP" + } + + // No Status Change + if upBefore == upAfter { + // We're still with the same status, no need to propagate + log.Ctx(ctx).Debug().Msgf("Still %s, no need to propagate", status) + return + } + + // Status Change + log.Ctx(ctx).Debug().Msgf("Propagating new %s status", status) + for _, fn := range b.updaters { + fn(upAfter) + } +} + +// RegisterStatusUpdater adds fn to the list of hooks that are run when the +// status of the Balancer changes. +// Not thread safe. +func (b *Balancer) RegisterStatusUpdater(fn func(up bool)) error { + if !b.wantsHealthCheck { + return errors.New("healthCheck not enabled in config for this weighted service") + } + b.updaters = append(b.updaters, fn) + return nil +} + +var errNoAvailableServer = errors.New("no available server") + +func (b *Balancer) nextServer() (*namedHandler, error) { + // We kept the same representation (map) as in the WRR strategy to improve maintainability. + // However, with the P2C strategy, we only need a slice of healthy servers. + b.handlersMu.RLock() + var healthy []*namedHandler + for _, h := range b.handlers { + if _, ok := b.status[h.name]; ok { + if _, fenced := b.fenced[h.name]; !fenced { + healthy = append(healthy, h) + } + } + } + b.handlersMu.RUnlock() + + if len(healthy) == 0 { + return nil, errNoAvailableServer + } + + // If there is only one healthy server, return it. + if len(healthy) == 1 { + return healthy[0], nil + } + // In order to not get the same backend twice, we make the second call to s.rand.IntN one fewer + // than the length of the slice. We then have to shift over the second index if it is equal or + // greater than the first index, wrapping round if needed. + n1, n2 := b.rand.Intn(len(healthy)), b.rand.Intn(len(healthy)) + if n2 == n1 { + n2 = (n2 + 1) % len(healthy) + } + + h1, h2 := healthy[n1], healthy[n2] + // Ensure h1 has fewer inflight requests than h2. + if h2.inflight.Load() < h1.inflight.Load() { + log.Debug().Msgf("Service selected by P2C: %s", h2.name) + return h2, nil + } + + log.Debug().Msgf("Service selected by P2C: %s", h1.name) + return h1, nil +} + +func (b *Balancer) ServeHTTP(rw http.ResponseWriter, req *http.Request) { + if b.sticky != nil { + h, rewrite, err := b.sticky.StickyHandler(req) + if err != nil { + log.Error().Err(err).Msg("Error while getting sticky handler") + } else if h != nil { + if _, ok := b.status[h.Name]; ok { + if rewrite { + if err := b.sticky.WriteStickyCookie(rw, h.Name); err != nil { + log.Error().Err(err).Msg("Writing sticky cookie") + } + } + + h.ServeHTTP(rw, req) + return + } + } + } + + server, err := b.nextServer() + if err != nil { + if errors.Is(err, errNoAvailableServer) { + http.Error(rw, errNoAvailableServer.Error(), http.StatusServiceUnavailable) + } else { + http.Error(rw, err.Error(), http.StatusInternalServerError) + } + return + } + + if b.sticky != nil { + if err := b.sticky.WriteStickyCookie(rw, server.name); err != nil { + log.Error().Err(err).Msg("Error while writing sticky cookie") + } + } + + server.ServeHTTP(rw, req) +} + +// AddServer adds a handler with a server. +func (b *Balancer) AddServer(name string, handler http.Handler, server dynamic.Server) { + h := &namedHandler{Handler: handler, name: name} + + b.handlersMu.Lock() + b.handlers = append(b.handlers, h) + b.status[name] = struct{}{} + if server.Fenced { + b.fenced[name] = struct{}{} + } + b.handlersMu.Unlock() + + if b.sticky != nil { + b.sticky.AddHandler(name, h) + } +} diff --git a/pkg/server/service/loadbalancer/p2c/p2c_test.go b/pkg/server/service/loadbalancer/p2c/p2c_test.go new file mode 100644 index 000000000..b89420de2 --- /dev/null +++ b/pkg/server/service/loadbalancer/p2c/p2c_test.go @@ -0,0 +1,288 @@ +package p2c + +import ( + "context" + "net/http" + "net/http/httptest" + "strconv" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/traefik/traefik/v3/pkg/config/dynamic" +) + +func TestP2C(t *testing.T) { + testCases := []struct { + desc string + handlers []*namedHandler + rand *mockRand + expectedHandler string + }{ + { + desc: "one healthy handler", + handlers: testHandlers(0), + rand: nil, + expectedHandler: "0", + }, + { + desc: "two handlers zero in flight", + handlers: testHandlers(0, 0), + rand: &mockRand{vals: []int{1, 0}}, + expectedHandler: "1", + }, + { + desc: "chooses lower of two", + handlers: testHandlers(0, 1), + rand: &mockRand{vals: []int{1, 0}}, + expectedHandler: "0", + }, + { + desc: "chooses lower of three", + handlers: testHandlers(10, 90, 40), + rand: &mockRand{vals: []int{1, 1}}, + expectedHandler: "2", + }, + } + + for _, test := range testCases { + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + balancer := New(nil, false) + balancer.rand = test.rand + + for _, h := range test.handlers { + balancer.handlers = append(balancer.handlers, h) + balancer.status[h.name] = struct{}{} + } + + got, err := balancer.nextServer() + require.NoError(t, err) + + assert.Equal(t, test.expectedHandler, got.name) + }) + } +} + +func TestSticky(t *testing.T) { + balancer := New(&dynamic.Sticky{ + Cookie: &dynamic.Cookie{ + Name: "test", + Secure: true, + HTTPOnly: true, + SameSite: "none", + MaxAge: 42, + Path: func(v string) *string { return &v }("/foo"), + }, + }, false) + balancer.rand = &mockRand{vals: []int{1, 0}} + + balancer.AddServer("first", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set("server", "first") + rw.WriteHeader(http.StatusOK) + }), dynamic.Server{}) + + balancer.AddServer("second", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set("server", "second") + rw.WriteHeader(http.StatusOK) + }), dynamic.Server{}) + + recorder := &responseRecorder{ + ResponseRecorder: httptest.NewRecorder(), + save: map[string]int{}, + cookies: make(map[string]*http.Cookie), + } + + req := httptest.NewRequest(http.MethodGet, "/", nil) + for range 3 { + for _, cookie := range recorder.Result().Cookies() { + assert.NotContains(t, "first", cookie.Value) + assert.NotContains(t, "second", cookie.Value) + req.AddCookie(cookie) + } + recorder.ResponseRecorder = httptest.NewRecorder() + + balancer.ServeHTTP(recorder, req) + } + + assert.Equal(t, 0, recorder.save["first"]) + assert.Equal(t, 3, recorder.save["second"]) + assert.True(t, recorder.cookies["test"].HttpOnly) + assert.True(t, recorder.cookies["test"].Secure) + assert.Equal(t, http.SameSiteNoneMode, recorder.cookies["test"].SameSite) + assert.Equal(t, 42, recorder.cookies["test"].MaxAge) + assert.Equal(t, "/foo", recorder.cookies["test"].Path) +} + +func TestSticky_Fallback(t *testing.T) { + balancer := New(&dynamic.Sticky{ + Cookie: &dynamic.Cookie{Name: "test"}, + }, false) + balancer.rand = &mockRand{vals: []int{1, 0}} + + balancer.AddServer("first", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set("server", "first") + rw.WriteHeader(http.StatusOK) + }), dynamic.Server{}) + + balancer.AddServer("second", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set("server", "second") + rw.WriteHeader(http.StatusOK) + }), dynamic.Server{}) + + recorder := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}, cookies: make(map[string]*http.Cookie)} + + req := httptest.NewRequest(http.MethodGet, "/", nil) + req.AddCookie(&http.Cookie{Name: "test", Value: "second"}) + for range 3 { + recorder.ResponseRecorder = httptest.NewRecorder() + + balancer.ServeHTTP(recorder, req) + } + + assert.Equal(t, 0, recorder.save["first"]) + assert.Equal(t, 3, recorder.save["second"]) +} + +// TestSticky_Fenced checks that fenced node receive traffic if their sticky cookie matches. +func TestSticky_Fenced(t *testing.T) { + balancer := New(&dynamic.Sticky{Cookie: &dynamic.Cookie{Name: "test"}}, false) + balancer.rand = &mockRand{vals: []int{1, 0, 1, 0}} + + balancer.AddServer("first", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set("server", "first") + rw.WriteHeader(http.StatusOK) + }), dynamic.Server{}) + + balancer.AddServer("second", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set("server", "second") + rw.WriteHeader(http.StatusOK) + }), dynamic.Server{}) + + balancer.AddServer("fenced", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set("server", "fenced") + rw.WriteHeader(http.StatusOK) + }), dynamic.Server{Fenced: true}) + + recorder := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}, cookies: make(map[string]*http.Cookie)} + + stickyReq := httptest.NewRequest(http.MethodGet, "/", nil) + stickyReq.AddCookie(&http.Cookie{Name: "test", Value: "fenced"}) + + req := httptest.NewRequest(http.MethodGet, "/", nil) + + for range 2 { + recorder.ResponseRecorder = httptest.NewRecorder() + + balancer.ServeHTTP(recorder, stickyReq) + balancer.ServeHTTP(recorder, req) + } + + assert.Equal(t, 2, recorder.save["fenced"]) + assert.Equal(t, 0, recorder.save["first"]) + assert.Equal(t, 2, recorder.save["second"]) +} + +func TestBalancerPropagate(t *testing.T) { + balancer := New(nil, true) + + balancer.AddServer("first", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set("server", "first") + rw.WriteHeader(http.StatusOK) + }), dynamic.Server{}) + balancer.AddServer("second", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set("server", "second") + rw.WriteHeader(http.StatusOK) + }), dynamic.Server{}) + + var calls int + err := balancer.RegisterStatusUpdater(func(up bool) { + calls++ + }) + require.NoError(t, err) + + recorder := httptest.NewRecorder() + balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) + assert.Equal(t, http.StatusOK, recorder.Code) + + // two gets downed, but balancer still up since first is still up. + balancer.SetStatus(context.Background(), "second", false) + assert.Equal(t, 0, calls) + + recorder = httptest.NewRecorder() + balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) + assert.Equal(t, http.StatusOK, recorder.Code) + assert.Equal(t, "first", recorder.Header().Get("server")) + + // first gets downed, balancer is down. + balancer.SetStatus(context.Background(), "first", false) + assert.Equal(t, 1, calls) + + recorder = httptest.NewRecorder() + balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) + assert.Equal(t, http.StatusServiceUnavailable, recorder.Code) + + // two gets up, balancer up. + balancer.SetStatus(context.Background(), "second", true) + assert.Equal(t, 2, calls) + + recorder = httptest.NewRecorder() + balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) + assert.Equal(t, http.StatusOK, recorder.Code) + assert.Equal(t, "second", recorder.Header().Get("server")) +} + +func TestBalancerAllServersFenced(t *testing.T) { + balancer := New(nil, false) + + balancer.AddServer("test", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {}), dynamic.Server{Fenced: true}) + balancer.AddServer("test2", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {}), dynamic.Server{Fenced: true}) + + recorder := httptest.NewRecorder() + balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) + + assert.Equal(t, http.StatusServiceUnavailable, recorder.Result().StatusCode) +} + +type responseRecorder struct { + *httptest.ResponseRecorder + save map[string]int + sequence []string + status []int + cookies map[string]*http.Cookie +} + +func (r *responseRecorder) WriteHeader(statusCode int) { + r.save[r.Header().Get("server")]++ + r.sequence = append(r.sequence, r.Header().Get("server")) + r.status = append(r.status, statusCode) + for _, cookie := range r.Result().Cookies() { + r.cookies[cookie.Name] = cookie + } + r.ResponseRecorder.WriteHeader(statusCode) +} + +type mockRand struct { + vals []int + calls int +} + +func (m *mockRand) Intn(int) int { + defer func() { + m.calls++ + }() + return m.vals[m.calls] +} + +func testHandlers(inflights ...int) []*namedHandler { + var out []*namedHandler + for i, inflight := range inflights { + h := &namedHandler{ + name: strconv.Itoa(i), + } + h.inflight.Store(int64(inflight)) + out = append(out, h) + } + return out +} diff --git a/pkg/server/service/loadbalancer/sticky.go b/pkg/server/service/loadbalancer/sticky.go new file mode 100644 index 000000000..380234e18 --- /dev/null +++ b/pkg/server/service/loadbalancer/sticky.go @@ -0,0 +1,179 @@ +package loadbalancer + +import ( + "crypto/sha256" + "encoding/hex" + "errors" + "fmt" + "hash/fnv" + "net/http" + "strconv" + "sync" + + "github.com/traefik/traefik/v3/pkg/config/dynamic" +) + +// NamedHandler is a http.Handler with a name. +type NamedHandler struct { + http.Handler + + Name string +} + +// stickyCookie represents a sticky cookie. +type stickyCookie struct { + name string + secure bool + httpOnly bool + sameSite http.SameSite + maxAge int + path string + domain string +} + +// Sticky ensures that client consistently interacts with the same HTTP handler by adding a sticky cookie to the response. +// This cookie allows subsequent requests from the same client to be routed to the same handler, +// enabling session persistence across multiple requests. +type Sticky struct { + // cookie is the sticky cookie configuration. + cookie *stickyCookie + + // References all the handlers by name and also by the hashed value of the name. + handlersMu sync.RWMutex + hashMap map[string]string + stickyMap map[string]*NamedHandler + compatibilityStickyMap map[string]*NamedHandler +} + +// NewSticky creates a new Sticky instance. +func NewSticky(cookieConfig dynamic.Cookie) *Sticky { + cookie := &stickyCookie{ + name: cookieConfig.Name, + secure: cookieConfig.Secure, + httpOnly: cookieConfig.HTTPOnly, + sameSite: convertSameSite(cookieConfig.SameSite), + maxAge: cookieConfig.MaxAge, + path: "/", + domain: cookieConfig.Domain, + } + if cookieConfig.Path != nil { + cookie.path = *cookieConfig.Path + } + + return &Sticky{ + cookie: cookie, + hashMap: make(map[string]string), + stickyMap: make(map[string]*NamedHandler), + compatibilityStickyMap: make(map[string]*NamedHandler), + } +} + +// AddHandler adds a http.Handler to the sticky pool. +func (s *Sticky) AddHandler(name string, h http.Handler) { + s.handlersMu.Lock() + defer s.handlersMu.Unlock() + + sha256HashedName := sha256Hash(name) + s.hashMap[name] = sha256HashedName + + handler := &NamedHandler{ + Handler: h, + Name: name, + } + + s.stickyMap[sha256HashedName] = handler + s.compatibilityStickyMap[name] = handler + + hashedName := fnvHash(name) + s.compatibilityStickyMap[hashedName] = handler + + // server.URL was fnv hashed in service.Manager + // so we can have "double" fnv hash in already existing cookies + hashedName = fnvHash(hashedName) + s.compatibilityStickyMap[hashedName] = handler +} + +// StickyHandler returns the NamedHandler corresponding to the sticky cookie if one. +// It also returns a boolean which indicates if the sticky cookie has to be overwritten because it uses a deprecated hash algorithm. +func (s *Sticky) StickyHandler(req *http.Request) (*NamedHandler, bool, error) { + cookie, err := req.Cookie(s.cookie.name) + if err != nil && errors.Is(err, http.ErrNoCookie) { + return nil, false, nil + } + if err != nil { + return nil, false, fmt.Errorf("reading cookie: %w", err) + } + + s.handlersMu.RLock() + handler, ok := s.stickyMap[cookie.Value] + s.handlersMu.RUnlock() + + if ok && handler != nil { + return handler, false, nil + } + + s.handlersMu.RLock() + handler, ok = s.compatibilityStickyMap[cookie.Value] + s.handlersMu.RUnlock() + + return handler, ok, nil +} + +// WriteStickyCookie writes a sticky cookie to the response to stick the client to the given handler name. +func (s *Sticky) WriteStickyCookie(rw http.ResponseWriter, name string) error { + s.handlersMu.RLock() + hash, ok := s.hashMap[name] + s.handlersMu.RUnlock() + if !ok { + return fmt.Errorf("no hash found for handler named %s", name) + } + + cookie := &http.Cookie{ + Name: s.cookie.name, + Value: hash, + Path: s.cookie.path, + Domain: s.cookie.domain, + HttpOnly: s.cookie.httpOnly, + Secure: s.cookie.secure, + SameSite: s.cookie.sameSite, + MaxAge: s.cookie.maxAge, + } + http.SetCookie(rw, cookie) + + return nil +} + +func convertSameSite(sameSite string) http.SameSite { + switch sameSite { + case "none": + return http.SameSiteNoneMode + case "lax": + return http.SameSiteLaxMode + case "strict": + return http.SameSiteStrictMode + default: + return http.SameSiteDefaultMode + } +} + +// fnvHash returns the FNV-64 hash of the input string. +func fnvHash(input string) string { + hasher := fnv.New64() + // We purposely ignore the error because the implementation always returns nil. + _, _ = hasher.Write([]byte(input)) + + return strconv.FormatUint(hasher.Sum64(), 16) +} + +// sha256 returns the SHA-256 hash, truncated to 16 characters, of the input string. +func sha256Hash(input string) string { + hash := sha256.New() + // We purposely ignore the error because the implementation always returns nil. + _, _ = hash.Write([]byte(input)) + + hashedInput := hex.EncodeToString(hash.Sum(nil)) + if len(hashedInput) < 16 { + return hashedInput + } + return hashedInput[:16] +} diff --git a/pkg/server/service/loadbalancer/sticky_test.go b/pkg/server/service/loadbalancer/sticky_test.go new file mode 100644 index 000000000..d94992224 --- /dev/null +++ b/pkg/server/service/loadbalancer/sticky_test.go @@ -0,0 +1,138 @@ +package loadbalancer + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/traefik/traefik/v3/pkg/config/dynamic" +) + +func pointer[T any](v T) *T { return &v } + +func TestSticky_StickyHandler(t *testing.T) { + testCases := []struct { + desc string + handlers []string + cookies []*http.Cookie + wantHandler string + wantRewrite bool + }{ + { + desc: "No previous cookie", + handlers: []string{"first"}, + wantHandler: "", + wantRewrite: false, + }, + { + desc: "Wrong previous cookie", + handlers: []string{"first"}, + cookies: []*http.Cookie{ + {Name: "test", Value: sha256Hash("foo")}, + }, + wantHandler: "", + wantRewrite: false, + }, + { + desc: "Sha256 previous cookie", + handlers: []string{"first", "second"}, + cookies: []*http.Cookie{ + {Name: "test", Value: sha256Hash("first")}, + }, + wantHandler: "first", + wantRewrite: false, + }, + { + desc: "Raw previous cookie", + handlers: []string{"first", "second"}, + cookies: []*http.Cookie{ + {Name: "test", Value: "first"}, + }, + wantHandler: "first", + wantRewrite: true, + }, + { + desc: "Fnv previous cookie", + handlers: []string{"first", "second"}, + cookies: []*http.Cookie{ + {Name: "test", Value: fnvHash("first")}, + }, + wantHandler: "first", + wantRewrite: true, + }, + { + desc: "Double fnv previous cookie", + handlers: []string{"first", "second"}, + cookies: []*http.Cookie{ + {Name: "test", Value: fnvHash("first")}, + }, + wantHandler: "first", + wantRewrite: true, + }, + } + + for _, test := range testCases { + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + sticky := NewSticky(dynamic.Cookie{Name: "test"}) + + for _, handler := range test.handlers { + sticky.AddHandler(handler, http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {})) + } + + req := httptest.NewRequest(http.MethodGet, "/", nil) + for _, cookie := range test.cookies { + req.AddCookie(cookie) + } + + h, rewrite, err := sticky.StickyHandler(req) + require.NoError(t, err) + + if test.wantHandler != "" { + assert.NotNil(t, h) + assert.Equal(t, test.wantHandler, h.Name) + } else { + assert.Nil(t, h) + } + assert.Equal(t, test.wantRewrite, rewrite) + }) + } +} + +func TestSticky_WriteStickyCookie(t *testing.T) { + sticky := NewSticky(dynamic.Cookie{ + Name: "test", + Secure: true, + HTTPOnly: true, + SameSite: "none", + MaxAge: 42, + Path: pointer("/foo"), + Domain: "foo.com", + }) + + // Should return an error if the handler does not exist. + res := httptest.NewRecorder() + require.Error(t, sticky.WriteStickyCookie(res, "first")) + + // Should write the sticky cookie and use the sha256 hash. + sticky.AddHandler("first", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {})) + + res = httptest.NewRecorder() + require.NoError(t, sticky.WriteStickyCookie(res, "first")) + + assert.Len(t, res.Result().Cookies(), 1) + + cookie := res.Result().Cookies()[0] + + assert.Equal(t, sha256Hash("first"), cookie.Value) + assert.Equal(t, "test", cookie.Name) + assert.True(t, cookie.Secure) + assert.True(t, cookie.HttpOnly) + assert.Equal(t, http.SameSiteNoneMode, cookie.SameSite) + assert.Equal(t, 42, cookie.MaxAge) + assert.Equal(t, "/foo", cookie.Path) + assert.Equal(t, "foo.com", cookie.Domain) +} diff --git a/pkg/server/service/loadbalancer/wrr/wrr.go b/pkg/server/service/loadbalancer/wrr/wrr.go index b26b06320..8206b97aa 100644 --- a/pkg/server/service/loadbalancer/wrr/wrr.go +++ b/pkg/server/service/loadbalancer/wrr/wrr.go @@ -3,47 +3,20 @@ package wrr import ( "container/heap" "context" - "crypto/sha256" - "encoding/hex" "errors" - "hash/fnv" "net/http" - "strconv" "sync" "github.com/rs/zerolog/log" "github.com/traefik/traefik/v3/pkg/config/dynamic" + "github.com/traefik/traefik/v3/pkg/server/service/loadbalancer" ) type namedHandler struct { http.Handler - name string - hashedName string - weight float64 - deadline float64 -} - -type stickyCookie struct { name string - secure bool - httpOnly bool - sameSite string - maxAge int - path string - domain string -} - -func convertSameSite(sameSite string) http.SameSite { - switch sameSite { - case "none": - return http.SameSiteNoneMode - case "lax": - return http.SameSiteLaxMode - case "strict": - return http.SameSiteStrictMode - default: - return http.SameSiteDefaultMode - } + weight float64 + deadline float64 } // Balancer is a WeightedRoundRobin load balancer based on Earliest Deadline First (EDF). @@ -52,15 +25,10 @@ func convertSameSite(sameSite string) http.SameSite { // Entries have deadlines set at currentDeadline + 1 / weight, // providing weighted round-robin behavior with floating point weights and an O(log n) pick time. type Balancer struct { - stickyCookie *stickyCookie wantsHealthCheck bool handlersMu sync.RWMutex - // References all the handlers by name and also by the hashed value of the name. - stickyMap map[string]*namedHandler - compatibilityStickyMap map[string]*namedHandler - handlers []*namedHandler - curDeadline float64 + handlers []*namedHandler // status is a record of which child services of the Balancer are healthy, keyed // by name of child service. A service is initially added to the map when it is // created via Add, and it is later removed or added to the map as needed, @@ -71,31 +39,21 @@ type Balancer struct { updaters []func(bool) // fenced is the list of terminating yet still serving child services. fenced map[string]struct{} + + sticky *loadbalancer.Sticky + + curDeadline float64 } // New creates a new load balancer. -func New(sticky *dynamic.Sticky, wantHealthCheck bool) *Balancer { +func New(sticky *dynamic.Sticky, wantsHealthCheck bool) *Balancer { balancer := &Balancer{ status: make(map[string]struct{}), fenced: make(map[string]struct{}), - wantsHealthCheck: wantHealthCheck, + wantsHealthCheck: wantsHealthCheck, } if sticky != nil && sticky.Cookie != nil { - balancer.stickyCookie = &stickyCookie{ - name: sticky.Cookie.Name, - secure: sticky.Cookie.Secure, - httpOnly: sticky.Cookie.HTTPOnly, - sameSite: sticky.Cookie.SameSite, - maxAge: sticky.Cookie.MaxAge, - path: "/", - domain: sticky.Cookie.Domain, - } - if sticky.Cookie.Path != nil { - balancer.stickyCookie.path = *sticky.Cookie.Path - } - - balancer.stickyMap = make(map[string]*namedHandler) - balancer.compatibilityStickyMap = make(map[string]*namedHandler) + balancer.sticky = loadbalancer.NewSticky(*sticky.Cookie) } return balancer @@ -216,43 +174,21 @@ func (b *Balancer) nextServer() (*namedHandler, error) { return handler, nil } -func (b *Balancer) ServeHTTP(w http.ResponseWriter, req *http.Request) { - if b.stickyCookie != nil { - cookie, err := req.Cookie(b.stickyCookie.name) - - if err != nil && !errors.Is(err, http.ErrNoCookie) { - log.Warn().Err(err).Msg("Error while reading cookie") - } - - if err == nil && cookie != nil { - b.handlersMu.RLock() - handler, ok := b.stickyMap[cookie.Value] - b.handlersMu.RUnlock() - - if ok && handler != nil { - b.handlersMu.RLock() - _, isHealthy := b.status[handler.name] - b.handlersMu.RUnlock() - if isHealthy { - handler.ServeHTTP(w, req) - return +func (b *Balancer) ServeHTTP(rw http.ResponseWriter, req *http.Request) { + if b.sticky != nil { + h, rewrite, err := b.sticky.StickyHandler(req) + if err != nil { + log.Error().Err(err).Msg("Error while getting sticky handler") + } else if h != nil { + if _, ok := b.status[h.Name]; ok { + if rewrite { + if err := b.sticky.WriteStickyCookie(rw, h.Name); err != nil { + log.Error().Err(err).Msg("Writing sticky cookie") + } } - } - b.handlersMu.RLock() - handler, ok = b.compatibilityStickyMap[cookie.Value] - b.handlersMu.RUnlock() - - if ok && handler != nil { - b.handlersMu.RLock() - _, isHealthy := b.status[handler.name] - b.handlersMu.RUnlock() - if isHealthy { - b.writeStickyCookie(w, handler) - - handler.ServeHTTP(w, req) - return - } + h.ServeHTTP(rw, req) + return } } } @@ -260,32 +196,25 @@ func (b *Balancer) ServeHTTP(w http.ResponseWriter, req *http.Request) { server, err := b.nextServer() if err != nil { if errors.Is(err, errNoAvailableServer) { - http.Error(w, errNoAvailableServer.Error(), http.StatusServiceUnavailable) + http.Error(rw, errNoAvailableServer.Error(), http.StatusServiceUnavailable) } else { - http.Error(w, err.Error(), http.StatusInternalServerError) + http.Error(rw, err.Error(), http.StatusInternalServerError) } return } - if b.stickyCookie != nil { - b.writeStickyCookie(w, server) + if b.sticky != nil { + if err := b.sticky.WriteStickyCookie(rw, server.name); err != nil { + log.Error().Err(err).Msg("Error while writing sticky cookie") + } } - server.ServeHTTP(w, req) + server.ServeHTTP(rw, req) } -func (b *Balancer) writeStickyCookie(w http.ResponseWriter, handler *namedHandler) { - cookie := &http.Cookie{ - Name: b.stickyCookie.name, - Value: handler.hashedName, - Path: b.stickyCookie.path, - HttpOnly: b.stickyCookie.httpOnly, - Secure: b.stickyCookie.secure, - SameSite: convertSameSite(b.stickyCookie.sameSite), - MaxAge: b.stickyCookie.maxAge, - Domain: b.stickyCookie.domain, - } - http.SetCookie(w, cookie) +// AddServer adds a handler with a server. +func (b *Balancer) AddServer(name string, handler http.Handler, server dynamic.Server) { + b.Add(name, handler, server.Weight, server.Fenced) } // Add adds a handler. @@ -309,41 +238,9 @@ func (b *Balancer) Add(name string, handler http.Handler, weight *int, fenced bo if fenced { b.fenced[name] = struct{}{} } - - if b.stickyCookie != nil { - sha256HashedName := sha256Hash(name) - h.hashedName = sha256HashedName - - b.stickyMap[sha256HashedName] = h - b.compatibilityStickyMap[name] = h - - hashedName := fnvHash(name) - b.compatibilityStickyMap[hashedName] = h - - // server.URL was fnv hashed in service.Manager - // so we can have "double" fnv hash in already existing cookies - hashedName = fnvHash(hashedName) - b.compatibilityStickyMap[hashedName] = h - } b.handlersMu.Unlock() -} -func fnvHash(input string) string { - hasher := fnv.New64() - // We purposely ignore the error because the implementation always returns nil. - _, _ = hasher.Write([]byte(input)) - - return strconv.FormatUint(hasher.Sum64(), 16) -} - -func sha256Hash(input string) string { - hash := sha256.New() - // We purposely ignore the error because the implementation always returns nil. - _, _ = hash.Write([]byte(input)) - - hashedInput := hex.EncodeToString(hash.Sum(nil)) - if len(hashedInput) < 16 { - return hashedInput + if b.sticky != nil { + b.sticky.AddHandler(name, handler) } - return hashedInput[:16] } diff --git a/pkg/server/service/loadbalancer/wrr/wrr_test.go b/pkg/server/service/loadbalancer/wrr/wrr_test.go index 266a58f3d..7e9c3bc04 100644 --- a/pkg/server/service/loadbalancer/wrr/wrr_test.go +++ b/pkg/server/service/loadbalancer/wrr/wrr_test.go @@ -10,6 +10,10 @@ import ( "github.com/traefik/traefik/v3/pkg/config/dynamic" ) +type key string + +const serviceName key = "serviceName" + func pointer[T any](v T) *T { return &v } func TestBalancer(t *testing.T) { @@ -61,10 +65,6 @@ func TestBalancerOneServerZeroWeight(t *testing.T) { assert.Equal(t, 3, recorder.save["first"]) } -type key string - -const serviceName key = "serviceName" - func TestBalancerNoServiceUp(t *testing.T) { balancer := New(nil, false) @@ -264,8 +264,8 @@ func TestSticky(t *testing.T) { req := httptest.NewRequest(http.MethodGet, "/", nil) for range 3 { for _, cookie := range recorder.Result().Cookies() { - assert.NotContains(t, "test=first", cookie.Value) - assert.NotContains(t, "test=second", cookie.Value) + assert.NotContains(t, "first", cookie.Value) + assert.NotContains(t, "second", cookie.Value) req.AddCookie(cookie) } recorder.ResponseRecorder = httptest.NewRecorder() @@ -283,7 +283,7 @@ func TestSticky(t *testing.T) { assert.Equal(t, "/foo", recorder.cookies["test"].Path) } -func TestSticky_FallBack(t *testing.T) { +func TestSticky_Fallback(t *testing.T) { balancer := New(&dynamic.Sticky{ Cookie: &dynamic.Cookie{Name: "test"}, }, false) @@ -312,6 +312,44 @@ func TestSticky_FallBack(t *testing.T) { assert.Equal(t, 3, recorder.save["second"]) } +// TestSticky_Fenced checks that fenced node receive traffic if their sticky cookie matches. +func TestSticky_Fenced(t *testing.T) { + balancer := New(&dynamic.Sticky{Cookie: &dynamic.Cookie{Name: "test"}}, false) + + balancer.Add("first", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set("server", "first") + rw.WriteHeader(http.StatusOK) + }), pointer(1), false) + + balancer.Add("second", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set("server", "second") + rw.WriteHeader(http.StatusOK) + }), pointer(1), false) + + balancer.Add("fenced", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set("server", "fenced") + rw.WriteHeader(http.StatusOK) + }), pointer(1), true) + + recorder := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}, cookies: make(map[string]*http.Cookie)} + + stickyReq := httptest.NewRequest(http.MethodGet, "/", nil) + stickyReq.AddCookie(&http.Cookie{Name: "test", Value: "fenced"}) + + req := httptest.NewRequest(http.MethodGet, "/", nil) + + for range 4 { + recorder.ResponseRecorder = httptest.NewRecorder() + + balancer.ServeHTTP(recorder, stickyReq) + balancer.ServeHTTP(recorder, req) + } + + assert.Equal(t, 4, recorder.save["fenced"]) + assert.Equal(t, 2, recorder.save["first"]) + assert.Equal(t, 2, recorder.save["second"]) +} + // TestBalancerBias makes sure that the WRR algorithm spreads elements evenly right from the start, // and that it does not "over-favor" the high-weighted ones with a biased start-up regime. func TestBalancerBias(t *testing.T) { @@ -355,137 +393,3 @@ func (r *responseRecorder) WriteHeader(statusCode int) { } r.ResponseRecorder.WriteHeader(statusCode) } - -// TestSticky_Fenced checks that fenced node receive traffic if their sticky cookie matches. -func TestSticky_Fenced(t *testing.T) { - balancer := New(&dynamic.Sticky{Cookie: &dynamic.Cookie{Name: "test"}}, false) - - balancer.Add("first", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { - rw.Header().Set("server", "first") - rw.WriteHeader(http.StatusOK) - }), pointer(1), false) - - balancer.Add("second", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { - rw.Header().Set("server", "second") - rw.WriteHeader(http.StatusOK) - }), pointer(1), false) - - balancer.Add("fenced", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { - rw.Header().Set("server", "fenced") - rw.WriteHeader(http.StatusOK) - }), pointer(1), true) - - recorder := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}, cookies: make(map[string]*http.Cookie)} - - stickyReq := httptest.NewRequest(http.MethodGet, "/", nil) - stickyReq.AddCookie(&http.Cookie{Name: "test", Value: "fenced"}) - - req := httptest.NewRequest(http.MethodGet, "/", nil) - - for range 4 { - recorder.ResponseRecorder = httptest.NewRecorder() - - balancer.ServeHTTP(recorder, stickyReq) - balancer.ServeHTTP(recorder, req) - } - - assert.Equal(t, 4, recorder.save["fenced"]) - assert.Equal(t, 2, recorder.save["first"]) - assert.Equal(t, 2, recorder.save["second"]) -} - -func TestStickyWithCompatibility(t *testing.T) { - testCases := []struct { - desc string - servers []string - cookies []*http.Cookie - - expectedCookies []*http.Cookie - expectedServer string - }{ - { - desc: "No previous cookie", - servers: []string{"first"}, - - expectedServer: "first", - expectedCookies: []*http.Cookie{ - {Name: "test", Value: sha256Hash("first")}, - }, - }, - { - desc: "Sha256 previous cookie", - servers: []string{"first", "second"}, - cookies: []*http.Cookie{ - {Name: "test", Value: sha256Hash("first")}, - }, - expectedServer: "first", - expectedCookies: []*http.Cookie{}, - }, - { - desc: "Raw previous cookie", - servers: []string{"first", "second"}, - cookies: []*http.Cookie{ - {Name: "test", Value: "first"}, - }, - expectedServer: "first", - expectedCookies: []*http.Cookie{ - {Name: "test", Value: sha256Hash("first")}, - }, - }, - { - desc: "Fnv previous cookie", - servers: []string{"first", "second"}, - cookies: []*http.Cookie{ - {Name: "test", Value: fnvHash("first")}, - }, - expectedServer: "first", - expectedCookies: []*http.Cookie{ - {Name: "test", Value: sha256Hash("first")}, - }, - }, - { - desc: "Double fnv previous cookie", - servers: []string{"first", "second"}, - cookies: []*http.Cookie{ - {Name: "test", Value: fnvHash(fnvHash("first"))}, - }, - expectedServer: "first", - expectedCookies: []*http.Cookie{ - {Name: "test", Value: sha256Hash("first")}, - }, - }, - } - - for _, test := range testCases { - t.Run(test.desc, func(t *testing.T) { - t.Parallel() - - balancer := New(&dynamic.Sticky{Cookie: &dynamic.Cookie{Name: "test"}}, false) - - for _, server := range test.servers { - balancer.Add(server, http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { - rw.WriteHeader(http.StatusOK) - _, _ = rw.Write([]byte(server)) - }), pointer(1), false) - } - - // Do it twice, to be sure it's not just the luck. - for range 2 { - req := httptest.NewRequest(http.MethodGet, "/", nil) - for _, cookie := range test.cookies { - req.AddCookie(cookie) - } - - recorder := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}, cookies: make(map[string]*http.Cookie)} - balancer.ServeHTTP(recorder, req) - - assert.Equal(t, test.expectedServer, recorder.Body.String()) - - assert.Len(t, recorder.cookies, len(test.expectedCookies)) - for _, cookie := range test.expectedCookies { - assert.Equal(t, cookie.Value, recorder.cookies[cookie.Name].Value) - } - } - }) - } -} diff --git a/pkg/server/service/service.go b/pkg/server/service/service.go index 245cb08f6..c44a0a7e5 100644 --- a/pkg/server/service/service.go +++ b/pkg/server/service/service.go @@ -29,6 +29,7 @@ import ( "github.com/traefik/traefik/v3/pkg/server/provider" "github.com/traefik/traefik/v3/pkg/server/service/loadbalancer/failover" "github.com/traefik/traefik/v3/pkg/server/service/loadbalancer/mirror" + "github.com/traefik/traefik/v3/pkg/server/service/loadbalancer/p2c" "github.com/traefik/traefik/v3/pkg/server/service/loadbalancer/wrr" "google.golang.org/grpc/status" ) @@ -304,6 +305,13 @@ func (m *Manager) getServiceHandler(ctx context.Context, service dynamic.WRRServ } } +type serverBalancer interface { + http.Handler + healthcheck.StatusSetter + + AddServer(name string, handler http.Handler, server dynamic.Server) +} + func (m *Manager) getLoadBalancerServiceHandler(ctx context.Context, serviceName string, info *runtime.ServiceInfo) (http.Handler, error) { service := info.LoadBalancer @@ -330,7 +338,18 @@ func (m *Manager) getLoadBalancerServiceHandler(ctx context.Context, serviceName passHostHeader = *service.PassHostHeader } - lb := wrr.New(service.Sticky, service.HealthCheck != nil) + var lb serverBalancer + switch service.Strategy { + // Here we are handling the empty value to comply with providers that are not applying defaults (e.g. REST provider) + // TODO: remove this when all providers apply default values. + case dynamic.BalancerStrategyWRR, "": + lb = wrr.New(service.Sticky, service.HealthCheck != nil) + case dynamic.BalancerStrategyP2C: + lb = p2c.New(service.Sticky, service.HealthCheck != nil) + default: + return nil, fmt.Errorf("unsupported load-balancer strategy %q", service.Strategy) + } + healthCheckTargets := make(map[string]*url.URL) for i, server := range shuffle(service.Servers, m.rand) { @@ -385,7 +404,7 @@ func (m *Manager) getLoadBalancerServiceHandler(ctx context.Context, serviceName proxy, _ = capture.Wrap(proxy) } - lb.Add(server.URL, proxy, server.Weight, server.Fenced) + lb.AddServer(server.URL, proxy, server) // servers are considered UP by default. info.UpdateServerStatus(target.String(), runtime.StatusUp) diff --git a/pkg/server/service/service_test.go b/pkg/server/service/service_test.go index 9628db7ff..48cef4c8b 100644 --- a/pkg/server/service/service_test.go +++ b/pkg/server/service/service_test.go @@ -38,6 +38,7 @@ func TestGetLoadBalancer(t *testing.T) { desc: "Fails when provided an invalid URL", serviceName: "test", service: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: ":", @@ -50,7 +51,9 @@ func TestGetLoadBalancer(t *testing.T) { { desc: "Succeeds when there are no servers", serviceName: "test", - service: &dynamic.ServersLoadBalancer{}, + service: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, + }, fwd: &forwarderMock{}, expectError: false, }, @@ -58,7 +61,8 @@ func TestGetLoadBalancer(t *testing.T) { desc: "Succeeds when sticky.cookie is set", serviceName: "test", service: &dynamic.ServersLoadBalancer{ - Sticky: &dynamic.Sticky{Cookie: &dynamic.Cookie{}}, + Strategy: dynamic.BalancerStrategyWRR, + Sticky: &dynamic.Sticky{Cookie: &dynamic.Cookie{}}, }, fwd: &forwarderMock{}, expectError: false, @@ -140,6 +144,7 @@ func TestGetLoadBalancerServiceHandler(t *testing.T) { desc: "Load balances between the two servers", serviceName: "test", service: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: boolPtr(true), Servers: []dynamic.Server{ { @@ -165,6 +170,7 @@ func TestGetLoadBalancerServiceHandler(t *testing.T) { desc: "StatusBadGateway when the server is not reachable", serviceName: "test", service: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: "http://foo", @@ -181,7 +187,8 @@ func TestGetLoadBalancerServiceHandler(t *testing.T) { desc: "ServiceUnavailable when no servers are available", serviceName: "test", service: &dynamic.ServersLoadBalancer{ - Servers: []dynamic.Server{}, + Strategy: dynamic.BalancerStrategyWRR, + Servers: []dynamic.Server{}, }, expected: []ExpectedResult{ { @@ -193,7 +200,8 @@ func TestGetLoadBalancerServiceHandler(t *testing.T) { desc: "Always call the same server when sticky.cookie is true", serviceName: "test", service: &dynamic.ServersLoadBalancer{ - Sticky: &dynamic.Sticky{Cookie: &dynamic.Cookie{}}, + Strategy: dynamic.BalancerStrategyWRR, + Sticky: &dynamic.Sticky{Cookie: &dynamic.Cookie{}}, Servers: []dynamic.Server{ { URL: server1.URL, @@ -216,7 +224,8 @@ func TestGetLoadBalancerServiceHandler(t *testing.T) { desc: "Sticky Cookie's options set correctly", serviceName: "test", service: &dynamic.ServersLoadBalancer{ - Sticky: &dynamic.Sticky{Cookie: &dynamic.Cookie{HTTPOnly: true, Secure: true}}, + Strategy: dynamic.BalancerStrategyWRR, + Sticky: &dynamic.Sticky{Cookie: &dynamic.Cookie{HTTPOnly: true, Secure: true}}, Servers: []dynamic.Server{ { URL: server1.URL, @@ -236,6 +245,7 @@ func TestGetLoadBalancerServiceHandler(t *testing.T) { desc: "PassHost passes the host instead of the IP", serviceName: "test", service: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Sticky: &dynamic.Sticky{Cookie: &dynamic.Cookie{}}, PassHostHeader: pointer(true), Servers: []dynamic.Server{ @@ -255,6 +265,7 @@ func TestGetLoadBalancerServiceHandler(t *testing.T) { desc: "PassHost doesn't pass the host instead of the IP", serviceName: "test", service: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, PassHostHeader: pointer(false), Sticky: &dynamic.Sticky{Cookie: &dynamic.Cookie{}}, Servers: []dynamic.Server{ @@ -274,6 +285,7 @@ func TestGetLoadBalancerServiceHandler(t *testing.T) { desc: "No user-agent", serviceName: "test", service: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: hasNoUserAgent.URL, @@ -291,6 +303,7 @@ func TestGetLoadBalancerServiceHandler(t *testing.T) { serviceName: "test", userAgent: "foobar", service: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: hasUserAgent.URL, @@ -379,6 +392,7 @@ func Test1xxResponses(t *testing.T) { info := &runtime.ServiceInfo{ Service: &dynamic.Service{ LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{ { URL: backend.URL, @@ -466,7 +480,9 @@ func TestManager_ServiceBuilders(t *testing.T) { manager := NewManager(map[string]*runtime.ServiceInfo{ "test@test": { Service: &dynamic.Service{ - LoadBalancer: &dynamic.ServersLoadBalancer{}, + LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, + }, }, }, }, nil, nil, &TransportManager{ @@ -505,7 +521,9 @@ func TestManager_Build(t *testing.T) { configs: map[string]*runtime.ServiceInfo{ "serviceName": { Service: &dynamic.Service{ - LoadBalancer: &dynamic.ServersLoadBalancer{}, + LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, + }, }, }, }, @@ -516,7 +534,9 @@ func TestManager_Build(t *testing.T) { configs: map[string]*runtime.ServiceInfo{ "serviceName@provider-1": { Service: &dynamic.Service{ - LoadBalancer: &dynamic.ServersLoadBalancer{}, + LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, + }, }, }, }, @@ -527,7 +547,9 @@ func TestManager_Build(t *testing.T) { configs: map[string]*runtime.ServiceInfo{ "serviceName@provider-1": { Service: &dynamic.Service{ - LoadBalancer: &dynamic.ServersLoadBalancer{}, + LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, + }, }, }, }, diff --git a/pkg/testhelpers/config.go b/pkg/testhelpers/config.go index 2bbea1956..ee3dbc975 100644 --- a/pkg/testhelpers/config.go +++ b/pkg/testhelpers/config.go @@ -70,6 +70,8 @@ func WithLoadBalancerServices(opts ...func(service *dynamic.ServersLoadBalancer) c.Services = make(map[string]*dynamic.Service) for _, opt := range opts { b := &dynamic.ServersLoadBalancer{} + b.SetDefaults() + name := opt(b) c.Services[name] = &dynamic.Service{ LoadBalancer: b,