WeightedRoundRobin load balancer

Co-authored-by: Ludovic Fernandez <ldez@users.noreply.github.com>
This commit is contained in:
Julien Salleyron 2019-08-26 10:30:05 +02:00 committed by Traefiker Bot
parent 84de444325
commit 6fed76a687
44 changed files with 1612 additions and 833 deletions

View file

@ -198,11 +198,13 @@ spec:
# "Parameter", etc, to support simpler forms of rule matching, but for now we # "Parameter", etc, to support simpler forms of rule matching, but for now we
# only support "Rule". # only support "Rule".
kind: Rule kind: Rule
# Priority disambiguates rules of the same length, for route matching. # (optional) Priority disambiguates rules of the same length, for route matching.
priority: 12 priority: 12
services: services:
- name: whoami - name: whoami
port: 80 port: 80
# (default 1) A weight used by the weighted round-robin strategy (WRR).
weight: 1
--- ---
apiVersion: traefik.containo.us/v1alpha1 apiVersion: traefik.containo.us/v1alpha1

View file

@ -130,10 +130,10 @@
- "traefik.http.services.service0.loadbalancer.healthcheck.timeout=foobar" - "traefik.http.services.service0.loadbalancer.healthcheck.timeout=foobar"
- "traefik.http.services.service0.loadbalancer.passhostheader=true" - "traefik.http.services.service0.loadbalancer.passhostheader=true"
- "traefik.http.services.service0.loadbalancer.responseforwarding.flushinterval=foobar" - "traefik.http.services.service0.loadbalancer.responseforwarding.flushinterval=foobar"
- "traefik.http.services.service0.loadbalancer.stickiness=true" - "traefik.http.services.service0.loadbalancer.sticky=true"
- "traefik.http.services.service0.loadbalancer.stickiness.cookiename=foobar" - "traefik.http.services.service0.loadbalancer.sticky.cookie.name=foobar"
- "traefik.http.services.service0.loadbalancer.stickiness.httponlycookie=true" - "traefik.http.services.service0.loadbalancer.sticky.cookie.httponly=true"
- "traefik.http.services.service0.loadbalancer.stickiness.securecookie=true" - "traefik.http.services.service0.loadbalancer.sticky.cookie.secure=true"
- "traefik.http.services.service0.loadbalancer.server.port=foobar" - "traefik.http.services.service0.loadbalancer.server.port=foobar"
- "traefik.http.services.service0.loadbalancer.server.scheme=foobar" - "traefik.http.services.service0.loadbalancer.server.scheme=foobar"
- "traefik.http.services.service1.loadbalancer.healthcheck.headers.name0=foobar" - "traefik.http.services.service1.loadbalancer.healthcheck.headers.name0=foobar"
@ -146,10 +146,10 @@
- "traefik.http.services.service1.loadbalancer.healthcheck.timeout=foobar" - "traefik.http.services.service1.loadbalancer.healthcheck.timeout=foobar"
- "traefik.http.services.service1.loadbalancer.passhostheader=true" - "traefik.http.services.service1.loadbalancer.passhostheader=true"
- "traefik.http.services.service1.loadbalancer.responseforwarding.flushinterval=foobar" - "traefik.http.services.service1.loadbalancer.responseforwarding.flushinterval=foobar"
- "traefik.http.services.service1.loadbalancer.stickiness=true" - "traefik.http.services.service1.loadbalancer.sticky=true"
- "traefik.http.services.service1.loadbalancer.stickiness.cookiename=foobar" - "traefik.http.services.service1.loadbalancer.sticky.cookie.name=foobar"
- "traefik.http.services.service1.loadbalancer.stickiness.httponlycookie=true" - "traefik.http.services.service1.loadbalancer.sticky.cookie.httponly=true"
- "traefik.http.services.service1.loadbalancer.stickiness.securecookie=true" - "traefik.http.services.service1.loadbalancer.sticky.cookie.secure=true"
- "traefik.http.services.service1.loadbalancer.server.port=foobar" - "traefik.http.services.service1.loadbalancer.server.port=foobar"
- "traefik.http.services.service1.loadbalancer.server.scheme=foobar" - "traefik.http.services.service1.loadbalancer.server.scheme=foobar"
- "traefik.tcp.routers.tcprouter0.entrypoints=foobar, foobar" - "traefik.tcp.routers.tcprouter0.entrypoints=foobar, foobar"

View file

@ -35,56 +35,47 @@
main = "foobar" main = "foobar"
sans = ["foobar", "foobar"] sans = ["foobar", "foobar"]
[http.services] [http.services]
[http.services.Service0] [http.services.Service01]
[http.services.Service0.loadBalancer] [http.services.Service01.loadBalancer]
passHostHeader = true passHostHeader = true
[http.services.Service0.loadBalancer.stickiness] [http.services.Service01.loadBalancer.sticky]
cookieName = "foobar" [http.services.Service01.loadBalancer.sticky.cookie]
secureCookie = true name = "foobar"
httpOnlyCookie = true secure = true
httpOnly = true
[[http.services.Service0.loadBalancer.servers]] [[http.services.Service01.loadBalancer.servers]]
url = "foobar" url = "foobar"
[[http.services.Service0.loadBalancer.servers]] [[http.services.Service01.loadBalancer.servers]]
url = "foobar" url = "foobar"
[http.services.Service0.loadBalancer.healthCheck] [http.services.Service01.loadBalancer.healthCheck]
scheme = "foobar" scheme = "foobar"
path = "foobar" path = "foobar"
port = 42 port = 42
interval = "foobar" interval = "foobar"
timeout = "foobar" timeout = "foobar"
hostname = "foobar" hostname = "foobar"
[http.services.Service0.loadBalancer.healthCheck.headers] [http.services.Service01.loadBalancer.healthCheck.headers]
name0 = "foobar" name0 = "foobar"
name1 = "foobar" name1 = "foobar"
[http.services.Service0.loadBalancer.responseForwarding] [http.services.Service01.loadBalancer.responseForwarding]
flushInterval = "foobar" flushInterval = "foobar"
[http.services.Service1] [http.services.Service02]
[http.services.Service1.loadBalancer] [http.services.Service02.weighted]
passHostHeader = true
[http.services.Service1.loadBalancer.stickiness]
cookieName = "foobar"
secureCookie = true
httpOnlyCookie = true
[[http.services.Service1.loadBalancer.servers]] [[http.services.Service02.weighted.services]]
url = "foobar" name = "foobar"
weight = 42
[[http.services.Service1.loadBalancer.servers]] [[http.services.Service02.weighted.services]]
url = "foobar" name = "foobar"
[http.services.Service1.loadBalancer.healthCheck] weight = 42
scheme = "foobar" [http.services.Service02.weighted.sticky]
path = "foobar" [http.services.Service02.weighted.sticky.cookie]
port = 42 name = "foobar"
interval = "foobar" secure = true
timeout = "foobar" httpOnly = true
hostname = "foobar"
[http.services.Service1.loadBalancer.healthCheck.headers]
name0 = "foobar"
name1 = "foobar"
[http.services.Service1.loadBalancer.responseForwarding]
flushInterval = "foobar"
[http.middlewares] [http.middlewares]
[http.middlewares.Middleware00] [http.middlewares.Middleware00]
[http.middlewares.Middleware00.addPrefix] [http.middlewares.Middleware00.addPrefix]

View file

@ -45,34 +45,13 @@ http:
- foobar - foobar
- foobar - foobar
services: services:
Service0: Service01:
loadBalancer: loadBalancer:
stickiness: sticky:
cookieName: foobar cookie:
secureCookie: true name: foobar
httpOnlyCookie: true secure: true
servers: httpOnly: true
- url: foobar
- url: foobar
healthCheck:
scheme: foobar
path: foobar
port: 42
interval: foobar
timeout: foobar
hostname: foobar
headers:
name0: foobar
name1: foobar
passHostHeader: true
responseForwarding:
flushInterval: foobar
Service1:
loadBalancer:
stickiness:
cookieName: foobar
secureCookie: true
httpOnlyCookie: true
servers: servers:
- url: foobar - url: foobar
- url: foobar - url: foobar
@ -89,6 +68,18 @@ http:
passHostHeader: true passHostHeader: true
responseForwarding: responseForwarding:
flushInterval: foobar flushInterval: foobar
Service02:
weighted:
services:
- name: foobar
weight: 42
- name: foobar
weight: 42
sticky:
cookie:
name: foobar
secure: true
httpOnly: true
middlewares: middlewares:
Middleware00: Middleware00:
addPrefix: addPrefix:

View file

@ -130,10 +130,10 @@
"traefik.http.services.service0.loadbalancer.healthcheck.timeout": "foobar", "traefik.http.services.service0.loadbalancer.healthcheck.timeout": "foobar",
"traefik.http.services.service0.loadbalancer.passhostheader": "true", "traefik.http.services.service0.loadbalancer.passhostheader": "true",
"traefik.http.services.service0.loadbalancer.responseforwarding.flushinterval": "foobar", "traefik.http.services.service0.loadbalancer.responseforwarding.flushinterval": "foobar",
"traefik.http.services.service0.loadbalancer.stickiness": "true", "traefik.http.services.service0.loadbalancer.sticky": "true",
"traefik.http.services.service0.loadbalancer.stickiness.cookiename": "foobar", "traefik.http.services.service0.loadbalancer.sticky.cookie.name": "foobar",
"traefik.http.services.service0.loadbalancer.stickiness.httponlycookie": "true", "traefik.http.services.service0.loadbalancer.sticky.cookie.httponly": "true",
"traefik.http.services.service0.loadbalancer.stickiness.securecookie": "true", "traefik.http.services.service0.loadbalancer.sticky.cookie.secure": "true",
"traefik.http.services.service0.loadbalancer.server.port": "foobar", "traefik.http.services.service0.loadbalancer.server.port": "foobar",
"traefik.http.services.service0.loadbalancer.server.scheme": "foobar", "traefik.http.services.service0.loadbalancer.server.scheme": "foobar",
"traefik.http.services.service1.loadbalancer.healthcheck.headers.name0": "foobar", "traefik.http.services.service1.loadbalancer.healthcheck.headers.name0": "foobar",
@ -146,10 +146,10 @@
"traefik.http.services.service1.loadbalancer.healthcheck.timeout": "foobar", "traefik.http.services.service1.loadbalancer.healthcheck.timeout": "foobar",
"traefik.http.services.service1.loadbalancer.passhostheader": "true", "traefik.http.services.service1.loadbalancer.passhostheader": "true",
"traefik.http.services.service1.loadbalancer.responseforwarding.flushinterval": "foobar", "traefik.http.services.service1.loadbalancer.responseforwarding.flushinterval": "foobar",
"traefik.http.services.service1.loadbalancer.stickiness": "true", "traefik.http.services.service1.loadbalancer.sticky": "true",
"traefik.http.services.service1.loadbalancer.stickiness.cookiename": "foobar", "traefik.http.services.service1.loadbalancer.sticky.cookie.name": "foobar",
"traefik.http.services.service1.loadbalancer.stickiness.httponlycookie": "true", "traefik.http.services.service1.loadbalancer.sticky.cookie.secure": "true",
"traefik.http.services.service1.loadbalancer.stickiness.securecookie": "true", "traefik.http.services.service1.loadbalancer.sticky.cookie.httponly": "true",
"traefik.http.services.service1.loadbalancer.server.port": "foobar", "traefik.http.services.service1.loadbalancer.server.port": "foobar",
"traefik.http.services.service1.loadbalancer.server.scheme": "foobar", "traefik.http.services.service1.loadbalancer.server.scheme": "foobar",
"traefik.tcp.routers.tcprouter0.entrypoints": "foobar, foobar", "traefik.tcp.routers.tcprouter0.entrypoints": "foobar, foobar",

View file

