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
# only support "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
services:
- name: whoami
port: 80
# (default 1) A weight used by the weighted round-robin strategy (WRR).
weight: 1
---
apiVersion: traefik.containo.us/v1alpha1

View file

@ -130,10 +130,10 @@
- "traefik.http.services.service0.loadbalancer.healthcheck.timeout=foobar"
- "traefik.http.services.service0.loadbalancer.passhostheader=true"
- "traefik.http.services.service0.loadbalancer.responseforwarding.flushinterval=foobar"
- "traefik.http.services.service0.loadbalancer.stickiness=true"
- "traefik.http.services.service0.loadbalancer.stickiness.cookiename=foobar"
- "traefik.http.services.service0.loadbalancer.stickiness.httponlycookie=true"
- "traefik.http.services.service0.loadbalancer.stickiness.securecookie=true"
- "traefik.http.services.service0.loadbalancer.sticky=true"
- "traefik.http.services.service0.loadbalancer.sticky.cookie.name=foobar"
- "traefik.http.services.service0.loadbalancer.sticky.cookie.httponly=true"
- "traefik.http.services.service0.loadbalancer.sticky.cookie.secure=true"
- "traefik.http.services.service0.loadbalancer.server.port=foobar"
- "traefik.http.services.service0.loadbalancer.server.scheme=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.passhostheader=true"
- "traefik.http.services.service1.loadbalancer.responseforwarding.flushinterval=foobar"
- "traefik.http.services.service1.loadbalancer.stickiness=true"
- "traefik.http.services.service1.loadbalancer.stickiness.cookiename=foobar"
- "traefik.http.services.service1.loadbalancer.stickiness.httponlycookie=true"
- "traefik.http.services.service1.loadbalancer.stickiness.securecookie=true"
- "traefik.http.services.service1.loadbalancer.sticky=true"
- "traefik.http.services.service1.loadbalancer.sticky.cookie.name=foobar"
- "traefik.http.services.service1.loadbalancer.sticky.cookie.httponly=true"
- "traefik.http.services.service1.loadbalancer.sticky.cookie.secure=true"
- "traefik.http.services.service1.loadbalancer.server.port=foobar"
- "traefik.http.services.service1.loadbalancer.server.scheme=foobar"
- "traefik.tcp.routers.tcprouter0.entrypoints=foobar, foobar"

View file

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

View file

@ -45,34 +45,13 @@ http:
- foobar
- foobar
services:
Service0:
Service01:
loadBalancer:
stickiness:
cookieName: foobar
secureCookie: true
httpOnlyCookie: true
servers:
- 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
sticky:
cookie:
name: foobar
secure: true
httpOnly: true
servers:
- url: foobar
- url: foobar
@ -89,6 +68,18 @@ http:
passHostHeader: true
responseForwarding:
flushInterval: foobar
Service02:
weighted:
services:
- name: foobar
weight: 42
- name: foobar
weight: 42
sticky:
cookie:
name: foobar
secure: true
httpOnly: true
middlewares:
Middleware00:
addPrefix:

View file

