Add p2c load-balancing strategy for servers load-balancer

Co-authored-by: Ian Ross <ifross@gmail.com>
Co-authored-by: Kevin Pollet <pollet.kevin@gmail.com>
This commit is contained in:
Romain 2025-03-10 12:12:04 +01:00 committed by GitHub
parent 550d96ea67
commit 9e029a84c4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
50 changed files with 1621 additions and 382 deletions

View File

@ -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.

View File

@ -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"

View File

@ -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]

View File

@ -80,6 +80,7 @@ http:
- url: foobar
weight: 42
preservePath: true
strategy: foobar
healthCheck:
scheme: foobar
mode: foobar

View File

@ -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:

View File

@ -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` |

View File

@ -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:

View File

@ -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:

View File

@ -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:

View File

@ -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.<service_name>.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.

View File

@ -461,6 +461,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.<service_name>.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>.`,

View File

@ -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.<service_name>.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.

View File

@ -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"

View File

@ -300,6 +300,14 @@ A Story of key & values
|---------------------------------------------------------------------------------|-------|
| `traefik/http/services/myservice/loadbalancer/responseforwarding/flushinterval` | `10` |
??? info "`traefik/http/services/<service_name>/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/<service_name>/mirroring/service`"
| Key (Path) | Value |

View File

@ -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.<service_name>.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.

View File

@ -467,6 +467,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.<service_name>.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>.`,

View File

@ -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

View File

@ -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:

View File

@ -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"

View File

@ -58,6 +58,7 @@
"url": "http://10.42.0.5:80"
}
],
"strategy": "wrr",
"passHostHeader": true,
"responseForwarding": {
"flushInterval": "100ms"

View File

@ -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"

View File

@ -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"

View File

@ -126,6 +126,7 @@
"url": "http://10.42.0.6:80"
}
],
"strategy": "wrr",
"passHostHeader": true,
"responseForwarding": {
"flushInterval": "100ms"

View File

@ -106,6 +106,7 @@
"url": "http://10.42.0.5:80"
}
],
"strategy": "wrr",
"passHostHeader": true,
"responseForwarding": {
"flushInterval": "100ms"

View File

@ -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"

View File

@ -106,6 +106,7 @@
"url": "http://10.42.0.5:80"
}
],
"strategy": "wrr",
"passHostHeader": true,
"responseForwarding": {
"flushInterval": "100ms"

View File

@ -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"

View File

@ -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"

View File

@ -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()
}

View File

@ -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",

View File

@ -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",

View File

@ -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",

View File

@ -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",

View File

@ -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) {

View File

@ -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{
{

View File

@ -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"`

View File

@ -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",

View File

@ -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{

View File

@ -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",

View File

@ -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{

View File

@ -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{},
},
},
},

View File

@ -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)
}
}

View File

@ -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
}

View File

@ -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]
}

View File

@ -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)
}

View File

@ -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]
}

View File

@ -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)
}
}
})
}
}

View File

@ -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)

View File

@ -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,
},
},
},
},

View File

@ -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,