@ -54,13 +54,7 @@ The `Services` are responsible for configuring how to reach the actual services
## Configuring HTTP Services ## Configuring HTTP Services
### General ### Servers Load Balancer
Currently, `LoadBalancer` is the only supported kind of HTTP `Service` (see below).
However, since Traefik is an ever evolving project, other kind of HTTP Services will be available in the future,
reason why you have to specify it.
### Load Balancer
The load balancers are able to load balance the requests between multiple instances of your programs. The load balancers are able to load balance the requests between multiple instances of your programs.
@ -161,7 +155,7 @@ On subsequent requests, the client is forwarded to the same server.
```toml tab="TOML" ```toml tab="TOML"
[http.services] [http.services]
[http.services.my-service] [http.services.my-service]
[http.services.my-service.loadBalancer.stickiness] [http.services.my-service.loadBalancer.sticky.cookie]
``` ```
```yaml tab="YAML" ```yaml tab="YAML"
@ -169,18 +163,19 @@ On subsequent requests, the client is forwarded to the same server.
services: services:
my-service: my-service:
loadBalancer: loadBalancer:
stickiness: {} sticky:
cookie: {}
``` ```
??? example "Adding Stickiness with a Custom Cookie Name" ??? example "Adding Stickiness with custom Options"
```toml tab="TOML" ```toml tab="TOML"
[http.services] [http.services]
[http.services.my-service] [http.services.my-service]
[http.services.my-service.loadBalancer.stickiness] [http.services.my-service.loadBalancer.sticky.cookie]
cookieName = "my_stickiness_cookie_name" name = "my_sticky_cookie_name"
secureCookie = true secure = true
httpOnlyCookie = true httpOnly = true
``` ```
```yaml tab="YAML" ```yaml tab="YAML"
@ -188,10 +183,11 @@ On subsequent requests, the client is forwarded to the same server.
services: services:
my-service: my-service:
loadBalancer: loadBalancer:
stickiness: sticky:
cookieName: my_stickiness_cookie_name cookie:
secureCookie: true name: my_sticky_cookie_name
httpOnlyCookie: true secure: true
httpOnly: true
``` ```
#### Health Check #### Health Check
@ -306,6 +302,57 @@ Below are the available options for the health check mechanism:
My-Header: bar My-Header: bar
``` ```
### Weighted Round Robin (service)
The WRR is able to load balance the requests between multiple services based on weights.
This strategy is only available to load balance between [services](./index.md) and not between [servers](./index.md#servers).
This strategy can be defined only with [File](../../providers/file.md).
```toml tab="TOML"
[http.services]
[http.services.canary]
[[http.services.canary.weighted.services]]
name = "appv1"
weight = 3
[[http.services.canary.weighted.services]]
name = "appv2"
weight = 1
[http.services.appv1]
[http.services.appv1.loadBalancer]
[[http.services.appv1.loadBalancer.servers]]
url = "http://private-ip-server-1/"
[http.services.appv2]
[http.services.appv2.loadBalancer]
[[http.services.appv2.loadBalancer.servers]]
url = "http://private-ip-server-2/"
```
```yaml tab="YAML"
http:
services:
canary:
weighted:
services:
- name: appv1
weight: 3
- name: appv2
weight: 1
appv1:
loadBalancer:
servers:
- url: "http://private-ip-server-1/"
appv2:
loadBalancer:
servers:
- url: "http://private-ip-server-2/"
```
## Configuring TCP Services ## Configuring TCP Services
### General ### General

View file

@ -34,7 +34,6 @@
[tcp.services.whoami-no-cert] [tcp.services.whoami-no-cert]
[tcp.services.whoami-no-cert.loadBalancer] [tcp.services.whoami-no-cert.loadBalancer]
method = "wrr"
[[tcp.services.whoami-no-cert.loadBalancer.servers]] [[tcp.services.whoami-no-cert.loadBalancer.servers]]
address = "localhost:8083" address = "localhost:8083"

View file

@ -0,0 +1,38 @@
[global]
checkNewVersion = false
sendAnonymousUsage = false
[api]
[log]
level = "DEBUG"
[entryPoints]
[entryPoints.web]
address = ":8000"
[providers.file]
filename = "{{ .SelfFilename }}"
## dynamic configuration ##
[http.routers]
[http.routers.router]
service = "wrr"
rule = "Path(`/whoami`)"
[http.services]
[[http.services.wrr.weighted.services]]
name = "service1"
weight = 3
[[http.services.wrr.weighted.services]]
name = "service2"
[http.services.service1.loadBalancer]
[[http.services.service1.loadBalancer.servers]]
url = "{{ .Server1 }}"
[http.services.service2.loadBalancer]
[[http.services.service2.loadBalancer.servers]]
url = "{{ .Server2 }}"

View file

@ -0,0 +1,41 @@
[global]
checkNewVersion = false
sendAnonymousUsage = false
[api]
[log]
level = "DEBUG"
[entryPoints]
[entryPoints.web]
address = ":8000"
[providers.file]
filename = "{{ .SelfFilename }}"
## dynamic configuration ##
[http.routers]
[http.routers.router]
service = "wrr"
rule = "Path(`/whoami`)"
[http.services]
[http.services.wrr.weighted.sticky.cookie]
name = "test"
[[http.services.wrr.weighted.services]]
name = "service1"
weight = 3
[[http.services.wrr.weighted.services]]
name = "service2"
weight = 1
[http.services.service1.loadBalancer]
[[http.services.service1.loadBalancer.servers]]
url = "{{ .Server1 }}"
[http.services.service2.loadBalancer]
[[http.services.service2.loadBalancer.servers]]
url = "{{ .Server2 }}"

View file

@ -51,7 +51,7 @@ func (s *RestSuite) TestSimpleConfiguration(c *check.C) {
}, },
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"service1": { "service1": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://" + s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress + ":80", URL: "http://" + s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress + ":80",

View file

@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"os" "os"
@ -563,7 +564,7 @@ func (s *SimpleSuite) TestServiceConfigErrors(c *check.C) {
c.Assert(err, checker.IsNil) c.Assert(err, checker.IsNil)
defer cmd.Process.Kill() defer cmd.Process.Kill()
err = try.GetRequest("http://127.0.0.1:8080/api/http/services", 1000*time.Millisecond, try.BodyContains(`["the service \"service1@file\" doesn't have any load balancer"]`)) err = try.GetRequest("http://127.0.0.1:8080/api/http/services", 1000*time.Millisecond, try.BodyContains(`["the service \"service1@file\" does not have any type defined"]`))
c.Assert(err, checker.IsNil) c.Assert(err, checker.IsNil)
err = try.GetRequest("http://127.0.0.1:8080/api/http/services/service1@file", 1000*time.Millisecond, try.BodyContains(`"status":"disabled"`)) err = try.GetRequest("http://127.0.0.1:8080/api/http/services/service1@file", 1000*time.Millisecond, try.BodyContains(`"status":"disabled"`))
@ -572,3 +573,101 @@ func (s *SimpleSuite) TestServiceConfigErrors(c *check.C) {
err = try.GetRequest("http://127.0.0.1:8080/api/http/services/service2@file", 1000*time.Millisecond, try.BodyContains(`"status":"enabled"`)) err = try.GetRequest("http://127.0.0.1:8080/api/http/services/service2@file", 1000*time.Millisecond, try.BodyContains(`"status":"enabled"`))
c.Assert(err, checker.IsNil) c.Assert(err, checker.IsNil)
} }
func (s *SimpleSuite) TestWRR(c *check.C) {
s.createComposeProject(c, "base")
s.composeProject.Start(c)
server1 := s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress
server2 := s.composeProject.Container(c, "whoami2").NetworkSettings.IPAddress
file := s.adaptFile(c, "fixtures/wrr.toml", struct {
Server1 string
Server2 string
}{Server1: "http://" + server1, Server2: "http://" + server2})
defer os.Remove(file)
cmd, output := s.traefikCmd(withConfigFile(file))
defer output(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
err = try.GetRequest("http://127.0.0.1:8080/api/http/services", 1000*time.Millisecond, try.BodyContains("service1", "service2"))
c.Assert(err, checker.IsNil)
repartition := map[string]int{}
for i := 0; i < 4; i++ {
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/whoami", nil)
c.Assert(err, checker.IsNil)
response, err := http.DefaultClient.Do(req)
c.Assert(err, checker.IsNil)
c.Assert(response.StatusCode, checker.Equals, http.StatusOK)
body, err := ioutil.ReadAll(response.Body)
c.Assert(err, checker.IsNil)
if strings.Contains(string(body), server1) {
repartition[server1]++
}
if strings.Contains(string(body), server2) {
repartition[server2]++
}
}
c.Assert(repartition[server1], checker.Equals, 3)
c.Assert(repartition[server2], checker.Equals, 1)
}
func (s *SimpleSuite) TestWRRSticky(c *check.C) {
s.createComposeProject(c, "base")
s.composeProject.Start(c)
server1 := s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress
server2 := s.composeProject.Container(c, "whoami2").NetworkSettings.IPAddress
file := s.adaptFile(c, "fixtures/wrr_sticky.toml", struct {
Server1 string
Server2 string
}{Server1: "http://" + server1, Server2: "http://" + server2})
defer os.Remove(file)
cmd, output := s.traefikCmd(withConfigFile(file))
defer output(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
err = try.GetRequest("http://127.0.0.1:8080/api/http/services", 1000*time.Millisecond, try.BodyContains("service1", "service2"))
c.Assert(err, checker.IsNil)
repartition := map[string]int{}
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/whoami", nil)
c.Assert(err, checker.IsNil)
for i := 0; i < 4; i++ {
response, err := http.DefaultClient.Do(req)
c.Assert(err, checker.IsNil)
c.Assert(response.StatusCode, checker.Equals, http.StatusOK)
for _, cookie := range response.Cookies() {
req.AddCookie(cookie)
}
body, err := ioutil.ReadAll(response.Body)
c.Assert(err, checker.IsNil)
if strings.Contains(string(body), server1) {
repartition[server1]++
}
if strings.Contains(string(body), server2) {
repartition[server2]++
}
}
c.Assert(repartition[server1], checker.Equals, 4)
c.Assert(repartition[server2], checker.Equals, 0)
}

View file

@ -203,7 +203,7 @@ func TestHandler_HTTP(t *testing.T) {
"bar@myprovider": func() *runtime.ServiceInfo { "bar@myprovider": func() *runtime.ServiceInfo {
si := &runtime.ServiceInfo{ si := &runtime.ServiceInfo{
Service: &dynamic.Service{ Service: &dynamic.Service{
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1", URL: "http://127.0.0.1",
@ -219,7 +219,7 @@ func TestHandler_HTTP(t *testing.T) {
"baz@myprovider": func() *runtime.ServiceInfo { "baz@myprovider": func() *runtime.ServiceInfo {
si := &runtime.ServiceInfo{ si := &runtime.ServiceInfo{
Service: &dynamic.Service{ Service: &dynamic.Service{
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.2", URL: "http://127.0.0.2",
@ -248,7 +248,7 @@ func TestHandler_HTTP(t *testing.T) {
"bar@myprovider": func() *runtime.ServiceInfo { "bar@myprovider": func() *runtime.ServiceInfo {
si := &runtime.ServiceInfo{ si := &runtime.ServiceInfo{
Service: &dynamic.Service{ Service: &dynamic.Service{
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1", URL: "http://127.0.0.1",
@ -264,7 +264,7 @@ func TestHandler_HTTP(t *testing.T) {
"baz@myprovider": func() *runtime.ServiceInfo { "baz@myprovider": func() *runtime.ServiceInfo {
si := &runtime.ServiceInfo{ si := &runtime.ServiceInfo{
Service: &dynamic.Service{ Service: &dynamic.Service{
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.2", URL: "http://127.0.0.2",
@ -280,7 +280,7 @@ func TestHandler_HTTP(t *testing.T) {
"test@myprovider": func() *runtime.ServiceInfo { "test@myprovider": func() *runtime.ServiceInfo {
si := &runtime.ServiceInfo{ si := &runtime.ServiceInfo{
Service: &dynamic.Service{ Service: &dynamic.Service{
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.3", URL: "http://127.0.0.3",
@ -309,7 +309,7 @@ func TestHandler_HTTP(t *testing.T) {
"bar@myprovider": func() *runtime.ServiceInfo { "bar@myprovider": func() *runtime.ServiceInfo {
si := &runtime.ServiceInfo{ si := &runtime.ServiceInfo{
Service: &dynamic.Service{ Service: &dynamic.Service{
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1", URL: "http://127.0.0.1",
@ -337,7 +337,7 @@ func TestHandler_HTTP(t *testing.T) {
"bar@myprovider": func() *runtime.ServiceInfo { "bar@myprovider": func() *runtime.ServiceInfo {
si := &runtime.ServiceInfo{ si := &runtime.ServiceInfo{
Service: &dynamic.Service{ Service: &dynamic.Service{
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1", URL: "http://127.0.0.1",

View file

@ -55,7 +55,7 @@ func TestHandler_Overview(t *testing.T) {
Services: map[string]*runtime.ServiceInfo{ Services: map[string]*runtime.ServiceInfo{
"foo-service@myprovider": { "foo-service@myprovider": {
Service: &dynamic.Service{ Service: &dynamic.Service{
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{{URL: "http://127.0.0.1"}}, Servers: []dynamic.Server{{URL: "http://127.0.0.1"}},
}, },
}, },
@ -63,7 +63,7 @@ func TestHandler_Overview(t *testing.T) {
}, },
"bar-service@myprovider": { "bar-service@myprovider": {
Service: &dynamic.Service{ Service: &dynamic.Service{
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{{URL: "http://127.0.0.1"}}, Servers: []dynamic.Server{{URL: "http://127.0.0.1"}},
}, },
}, },
@ -71,7 +71,7 @@ func TestHandler_Overview(t *testing.T) {
}, },
"fii-service@myprovider": { "fii-service@myprovider": {
Service: &dynamic.Service{ Service: &dynamic.Service{
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{{URL: "http://127.0.0.1"}}, Servers: []dynamic.Server{{URL: "http://127.0.0.1"}},
}, },
}, },

View file

@ -37,7 +37,7 @@ func TestHandler_RawData(t *testing.T) {
Services: map[string]*runtime.ServiceInfo{ Services: map[string]*runtime.ServiceInfo{
"foo-service@myprovider": { "foo-service@myprovider": {
Service: &dynamic.Service{ Service: &dynamic.Service{
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1", URL: "http://127.0.0.1",

View file

@ -404,8 +404,8 @@
[http.services.Service0] [http.services.Service0]
[http.services.Service0.loadBalancer] [http.services.Service0.loadBalancer]
passHostHeader = true passHostHeader = true
[http.services.Service0.loadBalancer.stickiness] [http.services.Service0.loadBalancer.sticky.cookie]
cookieName = "foobar" name = "foobar"
[[http.services.Service0.loadBalancer.servers]] [[http.services.Service0.loadBalancer.servers]]
url = "foobar" url = "foobar"

View file

@ -19,7 +19,8 @@ type HTTPConfiguration struct {
// Service holds a service configuration (can only be of one type at the same time). // Service holds a service configuration (can only be of one type at the same time).
type Service struct { type Service struct {
LoadBalancer *LoadBalancerService `json:"loadBalancer,omitempty" toml:"loadBalancer,omitempty" yaml:"loadBalancer,omitempty"` LoadBalancer *ServersLoadBalancer `json:"loadBalancer,omitempty" toml:"loadBalancer,omitempty" yaml:"loadBalancer,omitempty"`
Weighted *WeightedRoundRobin `json:"weighted,omitempty" toml:"weighted,omitempty" yaml:"weighted,omitempty" label:"-"`
} }
// +k8s:deepcopy-gen=true // +k8s:deepcopy-gen=true
@ -45,9 +46,47 @@ type RouterTLSConfig struct {
// +k8s:deepcopy-gen=true // +k8s:deepcopy-gen=true
// LoadBalancerService holds the LoadBalancerService configuration. // WeightedRoundRobin is a weighted round robin load-balancer of services.
type LoadBalancerService struct { type WeightedRoundRobin struct {
Stickiness *Stickiness `json:"stickiness,omitempty" toml:"stickiness,omitempty" yaml:"stickiness,omitempty" label:"allowEmpty"` Services []WRRService `json:"services,omitempty" toml:"services,omitempty" yaml:"services,omitempty"`
Sticky *Sticky `json:"sticky,omitempty" toml:"sticky,omitempty" yaml:"sticky,omitempty"`
}
// +k8s:deepcopy-gen=true
// WRRService is a reference to a service load-balanced with weighted round robin.
type WRRService struct {
Name string `json:"name,omitempty" toml:"name,omitempty" yaml:"name,omitempty"`
Weight *int `json:"weight,omitempty" toml:"weight,omitempty" yaml:"weight,omitempty"`
}
// SetDefaults Default values for a ServersLoadBalancer.
func (w *WRRService) SetDefaults() {
defaultWeight := 1
w.Weight = &defaultWeight
}
// +k8s:deepcopy-gen=true
// Sticky holds the sticky configuration.
type Sticky struct {
Cookie *Cookie `json:"cookie,omitempty" toml:"cookie,omitempty" yaml:"cookie,omitempty"`
}
// +k8s:deepcopy-gen=true
// Cookie holds the sticky configuration based on cookie.
type Cookie struct {
Name string `json:"name,omitempty" toml:"name,omitempty" yaml:"name,omitempty"`
Secure bool `json:"secure,omitempty" toml:"secure,omitempty" yaml:"secure,omitempty"`
HTTPOnly bool `json:"httpOnly,omitempty" toml:"httpOnly,omitempty" yaml:"httpOnly,omitempty"`
}
// +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"`
Servers []Server `json:"servers,omitempty" toml:"servers,omitempty" yaml:"servers,omitempty" label-slice-as-struct:"server"` Servers []Server `json:"servers,omitempty" toml:"servers,omitempty" yaml:"servers,omitempty" label-slice-as-struct:"server"`
HealthCheck *HealthCheck `json:"healthCheck,omitempty" toml:"healthCheck,omitempty" yaml:"healthCheck,omitempty"` HealthCheck *HealthCheck `json:"healthCheck,omitempty" toml:"healthCheck,omitempty" yaml:"healthCheck,omitempty"`
PassHostHeader bool `json:"passHostHeader" toml:"passHostHeader" yaml:"passHostHeader"` PassHostHeader bool `json:"passHostHeader" toml:"passHostHeader" yaml:"passHostHeader"`
@ -55,7 +94,7 @@ type LoadBalancerService struct {
} }
// Mergeable tells if the given service is mergeable. // Mergeable tells if the given service is mergeable.
func (l *LoadBalancerService) Mergeable(loadBalancer *LoadBalancerService) bool { func (l *ServersLoadBalancer) Mergeable(loadBalancer *ServersLoadBalancer) bool {
savedServers := l.Servers savedServers := l.Servers
defer func() { defer func() {
l.Servers = savedServers l.Servers = savedServers
@ -71,8 +110,8 @@ func (l *LoadBalancerService) Mergeable(loadBalancer *LoadBalancerService) bool
return reflect.DeepEqual(l, loadBalancer) return reflect.DeepEqual(l, loadBalancer)
} }
// SetDefaults Default values for a LoadBalancerService. // SetDefaults Default values for a ServersLoadBalancer.
func (l *LoadBalancerService) SetDefaults() { func (l *ServersLoadBalancer) SetDefaults() {
l.PassHostHeader = true l.PassHostHeader = true
} }
@ -85,15 +124,6 @@ type ResponseForwarding struct {
// +k8s:deepcopy-gen=true // +k8s:deepcopy-gen=true
// Stickiness holds the stickiness configuration.
type Stickiness struct {
CookieName string `json:"cookieName,omitempty" toml:"cookieName,omitempty" yaml:"cookieName,omitempty"`
SecureCookie bool `json:"secureCookie,omitempty" toml:"secureCookie,omitempty" yaml:"secureCookie,omitempty"`
HTTPOnlyCookie bool `json:"httpOnlyCookie,omitempty" toml:"httpOnlyCookie,omitempty" yaml:"httpOnlyCookie,omitempty"`
}
// +k8s:deepcopy-gen=true
// Server holds the server configuration. // Server holds the server configuration.
type Server struct { type Server struct {
URL string `json:"url,omitempty" toml:"url,omitempty" yaml:"url,omitempty" label:"-"` URL string `json:"url,omitempty" toml:"url,omitempty" yaml:"url,omitempty" label:"-"`

View file

@ -45,7 +45,7 @@ type RouterTCPTLSConfig struct {
// TCPLoadBalancerService holds the LoadBalancerService configuration. // TCPLoadBalancerService holds the LoadBalancerService configuration.
type TCPLoadBalancerService struct { type TCPLoadBalancerService struct {
Servers []TCPServer `json:"servers,omitempty" toml:"servers,omitempty" yaml:"servers,omitempty" label-slice-as-struct:"server" label-slice-as-struct:"server"` Servers []TCPServer `json:"servers,omitempty" toml:"servers,omitempty" yaml:"servers,omitempty" label-slice-as-struct:"server"`
} }
// Mergeable tells if the given service is mergeable. // Mergeable tells if the given service is mergeable.

View file

@ -247,6 +247,22 @@ func (in Configurations) DeepCopy() Configurations {
return *out return *out
} }
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Cookie) DeepCopyInto(out *Cookie) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Cookie.
func (in *Cookie) DeepCopy() *Cookie {
if in == nil {
return nil
}
out := new(Cookie)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DigestAuth) DeepCopyInto(out *DigestAuth) { func (in *DigestAuth) DeepCopyInto(out *DigestAuth) {
*out = *in *out = *in
@ -508,42 +524,6 @@ func (in *IPWhiteList) DeepCopy() *IPWhiteList {
return out return out
} }
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *LoadBalancerService) DeepCopyInto(out *LoadBalancerService) {
*out = *in
if in.Stickiness != nil {
in, out := &in.Stickiness, &out.Stickiness
*out = new(Stickiness)
**out = **in
}
if in.Servers != nil {
in, out := &in.Servers, &out.Servers
*out = make([]Server, len(*in))
copy(*out, *in)
}
if in.HealthCheck != nil {
in, out := &in.HealthCheck, &out.HealthCheck
*out = new(HealthCheck)
(*in).DeepCopyInto(*out)
}
if in.ResponseForwarding != nil {
in, out := &in.ResponseForwarding, &out.ResponseForwarding
*out = new(ResponseForwarding)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LoadBalancerService.
func (in *LoadBalancerService) DeepCopy() *LoadBalancerService {
if in == nil {
return nil
}
out := new(LoadBalancerService)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *MaxConn) DeepCopyInto(out *MaxConn) { func (in *MaxConn) DeepCopyInto(out *MaxConn) {
*out = *in *out = *in
@ -954,12 +934,53 @@ func (in *Server) DeepCopy() *Server {
return out return out
} }
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ServersLoadBalancer) DeepCopyInto(out *ServersLoadBalancer) {
*out = *in
if in.Sticky != nil {
in, out := &in.Sticky, &out.Sticky
*out = new(Sticky)
(*in).DeepCopyInto(*out)
}
if in.Servers != nil {
in, out := &in.Servers, &out.Servers
*out = make([]Server, len(*in))
copy(*out, *in)
}
if in.HealthCheck != nil {
in, out := &in.HealthCheck, &out.HealthCheck
*out = new(HealthCheck)
(*in).DeepCopyInto(*out)
}
if in.ResponseForwarding != nil {
in, out := &in.ResponseForwarding, &out.ResponseForwarding
*out = new(ResponseForwarding)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServersLoadBalancer.
func (in *ServersLoadBalancer) DeepCopy() *ServersLoadBalancer {
if in == nil {
return nil
}
out := new(ServersLoadBalancer)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Service) DeepCopyInto(out *Service) { func (in *Service) DeepCopyInto(out *Service) {
*out = *in *out = *in
if in.LoadBalancer != nil { if in.LoadBalancer != nil {
in, out := &in.LoadBalancer, &out.LoadBalancer in, out := &in.LoadBalancer, &out.LoadBalancer
*out = new(LoadBalancerService) *out = new(ServersLoadBalancer)
(*in).DeepCopyInto(*out)
}
if in.Weighted != nil {
in, out := &in.Weighted, &out.Weighted
*out = new(WeightedRoundRobin)
(*in).DeepCopyInto(*out) (*in).DeepCopyInto(*out)
} }
return return
@ -976,17 +997,22 @@ func (in *Service) DeepCopy() *Service {
} }
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Stickiness) DeepCopyInto(out *Stickiness) { func (in *Sticky) DeepCopyInto(out *Sticky) {
*out = *in *out = *in
if in.Cookie != nil {
in, out := &in.Cookie, &out.Cookie
*out = new(Cookie)
**out = **in
}
return return
} }
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Stickiness. // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Sticky.
func (in *Stickiness) DeepCopy() *Stickiness { func (in *Sticky) DeepCopy() *Sticky {
if in == nil { if in == nil {
return nil return nil
} }
out := new(Stickiness) out := new(Sticky)
in.DeepCopyInto(out) in.DeepCopyInto(out)
return out return out
} }
@ -1265,3 +1291,45 @@ func (in Users) DeepCopy() Users {
in.DeepCopyInto(out) in.DeepCopyInto(out)
return *out return *out
} }
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WRRService) DeepCopyInto(out *WRRService) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WRRService.
func (in *WRRService) DeepCopy() *WRRService {
if in == nil {
return nil
}
out := new(WRRService)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WeightedRoundRobin) DeepCopyInto(out *WeightedRoundRobin) {
*out = *in
if in.Services != nil {
in, out := &in.Services, &out.Services
*out = make([]WRRService, len(*in))
copy(*out, *in)
}
if in.Sticky != nil {
in, out := &in.Sticky, &out.Sticky
*out = new(Sticky)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WeightedRoundRobin.
func (in *WeightedRoundRobin) DeepCopy() *WeightedRoundRobin {
if in == nil {
return nil
}
out := new(WeightedRoundRobin)
in.DeepCopyInto(out)
return out
}

View file

@ -396,8 +396,8 @@
[http.services.Service0] [http.services.Service0]
[http.services.Service0.loadBalancer] [http.services.Service0.loadBalancer]
passHostHeader = true passHostHeader = true
[http.services.Service0.loadBalancer.stickiness] [http.services.Service0.loadBalancer.sticky.cookie]
cookieName = "foobar" name = "foobar"
[[http.services.Service0.loadBalancer.servers]] [[http.services.Service0.loadBalancer.servers]]
url = "foobar" url = "foobar"

View file

@ -142,8 +142,8 @@ func TestDecodeConfiguration(t *testing.T) {
"traefik.http.services.Service0.loadbalancer.responseforwarding.flushinterval": "foobar", "traefik.http.services.Service0.loadbalancer.responseforwarding.flushinterval": "foobar",
"traefik.http.services.Service0.loadbalancer.server.scheme": "foobar", "traefik.http.services.Service0.loadbalancer.server.scheme": "foobar",
"traefik.http.services.Service0.loadbalancer.server.port": "8080", "traefik.http.services.Service0.loadbalancer.server.port": "8080",
"traefik.http.services.Service0.loadbalancer.stickiness.cookiename": "foobar", "traefik.http.services.Service0.loadbalancer.sticky.cookie.name": "foobar",
"traefik.http.services.Service0.loadbalancer.stickiness.securecookie": "true", "traefik.http.services.Service0.loadbalancer.sticky.cookie.secure": "true",
"traefik.http.services.Service1.loadbalancer.healthcheck.headers.name0": "foobar", "traefik.http.services.Service1.loadbalancer.healthcheck.headers.name0": "foobar",
"traefik.http.services.Service1.loadbalancer.healthcheck.headers.name1": "foobar", "traefik.http.services.Service1.loadbalancer.healthcheck.headers.name1": "foobar",
"traefik.http.services.Service1.loadbalancer.healthcheck.hostname": "foobar", "traefik.http.services.Service1.loadbalancer.healthcheck.hostname": "foobar",
@ -156,8 +156,8 @@ func TestDecodeConfiguration(t *testing.T) {
"traefik.http.services.Service1.loadbalancer.responseforwarding.flushinterval": "foobar", "traefik.http.services.Service1.loadbalancer.responseforwarding.flushinterval": "foobar",
"traefik.http.services.Service1.loadbalancer.server.scheme": "foobar", "traefik.http.services.Service1.loadbalancer.server.scheme": "foobar",
"traefik.http.services.Service1.loadbalancer.server.port": "8080", "traefik.http.services.Service1.loadbalancer.server.port": "8080",
"traefik.http.services.Service1.loadbalancer.stickiness": "false", "traefik.http.services.Service1.loadbalancer.sticky": "false",
"traefik.http.services.Service1.loadbalancer.stickiness.cookiename": "fui", "traefik.http.services.Service1.loadbalancer.sticky.cookie.name": "fui",
"traefik.tcp.routers.Router0.rule": "foobar", "traefik.tcp.routers.Router0.rule": "foobar",
"traefik.tcp.routers.Router0.entrypoints": "foobar, fiibar", "traefik.tcp.routers.Router0.entrypoints": "foobar, fiibar",
"traefik.tcp.routers.Router0.service": "foobar", "traefik.tcp.routers.Router0.service": "foobar",
@ -510,11 +510,13 @@ func TestDecodeConfiguration(t *testing.T) {
}, },
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Service0": { "Service0": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Stickiness: &dynamic.Stickiness{ Sticky: &dynamic.Sticky{
CookieName: "foobar", Cookie: &dynamic.Cookie{
SecureCookie: true, Name: "foobar",
HTTPOnlyCookie: false, Secure: true,
HTTPOnly: false,
},
}, },
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
@ -541,7 +543,7 @@ func TestDecodeConfiguration(t *testing.T) {
}, },
}, },
"Service1": { "Service1": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
Scheme: "foobar", Scheme: "foobar",
@ -908,10 +910,12 @@ func TestEncodeConfiguration(t *testing.T) {
}, },
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Service0": { "Service0": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Stickiness: &dynamic.Stickiness{ Sticky: &dynamic.Sticky{
CookieName: "foobar", Cookie: &dynamic.Cookie{
HTTPOnlyCookie: true, Name: "foobar",
HTTPOnly: true,
},
}, },
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
@ -938,7 +942,7 @@ func TestEncodeConfiguration(t *testing.T) {
}, },
}, },
"Service1": { "Service1": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
Scheme: "foobar", Scheme: "foobar",
@ -1101,9 +1105,9 @@ func TestEncodeConfiguration(t *testing.T) {
"traefik.HTTP.Services.Service0.LoadBalancer.ResponseForwarding.FlushInterval": "foobar", "traefik.HTTP.Services.Service0.LoadBalancer.ResponseForwarding.FlushInterval": "foobar",
"traefik.HTTP.Services.Service0.LoadBalancer.server.Port": "8080", "traefik.HTTP.Services.Service0.LoadBalancer.server.Port": "8080",
"traefik.HTTP.Services.Service0.LoadBalancer.server.Scheme": "foobar", "traefik.HTTP.Services.Service0.LoadBalancer.server.Scheme": "foobar",
"traefik.HTTP.Services.Service0.LoadBalancer.Stickiness.CookieName": "foobar", "traefik.HTTP.Services.Service0.LoadBalancer.Sticky.Cookie.Name": "foobar",
"traefik.HTTP.Services.Service0.LoadBalancer.Stickiness.HTTPOnlyCookie": "true", "traefik.HTTP.Services.Service0.LoadBalancer.Sticky.Cookie.HTTPOnly": "true",
"traefik.HTTP.Services.Service0.LoadBalancer.Stickiness.SecureCookie": "false", "traefik.HTTP.Services.Service0.LoadBalancer.Sticky.Cookie.Secure": "false",
"traefik.HTTP.Services.Service1.LoadBalancer.HealthCheck.Headers.name0": "foobar", "traefik.HTTP.Services.Service1.LoadBalancer.HealthCheck.Headers.name0": "foobar",
"traefik.HTTP.Services.Service1.LoadBalancer.HealthCheck.Headers.name1": "foobar", "traefik.HTTP.Services.Service1.LoadBalancer.HealthCheck.Headers.name1": "foobar",
"traefik.HTTP.Services.Service1.LoadBalancer.HealthCheck.Hostname": "foobar", "traefik.HTTP.Services.Service1.LoadBalancer.HealthCheck.Hostname": "foobar",

View file

@ -43,7 +43,7 @@ func TestPopulateUsedBy(t *testing.T) {
Services: map[string]*runtime.ServiceInfo{ Services: map[string]*runtime.ServiceInfo{
"foo-service@myprovider": { "foo-service@myprovider": {
Service: &dynamic.Service{ Service: &dynamic.Service{
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{URL: "http://127.0.0.1:8085"}, {URL: "http://127.0.0.1:8085"},
{URL: "http://127.0.0.1:8086"}, {URL: "http://127.0.0.1:8086"},
@ -75,7 +75,7 @@ func TestPopulateUsedBy(t *testing.T) {
Services: map[string]*runtime.ServiceInfo{ Services: map[string]*runtime.ServiceInfo{
"foo-service@myprovider": { "foo-service@myprovider": {
Service: &dynamic.Service{ Service: &dynamic.Service{
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{URL: "http://127.0.0.1"}, {URL: "http://127.0.0.1"},
}, },
@ -149,7 +149,7 @@ func TestPopulateUsedBy(t *testing.T) {
Services: map[string]*runtime.ServiceInfo{ Services: map[string]*runtime.ServiceInfo{
"foo-service@myprovider": { "foo-service@myprovider": {
Service: &dynamic.Service{ Service: &dynamic.Service{
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1:8085", URL: "http://127.0.0.1:8085",
@ -167,7 +167,7 @@ func TestPopulateUsedBy(t *testing.T) {
}, },
"bar-service@myprovider": { "bar-service@myprovider": {
Service: &dynamic.Service{ Service: &dynamic.Service{
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1:8087", URL: "http://127.0.0.1:8087",
@ -222,7 +222,7 @@ func TestPopulateUsedBy(t *testing.T) {
Services: map[string]*runtime.ServiceInfo{ Services: map[string]*runtime.ServiceInfo{
"foo-service@myprovider": { "foo-service@myprovider": {
Service: &dynamic.Service{ Service: &dynamic.Service{
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1", URL: "http://127.0.0.1",
@ -293,7 +293,7 @@ func TestPopulateUsedBy(t *testing.T) {
Services: map[string]*runtime.ServiceInfo{ Services: map[string]*runtime.ServiceInfo{
"foo-service@myprovider": { "foo-service@myprovider": {
Service: &dynamic.Service{ Service: &dynamic.Service{
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1", URL: "http://127.0.0.1",
@ -337,7 +337,7 @@ func TestPopulateUsedBy(t *testing.T) {
Services: map[string]*runtime.ServiceInfo{ Services: map[string]*runtime.ServiceInfo{
"foo-service@myprovider": { "foo-service@myprovider": {
Service: &dynamic.Service{ Service: &dynamic.Service{
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1", URL: "http://127.0.0.1",
@ -389,7 +389,7 @@ func TestPopulateUsedBy(t *testing.T) {
Services: map[string]*runtime.ServiceInfo{ Services: map[string]*runtime.ServiceInfo{
"foo-service@myprovider": { "foo-service@myprovider": {
Service: &dynamic.Service{ Service: &dynamic.Service{
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1", URL: "http://127.0.0.1",

View file

@ -99,7 +99,7 @@ func (p *Provider) buildServiceConfiguration(ctx context.Context, container dock
if len(configuration.Services) == 0 { if len(configuration.Services) == 0 {
configuration.Services = make(map[string]*dynamic.Service) configuration.Services = make(map[string]*dynamic.Service)
lb := &dynamic.LoadBalancerService{} lb := &dynamic.ServersLoadBalancer{}
lb.SetDefaults() lb.SetDefaults()
configuration.Services[serviceName] = &dynamic.Service{ configuration.Services[serviceName] = &dynamic.Service{
LoadBalancer: lb, LoadBalancer: lb,
@ -171,7 +171,7 @@ func (p *Provider) addServerTCP(ctx context.Context, container dockerData, loadB
return nil return nil
} }
func (p *Provider) addServer(ctx context.Context, container dockerData, loadBalancer *dynamic.LoadBalancerService) error { func (p *Provider) addServer(ctx context.Context, container dockerData, loadBalancer *dynamic.ServersLoadBalancer) error {
serverPort := getLBServerPort(loadBalancer) serverPort := getLBServerPort(loadBalancer)
ip, port, err := p.getIPPort(ctx, container, serverPort) ip, port, err := p.getIPPort(ctx, container, serverPort)
if err != nil { if err != nil {
@ -291,7 +291,7 @@ func (p *Provider) getPortBinding(container dockerData, serverPort string) (*nat
return nil, fmt.Errorf("unable to find the external IP:Port for the container %q", container.Name) return nil, fmt.Errorf("unable to find the external IP:Port for the container %q", container.Name)
} }
func getLBServerPort(loadBalancer *dynamic.LoadBalancerService) string { func getLBServerPort(loadBalancer *dynamic.ServersLoadBalancer) string {
if loadBalancer != nil && len(loadBalancer.Servers) > 0 { if loadBalancer != nil && len(loadBalancer.Servers) > 0 {
return loadBalancer.Servers[0].Port return loadBalancer.Servers[0].Port
} }

View file

@ -56,7 +56,7 @@ func TestDefaultRule(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Test": { "Test": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1:80", URL: "http://127.0.0.1:80",
@ -105,7 +105,7 @@ func TestDefaultRule(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Test": { "Test": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1:80", URL: "http://127.0.0.1:80",
@ -156,7 +156,7 @@ func TestDefaultRule(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Test": { "Test": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1:80", URL: "http://127.0.0.1:80",
@ -200,7 +200,7 @@ func TestDefaultRule(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Test": { "Test": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1:80", URL: "http://127.0.0.1:80",
@ -244,7 +244,7 @@ func TestDefaultRule(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Test": { "Test": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1:80", URL: "http://127.0.0.1:80",
@ -293,7 +293,7 @@ func TestDefaultRule(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Test": { "Test": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1:80", URL: "http://127.0.0.1:80",
@ -376,7 +376,7 @@ func Test_buildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Test": { "Test": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1:80", URL: "http://127.0.0.1:80",
@ -444,7 +444,7 @@ func Test_buildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Test": { "Test": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1:80", URL: "http://127.0.0.1:80",
@ -454,7 +454,7 @@ func Test_buildConfiguration(t *testing.T) {
}, },
}, },
"Test2": { "Test2": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.2:80", URL: "http://127.0.0.2:80",
@ -520,7 +520,7 @@ func Test_buildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Test": { "Test": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1:80", URL: "http://127.0.0.1:80",
@ -573,7 +573,7 @@ func Test_buildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Service1": { "Service1": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1:80", URL: "http://127.0.0.1:80",
@ -625,7 +625,7 @@ func Test_buildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Service1": { "Service1": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1:80", URL: "http://127.0.0.1:80",
@ -669,7 +669,7 @@ func Test_buildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Test": { "Test": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1:80", URL: "http://127.0.0.1:80",
@ -726,7 +726,7 @@ func Test_buildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Service1": { "Service1": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1:80", URL: "http://127.0.0.1:80",
@ -773,7 +773,7 @@ func Test_buildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Service1": { "Service1": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1:80", URL: "http://127.0.0.1:80",
@ -783,7 +783,7 @@ func Test_buildConfiguration(t *testing.T) {
}, },
}, },
"Service2": { "Service2": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1:80", URL: "http://127.0.0.1:80",
@ -990,7 +990,7 @@ func Test_buildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Service1": { "Service1": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1:80", URL: "http://127.0.0.1:80",
@ -1042,7 +1042,7 @@ func Test_buildConfiguration(t *testing.T) {
}, },
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Test": { "Test": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1:80", URL: "http://127.0.0.1:80",
@ -1127,7 +1127,7 @@ func Test_buildConfiguration(t *testing.T) {
}, },
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Test": { "Test": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1:80", URL: "http://127.0.0.1:80",
@ -1200,7 +1200,7 @@ func Test_buildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Test": { "Test": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1:80", URL: "http://127.0.0.1:80",
@ -1292,7 +1292,7 @@ func Test_buildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Test": { "Test": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1:80", URL: "http://127.0.0.1:80",
@ -1363,7 +1363,7 @@ func Test_buildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Test": { "Test": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1:80", URL: "http://127.0.0.1:80",
@ -1450,7 +1450,7 @@ func Test_buildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Test": { "Test": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1:80", URL: "http://127.0.0.1:80",
@ -1526,7 +1526,7 @@ func Test_buildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Test": { "Test": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1:80", URL: "http://127.0.0.1:80",
@ -1592,7 +1592,7 @@ func Test_buildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Test": { "Test": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1:80", URL: "http://127.0.0.1:80",
@ -1602,7 +1602,7 @@ func Test_buildConfiguration(t *testing.T) {
}, },
}, },
"Test2": { "Test2": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.2:80", URL: "http://127.0.0.2:80",
@ -1652,7 +1652,7 @@ func Test_buildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Test": { "Test": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1:80", URL: "http://127.0.0.1:80",
@ -1703,7 +1703,7 @@ func Test_buildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Service1": { "Service1": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "h2c://127.0.0.1:8080", URL: "h2c://127.0.0.1:8080",
@ -1749,7 +1749,7 @@ func Test_buildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Service1": { "Service1": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1:80", URL: "http://127.0.0.1:80",
@ -1759,7 +1759,7 @@ func Test_buildConfiguration(t *testing.T) {
}, },
}, },
"Service2": { "Service2": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1:8080", URL: "http://127.0.0.1:8080",
@ -1974,7 +1974,7 @@ func Test_buildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Test": { "Test": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1:80", URL: "http://127.0.0.1:80",
@ -2035,7 +2035,7 @@ func Test_buildConfiguration(t *testing.T) {
}, },
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Test": { "Test": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1:80", URL: "http://127.0.0.1:80",
@ -2278,7 +2278,7 @@ func Test_buildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Service1": { "Service1": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1:80", URL: "http://127.0.0.1:80",

View file

@ -1,6 +1,6 @@
http: http:
{{ range $i, $e := until 20 }}
routers: routers:
{{ range $i, $e := until 20 }}
router{{ $e }}: router{{ $e }}:
service: application-1 service: application-1
{{ end }} {{ end }}

View file

@ -0,0 +1,22 @@
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: test.route
namespace: default
spec:
entryPoints:
- web
routes:
- match: Host(`foo.com`) && PathPrefix(`/foo`)
kind: Rule
priority: 12
services:
- name: whoami
port: 80
weight: 10
- name: whoami2
port: 8080
weight: 0

View file

@ -3,7 +3,6 @@ package crd
import ( import (
"context" "context"
"crypto/sha256" "crypto/sha256"
"errors"
"fmt" "fmt"
"os" "os"
"reflect" "reflect"
@ -16,7 +15,6 @@ import (
"github.com/containous/traefik/v2/pkg/config/dynamic" "github.com/containous/traefik/v2/pkg/config/dynamic"
"github.com/containous/traefik/v2/pkg/job" "github.com/containous/traefik/v2/pkg/job"
"github.com/containous/traefik/v2/pkg/log" "github.com/containous/traefik/v2/pkg/log"
"github.com/containous/traefik/v2/pkg/provider/kubernetes/crd/traefik/v1alpha1"
"github.com/containous/traefik/v2/pkg/safe" "github.com/containous/traefik/v2/pkg/safe"
"github.com/containous/traefik/v2/pkg/tls" "github.com/containous/traefik/v2/pkg/tls"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
@ -136,159 +134,22 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
return nil return nil
} }
func checkStringQuoteValidity(value string) error { func (p *Provider) loadConfigurationFromCRD(ctx context.Context, client Client) *dynamic.Configuration {
_, err := strconv.Unquote(`"` + value + `"`) tlsConfigs := make(map[string]*tls.CertAndStores)
return err conf := &dynamic.Configuration{
HTTP: p.loadIngressRouteConfiguration(ctx, client, tlsConfigs),
TCP: p.loadIngressRouteTCPConfiguration(ctx, client, tlsConfigs),
TLS: &dynamic.TLSConfiguration{
Certificates: getTLSConfig(tlsConfigs),
Options: buildTLSOptions(ctx, client),
},
} }
func loadTCPServers(client Client, namespace string, svc v1alpha1.ServiceTCP) ([]dynamic.TCPServer, error) { for _, middleware := range client.GetMiddlewares() {
service, exists, err := client.GetService(namespace, svc.Name) conf.HTTP.Middlewares[makeID(middleware.Namespace, middleware.Name)] = &middleware.Spec
if err != nil {
return nil, err
} }
if !exists { return conf
return nil, errors.New("service not found")
}
var portSpec *corev1.ServicePort
for _, p := range service.Spec.Ports {
if svc.Port == p.Port {
portSpec = &p
break
}
}
if portSpec == nil {
return nil, errors.New("service port not found")
}
var servers []dynamic.TCPServer
if service.Spec.Type == corev1.ServiceTypeExternalName {
servers = append(servers, dynamic.TCPServer{
Address: fmt.Sprintf("%s:%d", service.Spec.ExternalName, portSpec.Port),
})
} else {
endpoints, endpointsExists, endpointsErr := client.GetEndpoints(namespace, svc.Name)
if endpointsErr != nil {
return nil, endpointsErr
}
if !endpointsExists {
return nil, errors.New("endpoints not found")
}
if len(endpoints.Subsets) == 0 {
return nil, errors.New("subset not found")
}
var port int32
for _, subset := range endpoints.Subsets {
for _, p := range subset.Ports {
if portSpec.Name == p.Name {
port = p.Port
break
}
}
if port == 0 {
return nil, errors.New("cannot define a port")
}
for _, addr := range subset.Addresses {
servers = append(servers, dynamic.TCPServer{
Address: fmt.Sprintf("%s:%d", addr.IP, port),
})
}
}
}
return servers, nil
}
func loadServers(client Client, namespace string, svc v1alpha1.Service) ([]dynamic.Server, error) {
strategy := svc.Strategy
if strategy == "" {
strategy = "RoundRobin"
}
if strategy != "RoundRobin" {
return nil, fmt.Errorf("load balancing strategy %v is not supported", strategy)
}
service, exists, err := client.GetService(namespace, svc.Name)
if err != nil {
return nil, err
}
if !exists {
return nil, errors.New("service not found")
}
var portSpec *corev1.ServicePort
for _, p := range service.Spec.Ports {
if svc.Port == p.Port {
portSpec = &p
break
}
}
if portSpec == nil {
return nil, errors.New("service port not found")
}
var servers []dynamic.Server
if service.Spec.Type == corev1.ServiceTypeExternalName {
servers = append(servers, dynamic.Server{
URL: fmt.Sprintf("http://%s:%d", service.Spec.ExternalName, portSpec.Port),
})
} else {
endpoints, endpointsExists, endpointsErr := client.GetEndpoints(namespace, svc.Name)
if endpointsErr != nil {
return nil, endpointsErr
}
if !endpointsExists {
return nil, errors.New("endpoints not found")
}
if len(endpoints.Subsets) == 0 {
return nil, errors.New("subset not found")
}
var port int32
for _, subset := range endpoints.Subsets {
for _, p := range subset.Ports {
if portSpec.Name == p.Name {
port = p.Port
break
}
}
if port == 0 {
return nil, errors.New("cannot define a port")
}
protocol := "http"
switch svc.Scheme {
case "http", "https", "h2c":
protocol = svc.Scheme
case "":
if portSpec.Port == 443 || strings.HasPrefix(portSpec.Name, "https") {
protocol = "https"
}
default:
return nil, fmt.Errorf("invalid scheme %q specified", svc.Scheme)
}
for _, addr := range subset.Addresses {
servers = append(servers, dynamic.Server{
URL: fmt.Sprintf("%s://%s:%d", protocol, addr.IP, port),
})
}
}
}
return servers, nil
} }
func buildTLSOptions(ctx context.Context, client Client) map[string]tls.Options { func buildTLSOptions(ctx context.Context, client Client) map[string]tls.Options {
@ -338,250 +199,9 @@ func buildTLSOptions(ctx context.Context, client Client) map[string]tls.Options
return tlsOptions return tlsOptions
} }
func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Client, tlsConfigs map[string]*tls.CertAndStores) *dynamic.HTTPConfiguration { func checkStringQuoteValidity(value string) error {
conf := &dynamic.HTTPConfiguration{ _, err := strconv.Unquote(`"` + value + `"`)
Routers: map[string]*dynamic.Router{}, return err
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
}
for _, ingressRoute := range client.GetIngressRoutes() {
logger := log.FromContext(log.With(ctx, log.Str("ingress", ingressRoute.Name), log.Str("namespace", ingressRoute.Namespace)))
// TODO keep the name ingressClass?
if !shouldProcessIngress(p.IngressClass, ingressRoute.Annotations[annotationKubernetesIngressClass]) {
continue
}
err := getTLSHTTP(ctx, ingressRoute, client, tlsConfigs)
if err != nil {
logger.Errorf("Error configuring TLS: %v", err)
}
ingressName := ingressRoute.Name
if len(ingressName) == 0 {
ingressName = ingressRoute.GenerateName
}
for _, route := range ingressRoute.Spec.Routes {
if route.Kind != "Rule" {
logger.Errorf("Unsupported match kind: %s. Only \"Rule\" is supported for now.", route.Kind)
continue
}
if len(route.Match) == 0 {
logger.Errorf("Empty match rule")
continue
}
if err := checkStringQuoteValidity(route.Match); err != nil {
logger.Errorf("Invalid syntax for match rule: %s", route.Match)
continue
}
var allServers []dynamic.Server
for _, service := range route.Services {
servers, err := loadServers(client, ingressRoute.Namespace, service)
if err != nil {
logger.
WithField("serviceName", service.Name).
WithField("servicePort", service.Port).
Errorf("Cannot create service: %v", err)
continue
}
allServers = append(allServers, servers...)
}
var mds []string
for _, mi := range route.Middlewares {
if strings.Contains(mi.Name, "@") {
if len(mi.Namespace) > 0 {
logger.
WithField(log.MiddlewareName, mi.Name).
Warnf("namespace %q is ignored in cross-provider context", mi.Namespace)
}
mds = append(mds, mi.Name)
continue
}
ns := mi.Namespace
if len(ns) == 0 {
ns = ingressRoute.Namespace
}
mds = append(mds, makeID(ns, mi.Name))
}
key, err := makeServiceKey(route.Match, ingressName)
if err != nil {
logger.Error(err)
continue
}
serviceName := makeID(ingressRoute.Namespace, key)
conf.Routers[serviceName] = &dynamic.Router{
Middlewares: mds,
Priority: route.Priority,
EntryPoints: ingressRoute.Spec.EntryPoints,
Rule: route.Match,
Service: serviceName,
}
if ingressRoute.Spec.TLS != nil {
tlsConf := &dynamic.RouterTLSConfig{
CertResolver: ingressRoute.Spec.TLS.CertResolver,
}
if ingressRoute.Spec.TLS.Options != nil && len(ingressRoute.Spec.TLS.Options.Name) > 0 {
tlsOptionsName := ingressRoute.Spec.TLS.Options.Name
// Is a Kubernetes CRD reference, (i.e. not a cross-provider reference)
ns := ingressRoute.Spec.TLS.Options.Namespace
if !strings.Contains(tlsOptionsName, "@") {
if len(ns) == 0 {
ns = ingressRoute.Namespace
}
tlsOptionsName = makeID(ns, tlsOptionsName)
} else if len(ns) > 0 {
logger.
WithField("TLSoptions", ingressRoute.Spec.TLS.Options.Name).
Warnf("namespace %q is ignored in cross-provider context", ns)
}
tlsConf.Options = tlsOptionsName
}
conf.Routers[serviceName].TLS = tlsConf
}
conf.Services[serviceName] = &dynamic.Service{
LoadBalancer: &dynamic.LoadBalancerService{
Servers: allServers,
// TODO: support other strategies.
PassHostHeader: true,
},
}
}
}
return conf
}
func (p *Provider) loadIngressRouteTCPConfiguration(ctx context.Context, client Client, tlsConfigs map[string]*tls.CertAndStores) *dynamic.TCPConfiguration {
conf := &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Services: map[string]*dynamic.TCPService{},
}
for _, ingressRouteTCP := range client.GetIngressRouteTCPs() {
logger := log.FromContext(log.With(ctx, log.Str("ingress", ingressRouteTCP.Name), log.Str("namespace", ingressRouteTCP.Namespace)))
if !shouldProcessIngress(p.IngressClass, ingressRouteTCP.Annotations[annotationKubernetesIngressClass]) {
continue
}
if ingressRouteTCP.Spec.TLS != nil && !ingressRouteTCP.Spec.TLS.Passthrough {
err := getTLSTCP(ctx, ingressRouteTCP, client, tlsConfigs)
if err != nil {
logger.Errorf("Error configuring TLS: %v", err)
}
}
ingressName := ingressRouteTCP.Name
if len(ingressName) == 0 {
ingressName = ingressRouteTCP.GenerateName
}
for _, route := range ingressRouteTCP.Spec.Routes {
if len(route.Match) == 0 {
logger.Errorf("Empty match rule")
continue
}
if err := checkStringQuoteValidity(route.Match); err != nil {
logger.Errorf("Invalid syntax for match rule: %s", route.Match)
continue
}
var allServers []dynamic.TCPServer
for _, service := range route.Services {
servers, err := loadTCPServers(client, ingressRouteTCP.Namespace, service)
if err != nil {
logger.
WithField("serviceName", service.Name).
WithField("servicePort", service.Port).
Errorf("Cannot create service: %v", err)
continue
}
allServers = append(allServers, servers...)
}
key, e := makeServiceKey(route.Match, ingressName)
if e != nil {
logger.Error(e)
continue
}
serviceName := makeID(ingressRouteTCP.Namespace, key)
conf.Routers[serviceName] = &dynamic.TCPRouter{
EntryPoints: ingressRouteTCP.Spec.EntryPoints,
Rule: route.Match,
Service: serviceName,
}
if ingressRouteTCP.Spec.TLS != nil {
conf.Routers[serviceName].TLS = &dynamic.RouterTCPTLSConfig{
Passthrough: ingressRouteTCP.Spec.TLS.Passthrough,
CertResolver: ingressRouteTCP.Spec.TLS.CertResolver,
}
if ingressRouteTCP.Spec.TLS.Options != nil && len(ingressRouteTCP.Spec.TLS.Options.Name) > 0 {
tlsOptionsName := ingressRouteTCP.Spec.TLS.Options.Name
// Is a Kubernetes CRD reference (i.e. not a cross-provider reference)
ns := ingressRouteTCP.Spec.TLS.Options.Namespace
if !strings.Contains(tlsOptionsName, "@") {
if len(ns) == 0 {
ns = ingressRouteTCP.Namespace
}
tlsOptionsName = makeID(ns, tlsOptionsName)
} else if len(ns) > 0 {
logger.
WithField("TLSoptions", ingressRouteTCP.Spec.TLS.Options.Name).
Warnf("namespace %q is ignored in cross-provider context", ns)
}
conf.Routers[serviceName].TLS.Options = tlsOptionsName
}
}
conf.Services[serviceName] = &dynamic.TCPService{
LoadBalancer: &dynamic.TCPLoadBalancerService{
Servers: allServers,
},
}
}
}
return conf
}
func (p *Provider) loadConfigurationFromCRD(ctx context.Context, client Client) *dynamic.Configuration {
tlsConfigs := make(map[string]*tls.CertAndStores)
conf := &dynamic.Configuration{
HTTP: p.loadIngressRouteConfiguration(ctx, client, tlsConfigs),
TCP: p.loadIngressRouteTCPConfiguration(ctx, client, tlsConfigs),
TLS: &dynamic.TLSConfiguration{
Certificates: getTLSConfig(tlsConfigs),
Options: buildTLSOptions(ctx, client),
},
}
for _, middleware := range client.GetMiddlewares() {
conf.HTTP.Middlewares[makeID(middleware.Namespace, middleware.Name)] = &middleware.Spec
}
return conf
} }
func makeServiceKey(rule, ingressName string) (string, error) { func makeServiceKey(rule, ingressName string) (string, error) {
@ -608,50 +228,6 @@ func shouldProcessIngress(ingressClass string, ingressClassAnnotation string) bo
(len(ingressClass) == 0 && ingressClassAnnotation == traefikDefaultIngressClass) (len(ingressClass) == 0 && ingressClassAnnotation == traefikDefaultIngressClass)
} }
func getTLSHTTP(ctx context.Context, ingressRoute *v1alpha1.IngressRoute, k8sClient Client, tlsConfigs map[string]*tls.CertAndStores) error {
if ingressRoute.Spec.TLS == nil {
return nil
}
if ingressRoute.Spec.TLS.SecretName == "" {
log.FromContext(ctx).Debugf("Skipping TLS sub-section: No secret name provided")
return nil
}
configKey := ingressRoute.Namespace + "/" + ingressRoute.Spec.TLS.SecretName
if _, tlsExists := tlsConfigs[configKey]; !tlsExists {
tlsConf, err := getTLS(k8sClient, ingressRoute.Spec.TLS.SecretName, ingressRoute.Namespace)
if err != nil {
return err
}
tlsConfigs[configKey] = tlsConf
}
return nil
}
func getTLSTCP(ctx context.Context, ingressRoute *v1alpha1.IngressRouteTCP, k8sClient Client, tlsConfigs map[string]*tls.CertAndStores) error {
if ingressRoute.Spec.TLS == nil {
return nil
}
if ingressRoute.Spec.TLS.SecretName == "" {
log.FromContext(ctx).Debugf("Skipping TLS sub-section for TCP: No secret name provided")
return nil
}
configKey := ingressRoute.Namespace + "/" + ingressRoute.Spec.TLS.SecretName
if _, tlsExists := tlsConfigs[configKey]; !tlsExists {
tlsConf, err := getTLS(k8sClient, ingressRoute.Spec.TLS.SecretName, ingressRoute.Namespace)
if err != nil {
return err
}
tlsConfigs[configKey] = tlsConf
}
return nil
}
func getTLS(k8sClient Client, secretName, namespace string) (*tls.CertAndStores, error) { func getTLS(k8sClient Client, secretName, namespace string) (*tls.CertAndStores, error) {
secret, exists, err := k8sClient.GetSecret(namespace, secretName) secret, exists, err := k8sClient.GetSecret(namespace, secretName)
if err != nil { if err != nil {

View file

@ -0,0 +1,274 @@
package crd
import (
"context"
"errors"
"fmt"
"strings"
"github.com/containous/traefik/v2/pkg/config/dynamic"
"github.com/containous/traefik/v2/pkg/log"
"github.com/containous/traefik/v2/pkg/provider/kubernetes/crd/traefik/v1alpha1"
"github.com/containous/traefik/v2/pkg/tls"
corev1 "k8s.io/api/core/v1"
)
func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Client, tlsConfigs map[string]*tls.CertAndStores) *dynamic.HTTPConfiguration {
conf := &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
}
for _, ingressRoute := range client.GetIngressRoutes() {
ctxRt := log.With(ctx, log.Str("ingress", ingressRoute.Name), log.Str("namespace", ingressRoute.Namespace))
logger := log.FromContext(ctxRt)
// TODO keep the name ingressClass?
if !shouldProcessIngress(p.IngressClass, ingressRoute.Annotations[annotationKubernetesIngressClass]) {
continue
}
err := getTLSHTTP(ctx, ingressRoute, client, tlsConfigs)
if err != nil {
logger.Errorf("Error configuring TLS: %v", err)
}
ingressName := ingressRoute.Name
if len(ingressName) == 0 {
ingressName = ingressRoute.GenerateName
}
for _, route := range ingressRoute.Spec.Routes {
if route.Kind != "Rule" {
logger.Errorf("Unsupported match kind: %s. Only \"Rule\" is supported for now.", route.Kind)
continue
}
if len(route.Match) == 0 {
logger.Errorf("Empty match rule")
continue
}
if err := checkStringQuoteValidity(route.Match); err != nil {
logger.Errorf("Invalid syntax for match rule: %s", route.Match)
continue
}
key, err := makeServiceKey(route.Match, ingressName)
if err != nil {
logger.Error(err)
continue
}
serviceName := makeID(ingressRoute.Namespace, key)
for _, service := range route.Services {
balancerServerHTTP, err := createLoadBalancerServerHTTP(client, ingressRoute, service)
if err != nil {
logger.
WithField("serviceName", service.Name).
WithField("servicePort", service.Port).
Errorf("Cannot create service: %v", err)
continue
}
if len(route.Services) == 1 {
conf.Services[serviceName] = balancerServerHTTP
break
}
serviceKey := fmt.Sprintf("%s-%s-%d", serviceName, service.Name, service.Port)
conf.Services[serviceKey] = balancerServerHTTP
srv := dynamic.WRRService{Name: serviceKey}
srv.SetDefaults()
if service.Weight != nil {
srv.Weight = service.Weight
}
if conf.Services[serviceName] == nil {
conf.Services[serviceName] = &dynamic.Service{Weighted: &dynamic.WeightedRoundRobin{}}
}
conf.Services[serviceName].Weighted.Services = append(conf.Services[serviceName].Weighted.Services, srv)
}
var mds []string
for _, mi := range route.Middlewares {
if strings.Contains(mi.Name, "@") {
if len(mi.Namespace) > 0 {
logger.
WithField(log.MiddlewareName, mi.Name).
Warnf("namespace %q is ignored in cross-provider context", mi.Namespace)
}
mds = append(mds, mi.Name)
continue
}
ns := mi.Namespace
if len(ns) == 0 {
ns = ingressRoute.Namespace
}
mds = append(mds, makeID(ns, mi.Name))
}
conf.Routers[serviceName] = &dynamic.Router{
Middlewares: mds,
Priority: route.Priority,
EntryPoints: ingressRoute.Spec.EntryPoints,
Rule: route.Match,
Service: serviceName,
}
if ingressRoute.Spec.TLS != nil {
tlsConf := &dynamic.RouterTLSConfig{
CertResolver: ingressRoute.Spec.TLS.CertResolver,
}
if ingressRoute.Spec.TLS.Options != nil && len(ingressRoute.Spec.TLS.Options.Name) > 0 {
tlsOptionsName := ingressRoute.Spec.TLS.Options.Name
// Is a Kubernetes CRD reference, (i.e. not a cross-provider reference)
ns := ingressRoute.Spec.TLS.Options.Namespace
if !strings.Contains(tlsOptionsName, "@") {
if len(ns) == 0 {
ns = ingressRoute.Namespace
}
tlsOptionsName = makeID(ns, tlsOptionsName)
} else if len(ns) > 0 {
logger.
WithField("TLSoptions", ingressRoute.Spec.TLS.Options.Name).
Warnf("namespace %q is ignored in cross-provider context", ns)
}
tlsConf.Options = tlsOptionsName
}
conf.Routers[serviceName].TLS = tlsConf
}
}
}
return conf
}
func createLoadBalancerServerHTTP(client Client, ingressRoute *v1alpha1.IngressRoute, service v1alpha1.Service) (*dynamic.Service, error) {
servers, err := loadServers(client, ingressRoute.Namespace, service)
if err != nil {
return nil, err
}
return &dynamic.Service{
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: servers,
// TODO: support other strategies.
PassHostHeader: true,
},
}, nil
}
func loadServers(client Client, namespace string, svc v1alpha1.Service) ([]dynamic.Server, error) {
strategy := svc.Strategy
if strategy == "" {
strategy = "RoundRobin"
}
if strategy != "RoundRobin" {
return nil, fmt.Errorf("load balancing strategy %v is not supported", strategy)
}
service, exists, err := client.GetService(namespace, svc.Name)
if err != nil {
return nil, err
}
if !exists {
return nil, errors.New("service not found")
}
var portSpec *corev1.ServicePort
for _, p := range service.Spec.Ports {
if svc.Port == p.Port {
portSpec = &p
break
}
}
if portSpec == nil {
return nil, errors.New("service port not found")
}
var servers []dynamic.Server
if service.Spec.Type == corev1.ServiceTypeExternalName {
servers = append(servers, dynamic.Server{
URL: fmt.Sprintf("http://%s:%d", service.Spec.ExternalName, portSpec.Port),
})
} else {
endpoints, endpointsExists, endpointsErr := client.GetEndpoints(namespace, svc.Name)
if endpointsErr != nil {
return nil, endpointsErr
}
if !endpointsExists {
return nil, errors.New("endpoints not found")
}
if len(endpoints.Subsets) == 0 {
return nil, errors.New("subset not found")
}
var port int32
for _, subset := range endpoints.Subsets {
for _, p := range subset.Ports {
if portSpec.Name == p.Name {
port = p.Port
break
}
}
if port == 0 {
return nil, errors.New("cannot define a port")
}
protocol := "http"
switch svc.Scheme {
case "http", "https", "h2c":
protocol = svc.Scheme
case "":
if portSpec.Port == 443 || strings.HasPrefix(portSpec.Name, "https") {
protocol = "https"
}
default:
return nil, fmt.Errorf("invalid scheme %q specified", svc.Scheme)
}
for _, addr := range subset.Addresses {
servers = append(servers, dynamic.Server{
URL: fmt.Sprintf("%s://%s:%d", protocol, addr.IP, port),
})
}
}
}
return servers, nil
}
func getTLSHTTP(ctx context.Context, ingressRoute *v1alpha1.IngressRoute, k8sClient Client, tlsConfigs map[string]*tls.CertAndStores) error {
if ingressRoute.Spec.TLS == nil {
return nil
}
if ingressRoute.Spec.TLS.SecretName == "" {
log.FromContext(ctx).Debugf("No secret name provided")
return nil
}
configKey := ingressRoute.Namespace + "/" + ingressRoute.Spec.TLS.SecretName
if _, tlsExists := tlsConfigs[configKey]; !tlsExists {
tlsConf, err := getTLS(k8sClient, ingressRoute.Spec.TLS.SecretName, ingressRoute.Namespace)
if err != nil {
return err
}
tlsConfigs[configKey] = tlsConf
}
return nil
}

View file

@ -0,0 +1,201 @@
package crd
import (
"context"
"errors"
"fmt"
"strings"
"github.com/containous/traefik/v2/pkg/config/dynamic"
"github.com/containous/traefik/v2/pkg/log"
"github.com/containous/traefik/v2/pkg/provider/kubernetes/crd/traefik/v1alpha1"
"github.com/containous/traefik/v2/pkg/tls"
corev1 "k8s.io/api/core/v1"
)
func (p *Provider) loadIngressRouteTCPConfiguration(ctx context.Context, client Client, tlsConfigs map[string]*tls.CertAndStores) *dynamic.TCPConfiguration {
conf := &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Services: map[string]*dynamic.TCPService{},
}
for _, ingressRouteTCP := range client.GetIngressRouteTCPs() {
logger := log.FromContext(log.With(ctx, log.Str("ingress", ingressRouteTCP.Name), log.Str("namespace", ingressRouteTCP.Namespace)))
if !shouldProcessIngress(p.IngressClass, ingressRouteTCP.Annotations[annotationKubernetesIngressClass]) {
continue
}
if ingressRouteTCP.Spec.TLS != nil && !ingressRouteTCP.Spec.TLS.Passthrough {
err := getTLSTCP(ctx, ingressRouteTCP, client, tlsConfigs)
if err != nil {
logger.Errorf("Error configuring TLS: %v", err)
}
}
ingressName := ingressRouteTCP.Name
if len(ingressName) == 0 {
ingressName = ingressRouteTCP.GenerateName
}
for _, route := range ingressRouteTCP.Spec.Routes {
if len(route.Match) == 0 {
logger.Errorf("Empty match rule")
continue
}
if err := checkStringQuoteValidity(route.Match); err != nil {
logger.Errorf("Invalid syntax for match rule: %s", route.Match)
continue
}
var allServers []dynamic.TCPServer
for _, service := range route.Services {
servers, err := loadTCPServers(client, ingressRouteTCP.Namespace, service)
if err != nil {
logger.
WithField("serviceName", service.Name).
WithField("servicePort", service.Port).
Errorf("Cannot create service: %v", err)
continue
}
allServers = append(allServers, servers...)
}
key, e := makeServiceKey(route.Match, ingressName)
if e != nil {
logger.Error(e)
continue
}
serviceName := makeID(ingressRouteTCP.Namespace, key)
conf.Routers[serviceName] = &dynamic.TCPRouter{
EntryPoints: ingressRouteTCP.Spec.EntryPoints,
Rule: route.Match,
Service: serviceName,
}
if ingressRouteTCP.Spec.TLS != nil {
conf.Routers[serviceName].TLS = &dynamic.RouterTCPTLSConfig{
Passthrough: ingressRouteTCP.Spec.TLS.Passthrough,
CertResolver: ingressRouteTCP.Spec.TLS.CertResolver,
}
if ingressRouteTCP.Spec.TLS.Options != nil && len(ingressRouteTCP.Spec.TLS.Options.Name) > 0 {
tlsOptionsName := ingressRouteTCP.Spec.TLS.Options.Name
// Is a Kubernetes CRD reference (i.e. not a cross-provider reference)
ns := ingressRouteTCP.Spec.TLS.Options.Namespace
if !strings.Contains(tlsOptionsName, "@") {
if len(ns) == 0 {
ns = ingressRouteTCP.Namespace
}
tlsOptionsName = makeID(ns, tlsOptionsName)
} else if len(ns) > 0 {
logger.
WithField("TLSoptions", ingressRouteTCP.Spec.TLS.Options.Name).
Warnf("namespace %q is ignored in cross-provider context", ns)
}
conf.Routers[serviceName].TLS.Options = tlsOptionsName
}
}
conf.Services[serviceName] = &dynamic.TCPService{
LoadBalancer: &dynamic.TCPLoadBalancerService{
Servers: allServers,
},
}
}
}
return conf
}
func loadTCPServers(client Client, namespace string, svc v1alpha1.ServiceTCP) ([]dynamic.TCPServer, error) {
service, exists, err := client.GetService(namespace, svc.Name)
if err != nil {
return nil, err
}
if !exists {
return nil, errors.New("service not found")
}
var portSpec *corev1.ServicePort
for _, p := range service.Spec.Ports {
if svc.Port == p.Port {
portSpec = &p
break
}
}
if portSpec == nil {
return nil, errors.New("service port not found")
}
var servers []dynamic.TCPServer
if service.Spec.Type == corev1.ServiceTypeExternalName {
servers = append(servers, dynamic.TCPServer{
Address: fmt.Sprintf("%s:%d", service.Spec.ExternalName, portSpec.Port),
})
} else {
endpoints, endpointsExists, endpointsErr := client.GetEndpoints(namespace, svc.Name)
if endpointsErr != nil {
return nil, endpointsErr
}
if !endpointsExists {
return nil, errors.New("endpoints not found")
}
if len(endpoints.Subsets) == 0 {
return nil, errors.New("subset not found")
}
var port int32
for _, subset := range endpoints.Subsets {
for _, p := range subset.Ports {
if portSpec.Name == p.Name {
port = p.Port
break
}
}
if port == 0 {
return nil, errors.New("cannot define a port")
}
for _, addr := range subset.Addresses {
servers = append(servers, dynamic.TCPServer{
Address: fmt.Sprintf("%s:%d", addr.IP, port),
})
}
}
}
return servers, nil
}
func getTLSTCP(ctx context.Context, ingressRoute *v1alpha1.IngressRouteTCP, k8sClient Client, tlsConfigs map[string]*tls.CertAndStores) error {
if ingressRoute.Spec.TLS == nil {
return nil
}
if ingressRoute.Spec.TLS.SecretName == "" {
log.FromContext(ctx).Debugf("No secret name provided")
return nil
}
configKey := ingressRoute.Namespace + "/" + ingressRoute.Spec.TLS.SecretName
if _, tlsExists := tlsConfigs[configKey]; !tlsExists {
tlsConf, err := getTLS(k8sClient, ingressRoute.Spec.TLS.SecretName, ingressRoute.Namespace)
if err != nil {
return err
}
tlsConfigs[configKey] = tlsConf
}
return nil
}

View file

@ -12,6 +12,8 @@ import (
var _ provider.Provider = (*Provider)(nil) var _ provider.Provider = (*Provider)(nil)
func Int(v int) *int { return &v }
func TestLoadIngressRouteTCPs(t *testing.T) { func TestLoadIngressRouteTCPs(t *testing.T) {
testCases := []struct { testCases := []struct {
desc string desc string
@ -671,7 +673,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"default/test.route-6b204d94623b3df4370c": { "default/test.route-6b204d94623b3df4370c": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://10.10.0.1:80", URL: "http://10.10.0.1:80",
@ -720,7 +722,7 @@ func TestLoadIngressRoutes(t *testing.T) {
}, },
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"default/test2.route-23c7f4c450289ee29016": { "default/test2.route-23c7f4c450289ee29016": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://10.10.0.1:80", URL: "http://10.10.0.1:80",
@ -770,7 +772,7 @@ func TestLoadIngressRoutes(t *testing.T) {
}, },
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"default/test2.route-23c7f4c450289ee29016": { "default/test2.route-23c7f4c450289ee29016": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://10.10.0.1:80", URL: "http://10.10.0.1:80",
@ -812,7 +814,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"default/test.route-6b204d94623b3df4370c": { "default/test.route-6b204d94623b3df4370c": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://10.10.0.1:80", URL: "http://10.10.0.1:80",
@ -825,7 +827,7 @@ func TestLoadIngressRoutes(t *testing.T) {
}, },
}, },
"default/test.route-77c62dfe9517144aeeaa": { "default/test.route-77c62dfe9517144aeeaa": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://10.10.0.1:80", URL: "http://10.10.0.1:80",
@ -843,7 +845,7 @@ func TestLoadIngressRoutes(t *testing.T) {
}, },
}, },
{ {
desc: "One ingress Route with two different services, their servers will merge", desc: "One ingress Route with two different services",
paths: []string{"services.yml", "with_two_services.yml"}, paths: []string{"services.yml", "with_two_services.yml"},
expected: &dynamic.Configuration{ expected: &dynamic.Configuration{
TLS: &dynamic.TLSConfiguration{}, TLS: &dynamic.TLSConfiguration{},
@ -863,7 +865,21 @@ func TestLoadIngressRoutes(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"default/test.route-77c62dfe9517144aeeaa": { "default/test.route-77c62dfe9517144aeeaa": {
LoadBalancer: &dynamic.LoadBalancerService{ Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "default/test.route-77c62dfe9517144aeeaa-whoami-80",
Weight: func(i int) *int { return &i }(1),
},
{
Name: "default/test.route-77c62dfe9517144aeeaa-whoami2-8080",
Weight: func(i int) *int { return &i }(1),
},
},
},
},
"default/test.route-77c62dfe9517144aeeaa-whoami-80": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://10.10.0.1:80", URL: "http://10.10.0.1:80",
@ -871,6 +887,77 @@ func TestLoadIngressRoutes(t *testing.T) {
{ {
URL: "http://10.10.0.2:80", URL: "http://10.10.0.2:80",
}, },
},
PassHostHeader: true,
},
},
"default/test.route-77c62dfe9517144aeeaa-whoami2-8080": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://10.10.0.3:8080",
},
{
URL: "http://10.10.0.4:8080",
},
},
PassHostHeader: true,
},
},
},
},
},
},
{
desc: "One ingress Route with two different services, with weights",
paths: []string{"services.yml", "with_two_services_weight.yml"},
expected: &dynamic.Configuration{
TLS: &dynamic.TLSConfiguration{},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Services: map[string]*dynamic.TCPService{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"default/test.route-77c62dfe9517144aeeaa": {
EntryPoints: []string{"web"},
Service: "default/test.route-77c62dfe9517144aeeaa",
Rule: "Host(`foo.com`) && PathPrefix(`/foo`)",
Priority: 12,
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"default/test.route-77c62dfe9517144aeeaa": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "default/test.route-77c62dfe9517144aeeaa-whoami-80",
Weight: Int(10),
},
{
Name: "default/test.route-77c62dfe9517144aeeaa-whoami2-8080",
Weight: Int(0),
},
},
},
},
"default/test.route-77c62dfe9517144aeeaa-whoami-80": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: true,
},
},
"default/test.route-77c62dfe9517144aeeaa-whoami2-8080": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{ {
URL: "http://10.10.0.3:8080", URL: "http://10.10.0.3:8080",
}, },
@ -981,7 +1068,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"default/test.route-6b204d94623b3df4370c": { "default/test.route-6b204d94623b3df4370c": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://10.10.0.1:80", URL: "http://10.10.0.1:80",
@ -1039,7 +1126,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"default/test.route-6b204d94623b3df4370c": { "default/test.route-6b204d94623b3df4370c": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://10.10.0.1:80", URL: "http://10.10.0.1:80",
@ -1097,7 +1184,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"default/test.route-6b204d94623b3df4370c": { "default/test.route-6b204d94623b3df4370c": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://10.10.0.1:80", URL: "http://10.10.0.1:80",
@ -1154,7 +1241,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"default/test.route-6b204d94623b3df4370c": { "default/test.route-6b204d94623b3df4370c": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://10.10.0.1:80", URL: "http://10.10.0.1:80",
@ -1200,7 +1287,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"default/test.route-6b204d94623b3df4370c": { "default/test.route-6b204d94623b3df4370c": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://10.10.0.1:80", URL: "http://10.10.0.1:80",
@ -1246,7 +1333,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"default/test.route-6b204d94623b3df4370c": { "default/test.route-6b204d94623b3df4370c": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://10.10.0.1:80", URL: "http://10.10.0.1:80",
@ -1284,7 +1371,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"default/test.route-6b204d94623b3df4370c": { "default/test.route-6b204d94623b3df4370c": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://10.10.0.1:80", URL: "http://10.10.0.1:80",
@ -1321,7 +1408,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"default/test.route-6b204d94623b3df4370c": { "default/test.route-6b204d94623b3df4370c": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "https://10.10.0.5:8443", URL: "https://10.10.0.5:8443",
@ -1358,7 +1445,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"default/test.route-6b204d94623b3df4370c": { "default/test.route-6b204d94623b3df4370c": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "https://10.10.0.7:8443", URL: "https://10.10.0.7:8443",

View file

@ -49,6 +49,7 @@ type Service struct {
Scheme string `json:"scheme,omitempty"` Scheme string `json:"scheme,omitempty"`
HealthCheck *HealthCheck `json:"healthCheck,omitempty"` HealthCheck *HealthCheck `json:"healthCheck,omitempty"`
Strategy string `json:"strategy,omitempty"` Strategy string `json:"strategy,omitempty"`
Weight *int `json:"weight,omitempty"`
} }
// MiddlewareRef is a ref to the Middleware resources. // MiddlewareRef is a ref to the Middleware resources.

View file

@ -395,6 +395,11 @@ func (in *Service) DeepCopyInto(out *Service) {
*out = new(HealthCheck) *out = new(HealthCheck)
(*in).DeepCopyInto(*out) (*in).DeepCopyInto(*out)
} }
if in.Weight != nil {
in, out := &in.Weight, &out.Weight
*out = new(int)
**out = **in
}
return return
} }

View file

@ -229,7 +229,7 @@ func loadService(client Client, namespace string, backend v1beta1.IngressBackend
} }
return &dynamic.Service{ return &dynamic.Service{
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: servers, Servers: servers,
PassHostHeader: true, PassHostHeader: true,
}, },

View file

@ -50,7 +50,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
}, },
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"testing/service1/80": { "testing/service1/80": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: true, PassHostHeader: true,
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
@ -84,7 +84,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
}, },
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"testing/service1/80": { "testing/service1/80": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: true, PassHostHeader: true,
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
@ -118,7 +118,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
}, },
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"testing/service1/80": { "testing/service1/80": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: true, PassHostHeader: true,
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
@ -148,7 +148,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
}, },
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"testing/service1/80": { "testing/service1/80": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: true, PassHostHeader: true,
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
@ -177,7 +177,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
}, },
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"testing/example-com/80": { "testing/example-com/80": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: true, PassHostHeader: true,
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
@ -208,7 +208,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
}, },
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"testing/service1/80": { "testing/service1/80": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: true, PassHostHeader: true,
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
@ -242,7 +242,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
}, },
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"testing/service1/80": { "testing/service1/80": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: true, PassHostHeader: true,
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
@ -276,7 +276,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
}, },
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"testing/service1/80": { "testing/service1/80": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: true, PassHostHeader: true,
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
@ -317,7 +317,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
}, },
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"testing/service1/80": { "testing/service1/80": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: true, PassHostHeader: true,
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
@ -362,7 +362,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
}, },
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"testing/service1/80": { "testing/service1/80": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: true, PassHostHeader: true,
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
@ -375,7 +375,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
}, },
}, },
"testing/service2/8082": { "testing/service2/8082": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: true, PassHostHeader: true,
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
@ -428,7 +428,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
}, },
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"default-backend": { "default-backend": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: true, PassHostHeader: true,
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
@ -458,7 +458,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
}, },
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"testing/service1/80": { "testing/service1/80": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: true, PassHostHeader: true,
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
@ -488,7 +488,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
}, },
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"testing/service1/tchouk": { "testing/service1/tchouk": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: true, PassHostHeader: true,
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
@ -518,7 +518,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
}, },
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"testing/service1/tchouk": { "testing/service1/tchouk": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: true, PassHostHeader: true,
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
@ -552,7 +552,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
}, },
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"testing/service1/tchouk": { "testing/service1/tchouk": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: true, PassHostHeader: true,
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
@ -565,7 +565,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
}, },
}, },
"testing/service1/carotte": { "testing/service1/carotte": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: true, PassHostHeader: true,
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
@ -599,7 +599,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
}, },
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"testing/service1/tchouk": { "testing/service1/tchouk": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: true, PassHostHeader: true,
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
@ -612,7 +612,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
}, },
}, },
"toto/service1/tchouk": { "toto/service1/tchouk": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: true, PassHostHeader: true,
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
@ -664,7 +664,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
}, },
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"testing/service1/8080": { "testing/service1/8080": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: true, PassHostHeader: true,
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
@ -696,7 +696,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
}, },
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"testing/example-com/80": { "testing/example-com/80": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: true, PassHostHeader: true,
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
@ -733,7 +733,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
}, },
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"testing/service1/443": { "testing/service1/443": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: true, PassHostHeader: true,
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
@ -763,7 +763,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
}, },
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"testing/service1/8443": { "testing/service1/8443": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: true, PassHostHeader: true,
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
@ -794,7 +794,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
}, },
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"testing/service1/8443": { "testing/service1/8443": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: true, PassHostHeader: true,
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
@ -825,7 +825,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
}, },
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"default-backend": { "default-backend": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: true, PassHostHeader: true,
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
@ -855,7 +855,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
}, },
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"testing/service1/80": { "testing/service1/80": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: true, PassHostHeader: true,
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {

View file

@ -98,7 +98,7 @@ func (p *Provider) buildServiceConfiguration(ctx context.Context, app marathon.A
if len(conf.Services) == 0 { if len(conf.Services) == 0 {
conf.Services = make(map[string]*dynamic.Service) conf.Services = make(map[string]*dynamic.Service)
lb := &dynamic.LoadBalancerService{} lb := &dynamic.ServersLoadBalancer{}
lb.SetDefaults() lb.SetDefaults()
conf.Services[appName] = &dynamic.Service{ conf.Services[appName] = &dynamic.Service{
LoadBalancer: lb, LoadBalancer: lb,

View file

@ -56,7 +56,7 @@ func TestBuildConfiguration(t *testing.T) {
}, },
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"app": {LoadBalancer: &dynamic.LoadBalancerService{ "app": {LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://localhost:80", URL: "http://localhost:80",
@ -110,7 +110,7 @@ func TestBuildConfiguration(t *testing.T) {
}, },
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"app": {LoadBalancer: &dynamic.LoadBalancerService{ "app": {LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://localhost:80", URL: "http://localhost:80",
@ -156,7 +156,7 @@ func TestBuildConfiguration(t *testing.T) {
}, },
}, },
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"app": {LoadBalancer: &dynamic.LoadBalancerService{ "app": {LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://localhost:80", URL: "http://localhost:80",
@ -200,7 +200,7 @@ func TestBuildConfiguration(t *testing.T) {
}, },
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Service1": {LoadBalancer: &dynamic.LoadBalancerService{ "Service1": {LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://localhost:8080", URL: "http://localhost:8080",
@ -249,7 +249,7 @@ func TestBuildConfiguration(t *testing.T) {
}, },
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Service1": {LoadBalancer: &dynamic.LoadBalancerService{ "Service1": {LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://localhost:8080", URL: "http://localhost:8080",
@ -300,7 +300,7 @@ func TestBuildConfiguration(t *testing.T) {
}, },
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"foo": {LoadBalancer: &dynamic.LoadBalancerService{ "foo": {LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://localhost:8080", URL: "http://localhost:8080",
@ -308,7 +308,7 @@ func TestBuildConfiguration(t *testing.T) {
}, },
PassHostHeader: true, PassHostHeader: true,
}}, }},
"bar": {LoadBalancer: &dynamic.LoadBalancerService{ "bar": {LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://localhost:8081", URL: "http://localhost:8081",
@ -343,7 +343,7 @@ func TestBuildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"app": { "app": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://localhost:80", URL: "http://localhost:80",
@ -382,7 +382,7 @@ func TestBuildConfiguration(t *testing.T) {
}, },
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Service1": {LoadBalancer: &dynamic.LoadBalancerService{ "Service1": {LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://localhost:80", URL: "http://localhost:80",
@ -420,7 +420,7 @@ func TestBuildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Service1": { "Service1": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://localhost:80", URL: "http://localhost:80",
@ -451,7 +451,7 @@ func TestBuildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"app": { "app": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://localhost:80", URL: "http://localhost:80",
@ -495,7 +495,7 @@ func TestBuildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Service1": { "Service1": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://localhost:80", URL: "http://localhost:80",
@ -529,7 +529,7 @@ func TestBuildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Service1": { "Service1": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://localhost:80", URL: "http://localhost:80",
@ -539,7 +539,7 @@ func TestBuildConfiguration(t *testing.T) {
}, },
}, },
"Service2": { "Service2": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://localhost:80", URL: "http://localhost:80",
@ -629,7 +629,7 @@ func TestBuildConfiguration(t *testing.T) {
}, },
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"app": { "app": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://localhost:80", URL: "http://localhost:80",
@ -639,7 +639,7 @@ func TestBuildConfiguration(t *testing.T) {
}, },
}, },
"app2": { "app2": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://localhost:80", URL: "http://localhost:80",
@ -686,7 +686,7 @@ func TestBuildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"app": { "app": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://localhost:80", URL: "http://localhost:80",
@ -696,7 +696,7 @@ func TestBuildConfiguration(t *testing.T) {
}, },
}, },
"app2": { "app2": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://localhost:80", URL: "http://localhost:80",
@ -734,7 +734,7 @@ func TestBuildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"app": { "app": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://localhost:80", URL: "http://localhost:80",
@ -744,7 +744,7 @@ func TestBuildConfiguration(t *testing.T) {
}, },
}, },
"app2": { "app2": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://localhost:80", URL: "http://localhost:80",
@ -789,7 +789,7 @@ func TestBuildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Service1": { "Service1": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://localhost:80", URL: "http://localhost:80",
@ -830,7 +830,7 @@ func TestBuildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"app": { "app": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://localhost:80", URL: "http://localhost:80",
@ -840,7 +840,7 @@ func TestBuildConfiguration(t *testing.T) {
}, },
}, },
"app2": { "app2": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://localhost:80", URL: "http://localhost:80",
@ -877,7 +877,7 @@ func TestBuildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"app": { "app": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://localhost:80", URL: "http://localhost:80",
@ -915,7 +915,7 @@ func TestBuildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Service1": { "Service1": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "h2c://localhost:90", URL: "h2c://localhost:90",
@ -948,7 +948,7 @@ func TestBuildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Service1": { "Service1": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://localhost:80", URL: "http://localhost:80",
@ -958,7 +958,7 @@ func TestBuildConfiguration(t *testing.T) {
}, },
}, },
"Service2": { "Service2": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://localhost:8080", URL: "http://localhost:8080",
@ -1123,7 +1123,7 @@ func TestBuildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"app": { "app": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://localhost:80", URL: "http://localhost:80",
@ -1161,7 +1161,7 @@ func TestBuildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"app": { "app": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://localhost:80", URL: "http://localhost:80",
@ -1198,7 +1198,7 @@ func TestBuildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"a_b_app": { "a_b_app": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://localhost:80", URL: "http://localhost:80",
@ -1362,7 +1362,7 @@ func TestBuildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"bar": { "bar": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://localhost:80", URL: "http://localhost:80",

View file

@ -96,7 +96,7 @@ func (p *Provider) buildServiceConfiguration(ctx context.Context, service ranche
if len(configuration.Services) == 0 { if len(configuration.Services) == 0 {
configuration.Services = make(map[string]*dynamic.Service) configuration.Services = make(map[string]*dynamic.Service)
lb := &dynamic.LoadBalancerService{} lb := &dynamic.ServersLoadBalancer{}
lb.SetDefaults() lb.SetDefaults()
configuration.Services[serviceName] = &dynamic.Service{ configuration.Services[serviceName] = &dynamic.Service{
LoadBalancer: lb, LoadBalancer: lb,
@ -183,7 +183,7 @@ func (p *Provider) addServerTCP(ctx context.Context, service rancherData, loadBa
} }
func (p *Provider) addServers(ctx context.Context, service rancherData, loadBalancer *dynamic.LoadBalancerService) error { func (p *Provider) addServers(ctx context.Context, service rancherData, loadBalancer *dynamic.ServersLoadBalancer) error {
log.FromContext(ctx).Debugf("Trying to add servers for service %s \n", service.Name) log.FromContext(ctx).Debugf("Trying to add servers for service %s \n", service.Name)
serverPort := getLBServerPort(loadBalancer) serverPort := getLBServerPort(loadBalancer)
@ -216,7 +216,7 @@ func (p *Provider) addServers(ctx context.Context, service rancherData, loadBala
return nil return nil
} }
func getLBServerPort(loadBalancer *dynamic.LoadBalancerService) string { func getLBServerPort(loadBalancer *dynamic.ServersLoadBalancer) string {
if loadBalancer != nil && len(loadBalancer.Servers) > 0 { if loadBalancer != nil && len(loadBalancer.Servers) > 0 {
return loadBalancer.Servers[0].Port return loadBalancer.Servers[0].Port
} }

View file

@ -43,7 +43,7 @@ func Test_buildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Test": { "Test": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1:80", URL: "http://127.0.0.1:80",
@ -95,7 +95,7 @@ func Test_buildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Test1": { "Test1": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1:80", URL: "http://127.0.0.1:80",
@ -105,7 +105,7 @@ func Test_buildConfiguration(t *testing.T) {
}, },
}, },
"Test2": { "Test2": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.2:80", URL: "http://127.0.0.2:80",
@ -157,7 +157,7 @@ func Test_buildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Test1": { "Test1": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1:80", URL: "http://127.0.0.1:80",
@ -170,7 +170,7 @@ func Test_buildConfiguration(t *testing.T) {
}, },
}, },
"Test2": { "Test2": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://128.0.0.1:80", URL: "http://128.0.0.1:80",
@ -214,7 +214,7 @@ func Test_buildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Service1": { "Service1": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1:80", URL: "http://127.0.0.1:80",
@ -302,7 +302,7 @@ func Test_buildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Test": { "Test": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1:80", URL: "http://127.0.0.1:80",
@ -372,7 +372,7 @@ func Test_buildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Test": { "Test": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1:80", URL: "http://127.0.0.1:80",
@ -425,7 +425,7 @@ func Test_buildConfiguration(t *testing.T) {
}, },
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Test": { "Test": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1:80", URL: "http://127.0.0.1:80",
@ -467,7 +467,7 @@ func Test_buildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Test": { "Test": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1:80", URL: "http://127.0.0.1:80",
@ -652,7 +652,7 @@ func Test_buildConfiguration(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{}, Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{ Services: map[string]*dynamic.Service{
"Service1": { "Service1": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1:80", URL: "http://127.0.0.1:80",

View file

@ -48,7 +48,7 @@ func TestRouterManager_Get(t *testing.T) {
}, },
serviceConfig: map[string]*dynamic.Service{ serviceConfig: map[string]*dynamic.Service{
"foo-service": { "foo-service": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: server.URL, URL: server.URL,
@ -85,7 +85,7 @@ func TestRouterManager_Get(t *testing.T) {
}, },
serviceConfig: map[string]*dynamic.Service{ serviceConfig: map[string]*dynamic.Service{
"foo-service": { "foo-service": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: server.URL, URL: server.URL,
@ -108,7 +108,7 @@ func TestRouterManager_Get(t *testing.T) {
}, },
serviceConfig: map[string]*dynamic.Service{ serviceConfig: map[string]*dynamic.Service{
"foo-service": { "foo-service": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: server.URL, URL: server.URL,
@ -132,7 +132,7 @@ func TestRouterManager_Get(t *testing.T) {
}, },
serviceConfig: map[string]*dynamic.Service{ serviceConfig: map[string]*dynamic.Service{
"foo-service": { "foo-service": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: server.URL, URL: server.URL,
@ -173,7 +173,7 @@ func TestRouterManager_Get(t *testing.T) {
}, },
serviceConfig: map[string]*dynamic.Service{ serviceConfig: map[string]*dynamic.Service{
"foo-service": { "foo-service": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: server.URL, URL: server.URL,
@ -213,7 +213,7 @@ func TestRouterManager_Get(t *testing.T) {
}, },
serviceConfig: map[string]*dynamic.Service{ serviceConfig: map[string]*dynamic.Service{
"foo-service@provider-1": { "foo-service@provider-1": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: server.URL, URL: server.URL,
@ -236,7 +236,7 @@ func TestRouterManager_Get(t *testing.T) {
}, },
serviceConfig: map[string]*dynamic.Service{ serviceConfig: map[string]*dynamic.Service{
"foo-service@provider-2": { "foo-service@provider-2": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: server.URL, URL: server.URL,
@ -260,7 +260,7 @@ func TestRouterManager_Get(t *testing.T) {
}, },
serviceConfig: map[string]*dynamic.Service{ serviceConfig: map[string]*dynamic.Service{
"foo-service@provider-1": { "foo-service@provider-1": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: server.URL, URL: server.URL,
@ -355,7 +355,7 @@ func TestAccessLog(t *testing.T) {
}, },
serviceConfig: map[string]*dynamic.Service{ serviceConfig: map[string]*dynamic.Service{
"foo-service": { "foo-service": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: server.URL, URL: server.URL,
@ -383,7 +383,7 @@ func TestAccessLog(t *testing.T) {
}, },
serviceConfig: map[string]*dynamic.Service{ serviceConfig: map[string]*dynamic.Service{
"foo-service": { "foo-service": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: server.URL, URL: server.URL,
@ -448,7 +448,7 @@ func TestRuntimeConfiguration(t *testing.T) {
desc: "No error", desc: "No error",
serviceConfig: map[string]*dynamic.Service{ serviceConfig: map[string]*dynamic.Service{
"foo-service": { "foo-service": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1:8085", URL: "http://127.0.0.1:8085",
@ -482,7 +482,7 @@ func TestRuntimeConfiguration(t *testing.T) {
desc: "One router with wrong rule", desc: "One router with wrong rule",
serviceConfig: map[string]*dynamic.Service{ serviceConfig: map[string]*dynamic.Service{
"foo-service": { "foo-service": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1", URL: "http://127.0.0.1",
@ -509,7 +509,7 @@ func TestRuntimeConfiguration(t *testing.T) {
desc: "All router with wrong rule", desc: "All router with wrong rule",
serviceConfig: map[string]*dynamic.Service{ serviceConfig: map[string]*dynamic.Service{
"foo-service": { "foo-service": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1", URL: "http://127.0.0.1",
@ -536,7 +536,7 @@ func TestRuntimeConfiguration(t *testing.T) {
desc: "Router with unknown service", desc: "Router with unknown service",
serviceConfig: map[string]*dynamic.Service{ serviceConfig: map[string]*dynamic.Service{
"foo-service": { "foo-service": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1", URL: "http://127.0.0.1",
@ -579,7 +579,7 @@ func TestRuntimeConfiguration(t *testing.T) {
desc: "Router with middleware", desc: "Router with middleware",
serviceConfig: map[string]*dynamic.Service{ serviceConfig: map[string]*dynamic.Service{
"foo-service": { "foo-service": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1", URL: "http://127.0.0.1",
@ -619,7 +619,7 @@ func TestRuntimeConfiguration(t *testing.T) {
desc: "Router with unknown middleware", desc: "Router with unknown middleware",
serviceConfig: map[string]*dynamic.Service{ serviceConfig: map[string]*dynamic.Service{
"foo-service": { "foo-service": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1", URL: "http://127.0.0.1",
@ -650,7 +650,7 @@ func TestRuntimeConfiguration(t *testing.T) {
desc: "Router with broken middleware", desc: "Router with broken middleware",
serviceConfig: map[string]*dynamic.Service{ serviceConfig: map[string]*dynamic.Service{
"foo-service": { "foo-service": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://127.0.0.1", URL: "http://127.0.0.1",
@ -749,7 +749,7 @@ func BenchmarkRouterServe(b *testing.B) {
} }
serviceConfig := map[string]*dynamic.Service{ serviceConfig := map[string]*dynamic.Service{
"foo-service": { "foo-service": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: server.URL, URL: server.URL,
@ -793,7 +793,7 @@ func BenchmarkService(b *testing.B) {
serviceConfig := map[string]*dynamic.Service{ serviceConfig := map[string]*dynamic.Service{
"foo-service": { "foo-service": {
LoadBalancer: &dynamic.LoadBalancerService{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "tchouck", URL: "tchouck",

View file

@ -199,7 +199,7 @@ func TestServerResponseEmptyBackend(t *testing.T) {
th.WithRule(routeRule)), th.WithRule(routeRule)),
), ),
th.WithLoadBalancerServices(th.WithService("bar", th.WithLoadBalancerServices(th.WithService("bar",
th.WithStickiness("test")), th.WithSticky("test")),
), ),
) )
}, },
@ -229,7 +229,7 @@ func TestServerResponseEmptyBackend(t *testing.T) {
th.WithRule(routeRule)), th.WithRule(routeRule)),
), ),
th.WithLoadBalancerServices(th.WithService("bar", th.WithLoadBalancerServices(th.WithService("bar",
th.WithStickiness("test")), th.WithSticky("test")),
), ),
) )
}, },

View file

@ -0,0 +1,155 @@
package wrr
import (
"fmt"
"net/http"
"sync"
"github.com/containous/traefik/v2/pkg/config/dynamic"
"github.com/containous/traefik/v2/pkg/log"
)
type namedHandler struct {
http.Handler
name string
weight int
}
type stickyCookie struct {
name string
secure bool
httpOnly bool
}
// New creates a new load balancer.
func New(sticky *dynamic.Sticky) *Balancer {
balancer := &Balancer{
mutex: &sync.Mutex{},
index: -1,
}
if sticky != nil && sticky.Cookie != nil {
balancer.stickyCookie = &stickyCookie{
name: sticky.Cookie.Name,
secure: sticky.Cookie.Secure,
httpOnly: sticky.Cookie.HTTPOnly,
}
}
return balancer
}
// Balancer is a WeightedRoundRobin load balancer.
type Balancer struct {
handlers []*namedHandler
mutex *sync.Mutex
// Current index (starts from -1)
index int
currentWeight int
stickyCookie *stickyCookie
}
func (b *Balancer) maxWeight() int {
max := -1
for _, s := range b.handlers {
if s.weight > max {
max = s.weight
}
}
return max
}
func (b *Balancer) weightGcd() int {
divisor := -1
for _, s := range b.handlers {
if divisor == -1 {
divisor = s.weight
} else {
divisor = gcd(divisor, s.weight)
}
}
return divisor
}
func gcd(a, b int) int {
for b != 0 {
a, b = b, a%b
}
return a
}
func (b *Balancer) nextServer() (*namedHandler, error) {
b.mutex.Lock()
defer b.mutex.Unlock()
if len(b.handlers) == 0 {
return nil, fmt.Errorf("no servers in the pool")
}
// The algo below may look messy, but is actually very simple
// it calculates the GCD and subtracts it on every iteration, what interleaves servers
// and allows us not to build an iterator every time we readjust weights
// GCD across all enabled servers
gcd := b.weightGcd()
// Maximum weight across all enabled servers
max := b.maxWeight()
for {
b.index = (b.index + 1) % len(b.handlers)
if b.index == 0 {
b.currentWeight -= gcd
if b.currentWeight <= 0 {
b.currentWeight = max
if b.currentWeight == 0 {
return nil, fmt.Errorf("all servers have 0 weight")
}
}
}
srv := b.handlers[b.index]
if srv.weight >= b.currentWeight {
log.WithoutContext().Debugf("Service Select: %s", srv.name)
return srv, 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 && err != http.ErrNoCookie {
log.WithoutContext().Warnf("Error while reading cookie: %v", err)
}
if err == nil && cookie != nil {
for _, handler := range b.handlers {
if handler.name == cookie.Value {
handler.ServeHTTP(w, req)
return
}
}
}
}
server, err := b.nextServer()
if err != nil {
http.Error(w, http.StatusText(http.StatusInternalServerError)+err.Error(), http.StatusInternalServerError)
return
}
if b.stickyCookie != nil {
cookie := &http.Cookie{Name: b.stickyCookie.name, Value: server.name, Path: "/", HttpOnly: b.stickyCookie.httpOnly, Secure: b.stickyCookie.secure}
http.SetCookie(w, cookie)
}
server.ServeHTTP(w, req)
}
// AddService adds a handler.
// It is not thread safe with ServeHTTP.
func (b *Balancer) AddService(name string, handler http.Handler, weight *int) {
w := 1
if weight != nil {
w = *weight
}
b.handlers = append(b.handlers, &namedHandler{Handler: handler, name: name, weight: w})
}

View file

@ -0,0 +1,115 @@
package wrr
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/containous/traefik/v2/pkg/config/dynamic"
"github.com/stretchr/testify/assert"
)
func Int(v int) *int { return &v }
type responseRecorder struct {
*httptest.ResponseRecorder
save map[string]int
}
func (r *responseRecorder) WriteHeader(statusCode int) {
r.save[r.Header().Get("server")]++
r.ResponseRecorder.WriteHeader(statusCode)
}
func TestBalancer(t *testing.T) {
balancer := New(nil)
balancer.AddService("first", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
rw.Header().Set("server", "first")
rw.WriteHeader(http.StatusOK)
}), Int(3))
balancer.AddService("second", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
rw.Header().Set("server", "second")
rw.WriteHeader(http.StatusOK)
}), Int(1))
recorder := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}}
for i := 0; i < 4; i++ {
balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil))
}
assert.Equal(t, 3, recorder.save["first"])
assert.Equal(t, 1, recorder.save["second"])
}
func TestBalancerNoService(t *testing.T) {
balancer := New(nil)
recorder := httptest.NewRecorder()
balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil))
assert.Equal(t, http.StatusInternalServerError, recorder.Result().StatusCode)
}
func TestBalancerOneServerZeroWeight(t *testing.T) {
balancer := New(nil)
balancer.AddService("first", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
rw.Header().Set("server", "first")
rw.WriteHeader(http.StatusOK)
}), Int(1))
balancer.AddService("second", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {}), Int(0))
recorder := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}}
for i := 0; i < 3; i++ {
balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil))
}
assert.Equal(t, 3, recorder.save["first"])
}
func TestBalancerAllServersZeroWeight(t *testing.T) {
balancer := New(nil)
balancer.AddService("test", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {}), Int(0))
balancer.AddService("test2", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {}), Int(0))
recorder := httptest.NewRecorder()
balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil))
assert.Equal(t, http.StatusInternalServerError, recorder.Result().StatusCode)
}
func TestSticky(t *testing.T) {
balancer := New(&dynamic.Sticky{
Cookie: &dynamic.Cookie{Name: "test"},
})
balancer.AddService("first", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
rw.Header().Set("server", "first")
rw.WriteHeader(http.StatusOK)
}), Int(1))
balancer.AddService("second", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
rw.Header().Set("server", "second")
rw.WriteHeader(http.StatusOK)
}), Int(2))
recorder := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}}
req := httptest.NewRequest(http.MethodGet, "/", nil)
for i := 0; i < 3; i++ {
for _, cookie := range recorder.Result().Cookies() {
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"])
}

View file

@ -2,6 +2,7 @@ package service
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"net/http" "net/http"
"net/http/httputil" "net/http/httputil"
@ -20,6 +21,7 @@ import (
"github.com/containous/traefik/v2/pkg/middlewares/pipelining" "github.com/containous/traefik/v2/pkg/middlewares/pipelining"
"github.com/containous/traefik/v2/pkg/server/cookie" "github.com/containous/traefik/v2/pkg/server/cookie"
"github.com/containous/traefik/v2/pkg/server/internal" "github.com/containous/traefik/v2/pkg/server/internal"
"github.com/containous/traefik/v2/pkg/server/service/loadbalancer/wrr"
"github.com/vulcand/oxy/roundrobin" "github.com/vulcand/oxy/roundrobin"
) )
@ -60,27 +62,58 @@ func (m *Manager) BuildHTTP(rootCtx context.Context, serviceName string, respons
return nil, fmt.Errorf("the service %q does not exist", serviceName) return nil, fmt.Errorf("the service %q does not exist", serviceName)
} }
// TODO Should handle multiple service types if conf.LoadBalancer != nil && conf.Weighted != nil {
// FIXME Check if the service is declared multiple times with different types return nil, errors.New("cannot create service: multi-types service not supported, consider declaring two different pieces of service instead")
if conf.LoadBalancer == nil {
sErr := fmt.Errorf("the service %q doesn't have any load balancer", serviceName)
conf.AddError(sErr, true)
return nil, sErr
} }
lb, err := m.getLoadBalancerServiceHandler(ctx, serviceName, conf.LoadBalancer, responseModifier) var lb http.Handler
switch {
case conf.LoadBalancer != nil:
var err error
lb, err = m.getLoadBalancerServiceHandler(ctx, serviceName, conf.LoadBalancer, responseModifier)
if err != nil { if err != nil {
conf.AddError(err, true) conf.AddError(err, true)
return nil, err return nil, err
} }
case conf.Weighted != nil:
var err error
lb, err = m.getLoadBalancerWRRServiceHandler(ctx, serviceName, conf.Weighted, responseModifier)
if err != nil {
conf.AddError(err, true)
return nil, err
}
default:
sErr := fmt.Errorf("the service %q does not have any type defined", serviceName)
conf.AddError(sErr, true)
return nil, sErr
}
return lb, nil return lb, nil
} }
func (m *Manager) getLoadBalancerWRRServiceHandler(ctx context.Context, serviceName string, config *dynamic.WeightedRoundRobin, responseModifier func(*http.Response) error) (http.Handler, error) {
// TODO Handle accesslog and metrics with multiple service name
if config.Sticky != nil && config.Sticky.Cookie != nil {
config.Sticky.Cookie.Name = cookie.GetName(config.Sticky.Cookie.Name, serviceName)
}
balancer := wrr.New(config.Sticky)
for _, service := range config.Services {
serviceHandler, err := m.BuildHTTP(ctx, service.Name, responseModifier)
if err != nil {
return nil, err
}
balancer.AddService(service.Name, serviceHandler, service.Weight)
}
return balancer, nil
}
func (m *Manager) getLoadBalancerServiceHandler( func (m *Manager) getLoadBalancerServiceHandler(
ctx context.Context, ctx context.Context,
serviceName string, serviceName string,
service *dynamic.LoadBalancerService, service *dynamic.ServersLoadBalancer,
responseModifier func(*http.Response) error, responseModifier func(*http.Response) error,
) (http.Handler, error) { ) (http.Handler, error) {
fwd, err := buildProxy(service.PassHostHeader, service.ResponseForwarding, m.defaultRoundTripper, m.bufferPool, responseModifier) fwd, err := buildProxy(service.PassHostHeader, service.ResponseForwarding, m.defaultRoundTripper, m.bufferPool, responseModifier)
@ -193,16 +226,16 @@ func buildHealthCheckOptions(ctx context.Context, lb healthcheck.BalancerHandler
} }
} }
func (m *Manager) getLoadBalancer(ctx context.Context, serviceName string, service *dynamic.LoadBalancerService, fwd http.Handler) (healthcheck.BalancerHandler, error) { func (m *Manager) getLoadBalancer(ctx context.Context, serviceName string, service *dynamic.ServersLoadBalancer, fwd http.Handler) (healthcheck.BalancerHandler, error) {
logger := log.FromContext(ctx) logger := log.FromContext(ctx)
logger.Debug("Creating load-balancer") logger.Debug("Creating load-balancer")
var options []roundrobin.LBOption var options []roundrobin.LBOption
var cookieName string var cookieName string
if stickiness := service.Stickiness; stickiness != nil { if service.Sticky != nil && service.Sticky.Cookie != nil {
cookieName = cookie.GetName(stickiness.CookieName, serviceName) cookieName = cookie.GetName(service.Sticky.Cookie.Name, serviceName)
opts := roundrobin.CookieOptions{HTTPOnly: stickiness.HTTPOnlyCookie, Secure: stickiness.SecureCookie} opts := roundrobin.CookieOptions{HTTPOnly: service.Sticky.Cookie.HTTPOnly, Secure: service.Sticky.Cookie.Secure}
options = append(options, roundrobin.EnableStickySession(roundrobin.NewStickySessionWithOptions(cookieName, opts))) options = append(options, roundrobin.EnableStickySession(roundrobin.NewStickySessionWithOptions(cookieName, opts)))
logger.Debugf("Sticky session cookie name: %v", cookieName) logger.Debugf("Sticky session cookie name: %v", cookieName)
} }

View file

@ -27,14 +27,14 @@ func TestGetLoadBalancer(t *testing.T) {
testCases := []struct { testCases := []struct {
desc string desc string
serviceName string serviceName string
service *dynamic.LoadBalancerService service *dynamic.ServersLoadBalancer
fwd http.Handler fwd http.Handler
expectError bool expectError bool
}{ }{
{ {
desc: "Fails when provided an invalid URL", desc: "Fails when provided an invalid URL",
serviceName: "test", serviceName: "test",
service: &dynamic.LoadBalancerService{ service: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: ":", URL: ":",
@ -47,15 +47,15 @@ func TestGetLoadBalancer(t *testing.T) {
{ {
desc: "Succeeds when there are no servers", desc: "Succeeds when there are no servers",
serviceName: "test", serviceName: "test",
service: &dynamic.LoadBalancerService{}, service: &dynamic.ServersLoadBalancer{},
fwd: &MockForwarder{}, fwd: &MockForwarder{},
expectError: false, expectError: false,
}, },
{ {
desc: "Succeeds when stickiness is set", desc: "Succeeds when sticky.cookie is set",
serviceName: "test", serviceName: "test",
service: &dynamic.LoadBalancerService{ service: &dynamic.ServersLoadBalancer{
Stickiness: &dynamic.Stickiness{}, Sticky: &dynamic.Sticky{Cookie: &dynamic.Cookie{}},
}, },
fwd: &MockForwarder{}, fwd: &MockForwarder{},
expectError: false, expectError: false,
@ -114,7 +114,7 @@ func TestGetLoadBalancerServiceHandler(t *testing.T) {
testCases := []struct { testCases := []struct {
desc string desc string
serviceName string serviceName string
service *dynamic.LoadBalancerService service *dynamic.ServersLoadBalancer
responseModifier func(*http.Response) error responseModifier func(*http.Response) error
expected []ExpectedResult expected []ExpectedResult
@ -122,7 +122,7 @@ func TestGetLoadBalancerServiceHandler(t *testing.T) {
{ {
desc: "Load balances between the two servers", desc: "Load balances between the two servers",
serviceName: "test", serviceName: "test",
service: &dynamic.LoadBalancerService{ service: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: server1.URL, URL: server1.URL,
@ -146,7 +146,7 @@ func TestGetLoadBalancerServiceHandler(t *testing.T) {
{ {
desc: "StatusBadGateway when the server is not reachable", desc: "StatusBadGateway when the server is not reachable",
serviceName: "test", serviceName: "test",
service: &dynamic.LoadBalancerService{ service: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://foo", URL: "http://foo",
@ -162,7 +162,7 @@ func TestGetLoadBalancerServiceHandler(t *testing.T) {
{ {
desc: "ServiceUnavailable when no servers are available", desc: "ServiceUnavailable when no servers are available",
serviceName: "test", serviceName: "test",
service: &dynamic.LoadBalancerService{ service: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{}, Servers: []dynamic.Server{},
}, },
expected: []ExpectedResult{ expected: []ExpectedResult{
@ -172,10 +172,10 @@ func TestGetLoadBalancerServiceHandler(t *testing.T) {
}, },
}, },
{ {
desc: "Always call the same server when stickiness is true", desc: "Always call the same server when sticky.cookie is true",
serviceName: "test", serviceName: "test",
service: &dynamic.LoadBalancerService{ service: &dynamic.ServersLoadBalancer{
Stickiness: &dynamic.Stickiness{}, Sticky: &dynamic.Sticky{Cookie: &dynamic.Cookie{}},
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: server1.URL, URL: server1.URL,
@ -199,8 +199,8 @@ func TestGetLoadBalancerServiceHandler(t *testing.T) {
{ {
desc: "Sticky Cookie's options set correctly", desc: "Sticky Cookie's options set correctly",
serviceName: "test", serviceName: "test",
service: &dynamic.LoadBalancerService{ service: &dynamic.ServersLoadBalancer{
Stickiness: &dynamic.Stickiness{HTTPOnlyCookie: true, SecureCookie: true}, Sticky: &dynamic.Sticky{Cookie: &dynamic.Cookie{HTTPOnly: true, Secure: true}},
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: server1.URL, URL: server1.URL,
@ -219,8 +219,8 @@ func TestGetLoadBalancerServiceHandler(t *testing.T) {
{ {
desc: "PassHost passes the host instead of the IP", desc: "PassHost passes the host instead of the IP",
serviceName: "test", serviceName: "test",
service: &dynamic.LoadBalancerService{ service: &dynamic.ServersLoadBalancer{
Stickiness: &dynamic.Stickiness{}, Sticky: &dynamic.Sticky{Cookie: &dynamic.Cookie{}},
PassHostHeader: true, PassHostHeader: true,
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
@ -238,8 +238,8 @@ func TestGetLoadBalancerServiceHandler(t *testing.T) {
{ {
desc: "PassHost doesn't passe the host instead of the IP", desc: "PassHost doesn't passe the host instead of the IP",
serviceName: "test", serviceName: "test",
service: &dynamic.LoadBalancerService{ service: &dynamic.ServersLoadBalancer{
Stickiness: &dynamic.Stickiness{}, Sticky: &dynamic.Sticky{Cookie: &dynamic.Cookie{}},
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: serverPassHostFalse.URL, URL: serverPassHostFalse.URL,
@ -297,7 +297,7 @@ func TestManager_Build(t *testing.T) {
configs: map[string]*runtime.ServiceInfo{ configs: map[string]*runtime.ServiceInfo{
"serviceName": { "serviceName": {
Service: &dynamic.Service{ Service: &dynamic.Service{
LoadBalancer: &dynamic.LoadBalancerService{}, LoadBalancer: &dynamic.ServersLoadBalancer{},
}, },
}, },
}, },
@ -308,7 +308,7 @@ func TestManager_Build(t *testing.T) {
configs: map[string]*runtime.ServiceInfo{ configs: map[string]*runtime.ServiceInfo{
"serviceName@provider-1": { "serviceName@provider-1": {
Service: &dynamic.Service{ Service: &dynamic.Service{
LoadBalancer: &dynamic.LoadBalancerService{}, LoadBalancer: &dynamic.ServersLoadBalancer{},
}, },
}, },
}, },
@ -319,7 +319,7 @@ func TestManager_Build(t *testing.T) {
configs: map[string]*runtime.ServiceInfo{ configs: map[string]*runtime.ServiceInfo{
"serviceName@provider-1": { "serviceName@provider-1": {
Service: &dynamic.Service{ Service: &dynamic.Service{
LoadBalancer: &dynamic.LoadBalancerService{}, LoadBalancer: &dynamic.ServersLoadBalancer{},
}, },
}, },
}, },

View file

@ -50,11 +50,11 @@ func WithServiceName(serviceName string) func(*dynamic.Router) {
} }
// WithLoadBalancerServices is a helper to create a configuration. // WithLoadBalancerServices is a helper to create a configuration.
func WithLoadBalancerServices(opts ...func(service *dynamic.LoadBalancerService) string) func(*dynamic.HTTPConfiguration) { func WithLoadBalancerServices(opts ...func(service *dynamic.ServersLoadBalancer) string) func(*dynamic.HTTPConfiguration) {
return func(c *dynamic.HTTPConfiguration) { return func(c *dynamic.HTTPConfiguration) {
c.Services = make(map[string]*dynamic.Service) c.Services = make(map[string]*dynamic.Service)
for _, opt := range opts { for _, opt := range opts {
b := &dynamic.LoadBalancerService{} b := &dynamic.ServersLoadBalancer{}
name := opt(b) name := opt(b)
c.Services[name] = &dynamic.Service{ c.Services[name] = &dynamic.Service{
LoadBalancer: b, LoadBalancer: b,
@ -64,8 +64,8 @@ func WithLoadBalancerServices(opts ...func(service *dynamic.LoadBalancerService)
} }
// WithService is a helper to create a configuration. // WithService is a helper to create a configuration.
func WithService(name string, opts ...func(*dynamic.LoadBalancerService)) func(*dynamic.LoadBalancerService) string { func WithService(name string, opts ...func(*dynamic.ServersLoadBalancer)) func(*dynamic.ServersLoadBalancer) string {
return func(r *dynamic.LoadBalancerService) string { return func(r *dynamic.ServersLoadBalancer) string {
for _, opt := range opts { for _, opt := range opts {
opt(r) opt(r)
} }
@ -117,8 +117,8 @@ func WithRule(rule string) func(*dynamic.Router) {
} }
// WithServers is a helper to create a configuration. // WithServers is a helper to create a configuration.
func WithServers(opts ...func(*dynamic.Server)) func(*dynamic.LoadBalancerService) { func WithServers(opts ...func(*dynamic.Server)) func(*dynamic.ServersLoadBalancer) {
return func(b *dynamic.LoadBalancerService) { return func(b *dynamic.ServersLoadBalancer) {
for _, opt := range opts { for _, opt := range opts {
server := dynamic.Server{} server := dynamic.Server{}
opt(&server) opt(&server)
@ -137,11 +137,11 @@ func WithServer(url string, opts ...func(*dynamic.Server)) func(*dynamic.Server)
} }
} }
// WithStickiness is a helper to create a configuration. // WithSticky is a helper to create a configuration.
func WithStickiness(cookieName string) func(*dynamic.LoadBalancerService) { func WithSticky(cookieName string) func(*dynamic.ServersLoadBalancer) {
return func(b *dynamic.LoadBalancerService) { return func(b *dynamic.ServersLoadBalancer) {
b.Stickiness = &dynamic.Stickiness{ b.Sticky = &dynamic.Sticky{
CookieName: cookieName, Cookie: &dynamic.Cookie{Name: cookieName},
} }
} }
} }