@ -130,10 +130,10 @@
"traefik.http.services.service0.loadbalancer.healthcheck.timeout": "foobar",
"traefik.http.services.service0.loadbalancer.passhostheader": "true",
"traefik.http.services.service0.loadbalancer.responseforwarding.flushinterval": "foobar",
"traefik.http.services.service0.loadbalancer.stickiness": "true",
"traefik.http.services.service0.loadbalancer.stickiness.cookiename": "foobar",
"traefik.http.services.service0.loadbalancer.stickiness.httponlycookie": "true",
"traefik.http.services.service0.loadbalancer.stickiness.securecookie": "true",
"traefik.http.services.service0.loadbalancer.sticky": "true",
"traefik.http.services.service0.loadbalancer.sticky.cookie.name": "foobar",
"traefik.http.services.service0.loadbalancer.sticky.cookie.httponly": "true",
"traefik.http.services.service0.loadbalancer.sticky.cookie.secure": "true",
"traefik.http.services.service0.loadbalancer.server.port": "foobar",
"traefik.http.services.service0.loadbalancer.server.scheme": "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.passhostheader": "true",
"traefik.http.services.service1.loadbalancer.responseforwarding.flushinterval": "foobar",
"traefik.http.services.service1.loadbalancer.stickiness": "true",
"traefik.http.services.service1.loadbalancer.stickiness.cookiename": "foobar",
"traefik.http.services.service1.loadbalancer.stickiness.httponlycookie": "true",
"traefik.http.services.service1.loadbalancer.stickiness.securecookie": "true",
"traefik.http.services.service1.loadbalancer.sticky": "true",
"traefik.http.services.service1.loadbalancer.sticky.cookie.name": "foobar",
"traefik.http.services.service1.loadbalancer.sticky.cookie.secure": "true",
"traefik.http.services.service1.loadbalancer.sticky.cookie.httponly": "true",
"traefik.http.services.service1.loadbalancer.server.port": "foobar",
"traefik.http.services.service1.loadbalancer.server.scheme": "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
### General
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
### Servers Load Balancer
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"
[http.services]
[http.services.my-service]
[http.services.my-service.loadBalancer.stickiness]
[http.services.my-service.loadBalancer.sticky.cookie]
```
```yaml tab="YAML"
@ -169,18 +163,19 @@ On subsequent requests, the client is forwarded to the same server.
services:
my-service:
loadBalancer:
stickiness: {}
sticky:
cookie: {}
```
??? example "Adding Stickiness with a Custom Cookie Name"
??? example "Adding Stickiness with custom Options"
```toml tab="TOML"
[http.services]
[http.services.my-service]
[http.services.my-service.loadBalancer.stickiness]
cookieName = "my_stickiness_cookie_name"
secureCookie = true
httpOnlyCookie = true
[http.services.my-service.loadBalancer.sticky.cookie]
name = "my_sticky_cookie_name"
secure = true
httpOnly = true
```
```yaml tab="YAML"
@ -188,10 +183,11 @@ On subsequent requests, the client is forwarded to the same server.
services:
my-service:
loadBalancer:
stickiness:
cookieName: my_stickiness_cookie_name
secureCookie: true
httpOnlyCookie: true
sticky:
cookie:
name: my_sticky_cookie_name
secure: true
httpOnly: true
```
#### Health Check
@ -306,6 +302,57 @@ Below are the available options for the health check mechanism:
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
### General

View file

@ -34,7 +34,6 @@
[tcp.services.whoami-no-cert]
[tcp.services.whoami-no-cert.loadBalancer]
method = "wrr"
[[tcp.services.whoami-no-cert.loadBalancer.servers]]
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{
"service1": {
LoadBalancer: &dynamic.LoadBalancerService{
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://" + s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress + ":80",

View file

@ -4,6 +4,7 @@ import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"os"
@ -563,7 +564,7 @@ func (s *SimpleSuite) TestServiceConfigErrors(c *check.C) {
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(`["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)
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"`))
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 {
si := &runtime.ServiceInfo{
Service: &dynamic.Service{
LoadBalancer: &dynamic.LoadBalancerService{
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://127.0.0.1",
@ -219,7 +219,7 @@ func TestHandler_HTTP(t *testing.T) {
"baz@myprovider": func() *runtime.ServiceInfo {
si := &runtime.ServiceInfo{
Service: &dynamic.Service{
LoadBalancer: &dynamic.LoadBalancerService{
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://127.0.0.2",
@ -248,7 +248,7 @@ func TestHandler_HTTP(t *testing.T) {
"bar@myprovider": func() *runtime.ServiceInfo {
si := &runtime.ServiceInfo{
Service: &dynamic.Service{
LoadBalancer: &dynamic.LoadBalancerService{
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://127.0.0.1",
@ -264,7 +264,7 @@ func TestHandler_HTTP(t *testing.T) {
"baz@myprovider": func() *runtime.ServiceInfo {
si := &runtime.ServiceInfo{
Service: &dynamic.Service{
LoadBalancer: &dynamic.LoadBalancerService{
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://127.0.0.2",
@ -280,7 +280,7 @@ func TestHandler_HTTP(t *testing.T) {
"test@myprovider": func() *runtime.ServiceInfo {
si := &runtime.ServiceInfo{
Service: &dynamic.Service{
LoadBalancer: &dynamic.LoadBalancerService{
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://127.0.0.3",
@ -309,7 +309,7 @@ func TestHandler_HTTP(t *testing.T) {
"bar@myprovider": func() *runtime.ServiceInfo {
si := &runtime.ServiceInfo{
Service: &dynamic.Service{
LoadBalancer: &dynamic.LoadBalancerService{
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://127.0.0.1",
@ -337,7 +337,7 @@ func TestHandler_HTTP(t *testing.T) {
"bar@myprovider": func() *runtime.ServiceInfo {
si := &runtime.ServiceInfo{
Service: &dynamic.Service{
LoadBalancer: &dynamic.LoadBalancerService{
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://127.0.0.1",

View file

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

View file

@ -404,8 +404,8 @@
[http.services.Service0]
[http.services.Service0.loadBalancer]
passHostHeader = true
[http.services.Service0.loadBalancer.stickiness]
cookieName = "foobar"
[http.services.Service0.loadBalancer.sticky.cookie]
name = "foobar"
[[http.services.Service0.loadBalancer.servers]]
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).
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
@ -45,9 +46,47 @@ type RouterTLSConfig struct {
// +k8s:deepcopy-gen=true
// LoadBalancerService holds the LoadBalancerService configuration.
type LoadBalancerService struct {
Stickiness *Stickiness `json:"stickiness,omitempty" toml:"stickiness,omitempty" yaml:"stickiness,omitempty" label:"allowEmpty"`
// WeightedRoundRobin is a weighted round robin load-balancer of services.
type WeightedRoundRobin struct {
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"`
HealthCheck *HealthCheck `json:"healthCheck,omitempty" toml:"healthCheck,omitempty" yaml:"healthCheck,omitempty"`
PassHostHeader bool `json:"passHostHeader" toml:"passHostHeader" yaml:"passHostHeader"`
@ -55,7 +94,7 @@ type LoadBalancerService struct {
}
// 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
defer func() {
l.Servers = savedServers
@ -71,8 +110,8 @@ func (l *LoadBalancerService) Mergeable(loadBalancer *LoadBalancerService) bool
return reflect.DeepEqual(l, loadBalancer)
}
// SetDefaults Default values for a LoadBalancerService.
func (l *LoadBalancerService) SetDefaults() {
// SetDefaults Default values for a ServersLoadBalancer.
func (l *ServersLoadBalancer) SetDefaults() {
l.PassHostHeader = true
}
@ -85,15 +124,6 @@ type ResponseForwarding struct {
// +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.
type Server struct {
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.
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.

View file

@ -247,6 +247,22 @@ func (in Configurations) DeepCopy() Configurations {
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.
func (in *DigestAuth) DeepCopyInto(out *DigestAuth) {
*out = *in
@ -508,42 +524,6 @@ func (in *IPWhiteList) DeepCopy() *IPWhiteList {
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.
func (in *MaxConn) DeepCopyInto(out *MaxConn) {
*out = *in
@ -954,12 +934,53 @@ func (in *Server) DeepCopy() *Server {
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.
func (in *Service) DeepCopyInto(out *Service) {
*out = *in
if in.LoadBalancer != nil {
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)
}
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.
func (in *Stickiness) DeepCopyInto(out *Stickiness) {
func (in *Sticky) DeepCopyInto(out *Sticky) {
*out = *in
if in.Cookie != nil {
in, out := &in.Cookie, &out.Cookie
*out = new(Cookie)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Stickiness.
func (in *Stickiness) DeepCopy() *Stickiness {
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Sticky.
func (in *Sticky) DeepCopy() *Sticky {
if in == nil {
return nil
}
out := new(Stickiness)
out := new(Sticky)
in.DeepCopyInto(out)
return out
}
@ -1265,3 +1291,45 @@ func (in Users) DeepCopy() Users {
in.DeepCopyInto(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.loadBalancer]
passHostHeader = true
[http.services.Service0.loadBalancer.stickiness]
cookieName = "foobar"
[http.services.Service0.loadBalancer.sticky.cookie]
name = "foobar"
[[http.services.Service0.loadBalancer.servers]]
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.server.scheme": "foobar",
"traefik.http.services.Service0.loadbalancer.server.port": "8080",
"traefik.http.services.Service0.loadbalancer.stickiness.cookiename": "foobar",
"traefik.http.services.Service0.loadbalancer.stickiness.securecookie": "true",
"traefik.http.services.Service0.loadbalancer.sticky.cookie.name": "foobar",
"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.name1": "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.server.scheme": "foobar",
"traefik.http.services.Service1.loadbalancer.server.port": "8080",
"traefik.http.services.Service1.loadbalancer.stickiness": "false",
"traefik.http.services.Service1.loadbalancer.stickiness.cookiename": "fui",
"traefik.http.services.Service1.loadbalancer.sticky": "false",
"traefik.http.services.Service1.loadbalancer.sticky.cookie.name": "fui",
"traefik.tcp.routers.Router0.rule": "foobar",
"traefik.tcp.routers.Router0.entrypoints": "foobar, fiibar",
"traefik.tcp.routers.Router0.service": "foobar",
@ -510,11 +510,13 @@ func TestDecodeConfiguration(t *testing.T) {
},
Services: map[string]*dynamic.Service{
"Service0": {
LoadBalancer: &dynamic.LoadBalancerService{
Stickiness: &dynamic.Stickiness{
CookieName: "foobar",
SecureCookie: true,
HTTPOnlyCookie: false,
LoadBalancer: &dynamic.ServersLoadBalancer{
Sticky: &dynamic.Sticky{
Cookie: &dynamic.Cookie{
Name: "foobar",
Secure: true,
HTTPOnly: false,
},
},
Servers: []dynamic.Server{
{
@ -541,7 +543,7 @@ func TestDecodeConfiguration(t *testing.T) {
},
},
"Service1": {
LoadBalancer: &dynamic.LoadBalancerService{
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
Scheme: "foobar",
@ -908,10 +910,12 @@ func TestEncodeConfiguration(t *testing.T) {
},
Services: map[string]*dynamic.Service{
"Service0": {
LoadBalancer: &dynamic.LoadBalancerService{
Stickiness: &dynamic.Stickiness{
CookieName: "foobar",
HTTPOnlyCookie: true,
LoadBalancer: &dynamic.ServersLoadBalancer{
Sticky: &dynamic.Sticky{
Cookie: &dynamic.Cookie{
Name: "foobar",
HTTPOnly: true,
},
},
Servers: []dynamic.Server{
{
@ -938,7 +942,7 @@ func TestEncodeConfiguration(t *testing.T) {
},
},
"Service1": {
LoadBalancer: &dynamic.LoadBalancerService{
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
Scheme: "foobar",
@ -1101,9 +1105,9 @@ func TestEncodeConfiguration(t *testing.T) {
"traefik.HTTP.Services.Service0.LoadBalancer.ResponseForwarding.FlushInterval": "foobar",
"traefik.HTTP.Services.Service0.LoadBalancer.server.Port": "8080",
"traefik.HTTP.Services.Service0.LoadBalancer.server.Scheme": "foobar",
"traefik.HTTP.Services.Service0.LoadBalancer.Stickiness.CookieName": "foobar",
"traefik.HTTP.Services.Service0.LoadBalancer.Stickiness.HTTPOnlyCookie": "true",
"traefik.HTTP.Services.Service0.LoadBalancer.Stickiness.SecureCookie": "false",
"traefik.HTTP.Services.Service0.LoadBalancer.Sticky.Cookie.Name": "foobar",
"traefik.HTTP.Services.Service0.LoadBalancer.Sticky.Cookie.HTTPOnly": "true",
"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.name1": "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{
"foo-service@myprovider": {
Service: &dynamic.Service{
LoadBalancer: &dynamic.LoadBalancerService{
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{URL: "http://127.0.0.1:8085"},
{URL: "http://127.0.0.1:8086"},
@ -75,7 +75,7 @@ func TestPopulateUsedBy(t *testing.T) {
Services: map[string]*runtime.ServiceInfo{
"foo-service@myprovider": {
Service: &dynamic.Service{
LoadBalancer: &dynamic.LoadBalancerService{
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{URL: "http://127.0.0.1"},
},
@ -149,7 +149,7 @@ func TestPopulateUsedBy(t *testing.T) {
Services: map[string]*runtime.ServiceInfo{
"foo-service@myprovider": {
Service: &dynamic.Service{
LoadBalancer: &dynamic.LoadBalancerService{
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://127.0.0.1:8085",
@ -167,7 +167,7 @@ func TestPopulateUsedBy(t *testing.T) {
},
"bar-service@myprovider": {
Service: &dynamic.Service{
LoadBalancer: &dynamic.LoadBalancerService{
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://127.0.0.1:8087",
@ -222,7 +222,7 @@ func TestPopulateUsedBy(t *testing.T) {
Services: map[string]*runtime.ServiceInfo{
"foo-service@myprovider": {
Service: &dynamic.Service{
LoadBalancer: &dynamic.LoadBalancerService{
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://127.0.0.1",
@ -293,7 +293,7 @@ func TestPopulateUsedBy(t *testing.T) {
Services: map[string]*runtime.ServiceInfo{
"foo-service@myprovider": {
Service: &dynamic.Service{
LoadBalancer: &dynamic.LoadBalancerService{
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://127.0.0.1",
@ -337,7 +337,7 @@ func TestPopulateUsedBy(t *testing.T) {
Services: map[string]*runtime.ServiceInfo{
"foo-service@myprovider": {
Service: &dynamic.Service{
LoadBalancer: &dynamic.LoadBalancerService{
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://127.0.0.1",
@ -389,7 +389,7 @@ func TestPopulateUsedBy(t *testing.T) {
Services: map[string]*runtime.ServiceInfo{
"foo-service@myprovider": {
Service: &dynamic.Service{
LoadBalancer: &dynamic.LoadBalancerService{
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
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 {
configuration.Services = make(map[string]*dynamic.Service)
lb := &dynamic.LoadBalancerService{}
lb := &dynamic.ServersLoadBalancer{}
lb.SetDefaults()
configuration.Services[serviceName] = &dynamic.Service{
LoadBalancer: lb,
@ -171,7 +171,7 @@ func (p *Provider) addServerTCP(ctx context.Context, container dockerData, loadB
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)
ip, port, err := p.getIPPort(ctx, container, serverPort)
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)
}
func getLBServerPort(loadBalancer *dynamic.LoadBalancerService) string {
func getLBServerPort(loadBalancer *dynamic.ServersLoadBalancer) string {
if loadBalancer != nil && len(loadBalancer.Servers) > 0 {
return loadBalancer.Servers[0].Port
}

View file

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

View file

@ -1,6 +1,6 @@
http:
{{ range $i, $e := until 20 }}
routers:
{{ range $i, $e := until 20 }}
router{{ $e }}:
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 (
"context"
"crypto/sha256"
"errors"
"fmt"
"os"
"reflect"
@ -16,7 +15,6 @@ import (
"github.com/containous/traefik/v2/pkg/config/dynamic"
"github.com/containous/traefik/v2/pkg/job"
"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/tls"
corev1 "k8s.io/api/core/v1"
@ -136,159 +134,22 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
return nil
}
func checkStringQuoteValidity(value string) error {
_, err := strconv.Unquote(`"` + value + `"`)
return err
}
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
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),
},
}
if !exists {
return nil, errors.New("service not found")
for _, middleware := range client.GetMiddlewares() {
conf.HTTP.Middlewares[makeID(middleware.Namespace, middleware.Name)] = &middleware.Spec
}
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
return conf
}
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
}
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() {
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 checkStringQuoteValidity(value string) error {
_, err := strconv.Unquote(`"` + value + `"`)
return err
}
func makeServiceKey(rule, ingressName string) (string, error) {
@ -608,50 +228,6 @@ func shouldProcessIngress(ingressClass string, ingressClassAnnotation string) bo
(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) {
secret, exists, err := k8sClient.GetSecret(namespace, secretName)
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)
func Int(v int) *int { return &v }
func TestLoadIngressRouteTCPs(t *testing.T) {
testCases := []struct {
desc string
@ -671,7 +673,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"default/test.route-6b204d94623b3df4370c": {
LoadBalancer: &dynamic.LoadBalancerService{
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
@ -720,7 +722,7 @@ func TestLoadIngressRoutes(t *testing.T) {
},
Services: map[string]*dynamic.Service{
"default/test2.route-23c7f4c450289ee29016": {
LoadBalancer: &dynamic.LoadBalancerService{
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
@ -770,7 +772,7 @@ func TestLoadIngressRoutes(t *testing.T) {
},
Services: map[string]*dynamic.Service{
"default/test2.route-23c7f4c450289ee29016": {
LoadBalancer: &dynamic.LoadBalancerService{
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
@ -812,7 +814,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"default/test.route-6b204d94623b3df4370c": {
LoadBalancer: &dynamic.LoadBalancerService{
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
@ -825,7 +827,7 @@ func TestLoadIngressRoutes(t *testing.T) {
},
},
"default/test.route-77c62dfe9517144aeeaa": {
LoadBalancer: &dynamic.LoadBalancerService{
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
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"},
expected: &dynamic.Configuration{
TLS: &dynamic.TLSConfiguration{},
@ -863,7 +865,21 @@ func TestLoadIngressRoutes(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"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{
{
URL: "http://10.10.0.1:80",
@ -871,6 +887,77 @@ func TestLoadIngressRoutes(t *testing.T) {
{
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",
},
@ -981,7 +1068,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"default/test.route-6b204d94623b3df4370c": {
LoadBalancer: &dynamic.LoadBalancerService{
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
@ -1039,7 +1126,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"default/test.route-6b204d94623b3df4370c": {
LoadBalancer: &dynamic.LoadBalancerService{
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
@ -1097,7 +1184,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"default/test.route-6b204d94623b3df4370c": {
LoadBalancer: &dynamic.LoadBalancerService{
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
@ -1154,7 +1241,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"default/test.route-6b204d94623b3df4370c": {
LoadBalancer: &dynamic.LoadBalancerService{
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
@ -1200,7 +1287,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"default/test.route-6b204d94623b3df4370c": {
LoadBalancer: &dynamic.LoadBalancerService{
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
@ -1246,7 +1333,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"default/test.route-6b204d94623b3df4370c": {
LoadBalancer: &dynamic.LoadBalancerService{
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
@ -1284,7 +1371,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"default/test.route-6b204d94623b3df4370c": {
LoadBalancer: &dynamic.LoadBalancerService{
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
@ -1321,7 +1408,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"default/test.route-6b204d94623b3df4370c": {
LoadBalancer: &dynamic.LoadBalancerService{
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "https://10.10.0.5:8443",
@ -1358,7 +1445,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"default/test.route-6b204d94623b3df4370c": {
LoadBalancer: &dynamic.LoadBalancerService{
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "https://10.10.0.7:8443",

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -96,7 +96,7 @@ func (p *Provider) buildServiceConfiguration(ctx context.Context, service ranche
if len(configuration.Services) == 0 {
configuration.Services = make(map[string]*dynamic.Service)
lb := &dynamic.LoadBalancerService{}
lb := &dynamic.ServersLoadBalancer{}
lb.SetDefaults()
configuration.Services[serviceName] = &dynamic.Service{
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)
serverPort := getLBServerPort(loadBalancer)
@ -216,7 +216,7 @@ func (p *Provider) addServers(ctx context.Context, service rancherData, loadBala
return nil
}
func getLBServerPort(loadBalancer *dynamic.LoadBalancerService) string {
func getLBServerPort(loadBalancer *dynamic.ServersLoadBalancer) string {
if loadBalancer != nil && len(loadBalancer.Servers) > 0 {
return loadBalancer.Servers[0].Port
}

View file

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

View file

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

View file

@ -199,7 +199,7 @@ func TestServerResponseEmptyBackend(t *testing.T) {
th.WithRule(routeRule)),
),
th.WithLoadBalancerServices(th.WithService("bar",
th.WithStickiness("test")),
th.WithSticky("test")),
),
)
},
@ -229,7 +229,7 @@ func TestServerResponseEmptyBackend(t *testing.T) {
th.WithRule(routeRule)),
),
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 (
"context"
"errors"
"fmt"
"net/http"
"net/http/httputil"
@ -20,6 +21,7 @@ import (
"github.com/containous/traefik/v2/pkg/middlewares/pipelining"
"github.com/containous/traefik/v2/pkg/server/cookie"
"github.com/containous/traefik/v2/pkg/server/internal"
"github.com/containous/traefik/v2/pkg/server/service/loadbalancer/wrr"
"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)
}
// TODO Should handle multiple service types
// FIXME Check if the service is declared multiple times with different types
if conf.LoadBalancer == nil {
sErr := fmt.Errorf("the service %q doesn't have any load balancer", serviceName)
conf.AddError(sErr, true)
return nil, sErr
if conf.LoadBalancer != nil && conf.Weighted != nil {
return nil, errors.New("cannot create service: multi-types service not supported, consider declaring two different pieces of service instead")
}
lb, err := m.getLoadBalancerServiceHandler(ctx, serviceName, conf.LoadBalancer, responseModifier)
if err != nil {
conf.AddError(err, true)
return nil, err
var lb http.Handler
switch {
case conf.LoadBalancer != nil:
var err error
lb, err = m.getLoadBalancerServiceHandler(ctx, serviceName, conf.LoadBalancer, responseModifier)
if err != nil {
conf.AddError(err, true)
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
}
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(
ctx context.Context,
serviceName string,
service *dynamic.LoadBalancerService,
service *dynamic.ServersLoadBalancer,
responseModifier func(*http.Response) error,
) (http.Handler, error) {
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.Debug("Creating load-balancer")
var options []roundrobin.LBOption
var cookieName string
if stickiness := service.Stickiness; stickiness != nil {
cookieName = cookie.GetName(stickiness.CookieName, serviceName)
opts := roundrobin.CookieOptions{HTTPOnly: stickiness.HTTPOnlyCookie, Secure: stickiness.SecureCookie}
if service.Sticky != nil && service.Sticky.Cookie != nil {
cookieName = cookie.GetName(service.Sticky.Cookie.Name, serviceName)
opts := roundrobin.CookieOptions{HTTPOnly: service.Sticky.Cookie.HTTPOnly, Secure: service.Sticky.Cookie.Secure}
options = append(options, roundrobin.EnableStickySession(roundrobin.NewStickySessionWithOptions(cookieName, opts)))
logger.Debugf("Sticky session cookie name: %v", cookieName)
}

View file

@ -27,14 +27,14 @@ func TestGetLoadBalancer(t *testing.T) {
testCases := []struct {
desc string
serviceName string
service *dynamic.LoadBalancerService
service *dynamic.ServersLoadBalancer
fwd http.Handler
expectError bool
}{
{
desc: "Fails when provided an invalid URL",
serviceName: "test",
service: &dynamic.LoadBalancerService{
service: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: ":",
@ -47,15 +47,15 @@ func TestGetLoadBalancer(t *testing.T) {
{
desc: "Succeeds when there are no servers",
serviceName: "test",
service: &dynamic.LoadBalancerService{},
service: &dynamic.ServersLoadBalancer{},
fwd: &MockForwarder{},
expectError: false,
},
{
desc: "Succeeds when stickiness is set",
desc: "Succeeds when sticky.cookie is set",
serviceName: "test",
service: &dynamic.LoadBalancerService{
Stickiness: &dynamic.Stickiness{},
service: &dynamic.ServersLoadBalancer{
Sticky: &dynamic.Sticky{Cookie: &dynamic.Cookie{}},
},
fwd: &MockForwarder{},
expectError: false,
@ -114,7 +114,7 @@ func TestGetLoadBalancerServiceHandler(t *testing.T) {
testCases := []struct {
desc string
serviceName string
service *dynamic.LoadBalancerService
service *dynamic.ServersLoadBalancer
responseModifier func(*http.Response) error
expected []ExpectedResult
@ -122,7 +122,7 @@ func TestGetLoadBalancerServiceHandler(t *testing.T) {
{
desc: "Load balances between the two servers",
serviceName: "test",
service: &dynamic.LoadBalancerService{
service: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: server1.URL,
@ -146,7 +146,7 @@ func TestGetLoadBalancerServiceHandler(t *testing.T) {
{
desc: "StatusBadGateway when the server is not reachable",
serviceName: "test",
service: &dynamic.LoadBalancerService{
service: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://foo",
@ -162,7 +162,7 @@ func TestGetLoadBalancerServiceHandler(t *testing.T) {
{
desc: "ServiceUnavailable when no servers are available",
serviceName: "test",
service: &dynamic.LoadBalancerService{
service: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{},
},
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",
service: &dynamic.LoadBalancerService{
Stickiness: &dynamic.Stickiness{},
service: &dynamic.ServersLoadBalancer{
Sticky: &dynamic.Sticky{Cookie: &dynamic.Cookie{}},
Servers: []dynamic.Server{
{
URL: server1.URL,
@ -199,8 +199,8 @@ func TestGetLoadBalancerServiceHandler(t *testing.T) {
{
desc: "Sticky Cookie's options set correctly",
serviceName: "test",
service: &dynamic.LoadBalancerService{
Stickiness: &dynamic.Stickiness{HTTPOnlyCookie: true, SecureCookie: true},
service: &dynamic.ServersLoadBalancer{
Sticky: &dynamic.Sticky{Cookie: &dynamic.Cookie{HTTPOnly: true, Secure: true}},
Servers: []dynamic.Server{
{
URL: server1.URL,
@ -219,8 +219,8 @@ func TestGetLoadBalancerServiceHandler(t *testing.T) {
{
desc: "PassHost passes the host instead of the IP",
serviceName: "test",
service: &dynamic.LoadBalancerService{
Stickiness: &dynamic.Stickiness{},
service: &dynamic.ServersLoadBalancer{
Sticky: &dynamic.Sticky{Cookie: &dynamic.Cookie{}},
PassHostHeader: true,
Servers: []dynamic.Server{
{
@ -238,8 +238,8 @@ func TestGetLoadBalancerServiceHandler(t *testing.T) {
{
desc: "PassHost doesn't passe the host instead of the IP",
serviceName: "test",
service: &dynamic.LoadBalancerService{
Stickiness: &dynamic.Stickiness{},
service: &dynamic.ServersLoadBalancer{
Sticky: &dynamic.Sticky{Cookie: &dynamic.Cookie{}},
Servers: []dynamic.Server{
{
URL: serverPassHostFalse.URL,
@ -297,7 +297,7 @@ func TestManager_Build(t *testing.T) {
configs: map[string]*runtime.ServiceInfo{
"serviceName": {
Service: &dynamic.Service{
LoadBalancer: &dynamic.LoadBalancerService{},
LoadBalancer: &dynamic.ServersLoadBalancer{},
},
},
},
@ -308,7 +308,7 @@ func TestManager_Build(t *testing.T) {
configs: map[string]*runtime.ServiceInfo{
"serviceName@provider-1": {
Service: &dynamic.Service{
LoadBalancer: &dynamic.LoadBalancerService{},
LoadBalancer: &dynamic.ServersLoadBalancer{},
},
},
},
@ -319,7 +319,7 @@ func TestManager_Build(t *testing.T) {
configs: map[string]*runtime.ServiceInfo{
"serviceName@provider-1": {
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.
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) {
c.Services = make(map[string]*dynamic.Service)
for _, opt := range opts {
b := &dynamic.LoadBalancerService{}
b := &dynamic.ServersLoadBalancer{}
name := opt(b)
c.Services[name] = &dynamic.Service{
LoadBalancer: b,
@ -64,8 +64,8 @@ func WithLoadBalancerServices(opts ...func(service *dynamic.LoadBalancerService)
}
// WithService is a helper to create a configuration.
func WithService(name string, opts ...func(*dynamic.LoadBalancerService)) func(*dynamic.LoadBalancerService) string {
return func(r *dynamic.LoadBalancerService) string {
func WithService(name string, opts ...func(*dynamic.ServersLoadBalancer)) func(*dynamic.ServersLoadBalancer) string {
return func(r *dynamic.ServersLoadBalancer) string {
for _, opt := range opts {
opt(r)
}
@ -117,8 +117,8 @@ func WithRule(rule string) func(*dynamic.Router) {
}
// WithServers is a helper to create a configuration.
func WithServers(opts ...func(*dynamic.Server)) func(*dynamic.LoadBalancerService) {
return func(b *dynamic.LoadBalancerService) {
func WithServers(opts ...func(*dynamic.Server)) func(*dynamic.ServersLoadBalancer) {
return func(b *dynamic.ServersLoadBalancer) {
for _, opt := range opts {
server := dynamic.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.
func WithStickiness(cookieName string) func(*dynamic.LoadBalancerService) {
return func(b *dynamic.LoadBalancerService) {
b.Stickiness = &dynamic.Stickiness{
CookieName: cookieName,
// WithSticky is a helper to create a configuration.
func WithSticky(cookieName string) func(*dynamic.ServersLoadBalancer) {
return func(b *dynamic.ServersLoadBalancer) {
b.Sticky = &dynamic.Sticky{
Cookie: &dynamic.Cookie{Name: cookieName},
}
}
}