Add weighted round robin load balancer on TCP
Co-authored-by: Mathieu Lonjaret <mathieu.lonjaret@gmail.com>
This commit is contained in:
parent
8e18d37b3d
commit
685c6dc00c
33 changed files with 787 additions and 239 deletions
|
@ -404,13 +404,14 @@ http:
|
||||||
|
|
||||||
### General
|
### General
|
||||||
|
|
||||||
Currently, `LoadBalancer` is the only supported kind of TCP `Service`.
|
Each of the fields of the service section represents a kind of service.
|
||||||
However, since Traefik is an ever evolving project, other kind of TCP Services will be available in the future,
|
Which means, that for each specified service, one of the fields, and only one,
|
||||||
reason why you have to specify it.
|
has to be enabled to define what kind of service is created.
|
||||||
|
Currently, the two available kinds are `LoadBalancer`, and `Weighted`.
|
||||||
|
|
||||||
### Load Balancer
|
### Servers Load Balancer
|
||||||
|
|
||||||
The load balancers are able to load balance the requests between multiple instances of your programs.
|
The servers load balancer is in charge of balancing the requests between the servers of the same service.
|
||||||
|
|
||||||
??? example "Declaring a Service with Two Servers -- Using the [File Provider](../../providers/file.md)"
|
??? example "Declaring a Service with Two Servers -- Using the [File Provider](../../providers/file.md)"
|
||||||
|
|
||||||
|
@ -486,3 +487,54 @@ A negative value means an infinite deadline (i.e. the connection is never fully
|
||||||
loadBalancer:
|
loadBalancer:
|
||||||
terminationDelay: 200
|
terminationDelay: 200
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Weighted
|
||||||
|
|
||||||
|
The Weighted Round Robin (alias `WRR`) load-balancer of services is in charge of balancing the requests between multiple services based on provided weights.
|
||||||
|
|
||||||
|
This strategy is only available to load balance between [services](./index.md) and not between [servers](./index.md#servers).
|
||||||
|
|
||||||
|
This strategy can only be defined with [File](../../providers/file.md).
|
||||||
|
|
||||||
|
```toml tab="TOML"
|
||||||
|
[tcp.services]
|
||||||
|
[tcp.services.app]
|
||||||
|
[[tcp.services.app.weighted.services]]
|
||||||
|
name = "appv1"
|
||||||
|
weight = 3
|
||||||
|
[[tcp.services.app.weighted.services]]
|
||||||
|
name = "appv2"
|
||||||
|
weight = 1
|
||||||
|
|
||||||
|
[tcp.services.appv1]
|
||||||
|
[tcp.services.appv1.loadBalancer]
|
||||||
|
[[tcp.services.appv1.loadBalancer.servers]]
|
||||||
|
address = "private-ip-server-1/:8080"
|
||||||
|
|
||||||
|
[tcp.services.appv2]
|
||||||
|
[tcp.services.appv2.loadBalancer]
|
||||||
|
[[tcp.services.appv2.loadBalancer.servers]]
|
||||||
|
address = "private-ip-server-2/:8080"
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="YAML"
|
||||||
|
tcp:
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
weighted:
|
||||||
|
services:
|
||||||
|
- name: appv1
|
||||||
|
weight: 3
|
||||||
|
- name: appv2
|
||||||
|
weight: 1
|
||||||
|
|
||||||
|
appv1:
|
||||||
|
loadBalancer:
|
||||||
|
servers:
|
||||||
|
- address: "xxx.xxx.xxx.xxx:8080"
|
||||||
|
|
||||||
|
appv2:
|
||||||
|
loadBalancer:
|
||||||
|
servers:
|
||||||
|
- address: "xxx.xxx.xxx.xxx:8080"
|
||||||
|
```
|
||||||
|
|
42
integration/fixtures/tcp/wrr.toml
Normal file
42
integration/fixtures/tcp/wrr.toml
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
[global]
|
||||||
|
checkNewVersion = false
|
||||||
|
sendAnonymousUsage = false
|
||||||
|
|
||||||
|
[log]
|
||||||
|
level = "DEBUG"
|
||||||
|
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.tcp]
|
||||||
|
address = ":8093"
|
||||||
|
|
||||||
|
[api]
|
||||||
|
insecure = true
|
||||||
|
|
||||||
|
[providers.file]
|
||||||
|
filename = "{{ .SelfFilename }}"
|
||||||
|
|
||||||
|
## dynamic configuration ##
|
||||||
|
[tcp]
|
||||||
|
[tcp.routers]
|
||||||
|
[tcp.routers.to-whoami-a]
|
||||||
|
rule = "HostSNI(`whoami-a.test`)"
|
||||||
|
service = "whoami"
|
||||||
|
entryPoints = [ "tcp" ]
|
||||||
|
[tcp.routers.to-whoami-a.tls]
|
||||||
|
passthrough=true
|
||||||
|
|
||||||
|
[[tcp.services.whoami.weighted.services]]
|
||||||
|
name="whoami-a"
|
||||||
|
weight=3
|
||||||
|
[[tcp.services.whoami.weighted.services]]
|
||||||
|
name="whoami-b"
|
||||||
|
weight=1
|
||||||
|
|
||||||
|
[tcp.services.whoami-a.loadBalancer]
|
||||||
|
[[tcp.services.whoami-a.loadBalancer.servers]]
|
||||||
|
address = "localhost:8081"
|
||||||
|
|
||||||
|
[tcp.services.whoami-b.loadBalancer]
|
||||||
|
[[tcp.services.whoami-b.loadBalancer.servers]]
|
||||||
|
address = "localhost:8082"
|
||||||
|
|
|
@ -79,7 +79,7 @@ func (s *RestSuite) TestSimpleConfigurationInsecure(c *check.C) {
|
||||||
},
|
},
|
||||||
Services: map[string]*dynamic.TCPService{
|
Services: map[string]*dynamic.TCPService{
|
||||||
"service1": {
|
"service1": {
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress + ":80",
|
Address: s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress + ":80",
|
||||||
|
@ -183,7 +183,7 @@ func (s *RestSuite) TestSimpleConfiguration(c *check.C) {
|
||||||
},
|
},
|
||||||
Services: map[string]*dynamic.TCPService{
|
Services: map[string]*dynamic.TCPService{
|
||||||
"service1": {
|
"service1": {
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress + ":80",
|
Address: s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress + ":80",
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containous/traefik/v2/integration/try"
|
"github.com/containous/traefik/v2/integration/try"
|
||||||
|
@ -266,3 +267,35 @@ func guessWhoTLSMaxVersion(addr, serverName string, tlsCall bool, tlsMaxVersion
|
||||||
|
|
||||||
return string(out[:n]), nil
|
return string(out[:n]), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *TCPSuite) TestWRR(c *check.C) {
|
||||||
|
file := s.adaptFile(c, "fixtures/tcp/wrr.toml", struct{}{})
|
||||||
|
defer os.Remove(file)
|
||||||
|
|
||||||
|
cmd, display := s.traefikCmd(withConfigFile(file))
|
||||||
|
defer display(c)
|
||||||
|
|
||||||
|
err := cmd.Start()
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
|
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI"))
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
|
call := map[string]int{}
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
// Traefik passes through, termination handled by whoami-a
|
||||||
|
out, err := guessWho("127.0.0.1:8093", "whoami-a.test", true)
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
switch {
|
||||||
|
case strings.Contains(out, "whoami-a"):
|
||||||
|
call["whoami-a"]++
|
||||||
|
case strings.Contains(out, "whoami-b"):
|
||||||
|
call["whoami-b"]++
|
||||||
|
default:
|
||||||
|
call["unknown"]++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Assert(call, checker.DeepEquals, map[string]int{"whoami-a": 3, "whoami-b": 1})
|
||||||
|
}
|
||||||
|
|
|
@ -136,7 +136,7 @@ func TestHandler_Overview(t *testing.T) {
|
||||||
TCPServices: map[string]*runtime.TCPServiceInfo{
|
TCPServices: map[string]*runtime.TCPServiceInfo{
|
||||||
"tcpfoo-service@myprovider": {
|
"tcpfoo-service@myprovider": {
|
||||||
TCPService: &dynamic.TCPService{
|
TCPService: &dynamic.TCPService{
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "127.0.0.1",
|
Address: "127.0.0.1",
|
||||||
|
@ -148,7 +148,7 @@ func TestHandler_Overview(t *testing.T) {
|
||||||
},
|
},
|
||||||
"tcpbar-service@myprovider": {
|
"tcpbar-service@myprovider": {
|
||||||
TCPService: &dynamic.TCPService{
|
TCPService: &dynamic.TCPService{
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "127.0.0.2",
|
Address: "127.0.0.2",
|
||||||
|
@ -160,7 +160,7 @@ func TestHandler_Overview(t *testing.T) {
|
||||||
},
|
},
|
||||||
"tcpfii-service@myprovider": {
|
"tcpfii-service@myprovider": {
|
||||||
TCPService: &dynamic.TCPService{
|
TCPService: &dynamic.TCPService{
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "127.0.0.2",
|
Address: "127.0.0.2",
|
||||||
|
|
|
@ -255,7 +255,7 @@ func TestHandler_TCP(t *testing.T) {
|
||||||
TCPServices: map[string]*runtime.TCPServiceInfo{
|
TCPServices: map[string]*runtime.TCPServiceInfo{
|
||||||
"bar@myprovider": {
|
"bar@myprovider": {
|
||||||
TCPService: &dynamic.TCPService{
|
TCPService: &dynamic.TCPService{
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "127.0.0.1:2345",
|
Address: "127.0.0.1:2345",
|
||||||
|
@ -268,7 +268,7 @@ func TestHandler_TCP(t *testing.T) {
|
||||||
},
|
},
|
||||||
"baz@myprovider": {
|
"baz@myprovider": {
|
||||||
TCPService: &dynamic.TCPService{
|
TCPService: &dynamic.TCPService{
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "127.0.0.2:2345",
|
Address: "127.0.0.2:2345",
|
||||||
|
@ -281,7 +281,7 @@ func TestHandler_TCP(t *testing.T) {
|
||||||
},
|
},
|
||||||
"foz@myprovider": {
|
"foz@myprovider": {
|
||||||
TCPService: &dynamic.TCPService{
|
TCPService: &dynamic.TCPService{
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "127.0.0.2:2345",
|
Address: "127.0.0.2:2345",
|
||||||
|
@ -307,7 +307,7 @@ func TestHandler_TCP(t *testing.T) {
|
||||||
TCPServices: map[string]*runtime.TCPServiceInfo{
|
TCPServices: map[string]*runtime.TCPServiceInfo{
|
||||||
"bar@myprovider": {
|
"bar@myprovider": {
|
||||||
TCPService: &dynamic.TCPService{
|
TCPService: &dynamic.TCPService{
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "127.0.0.1:2345",
|
Address: "127.0.0.1:2345",
|
||||||
|
@ -320,7 +320,7 @@ func TestHandler_TCP(t *testing.T) {
|
||||||
},
|
},
|
||||||
"baz@myprovider": {
|
"baz@myprovider": {
|
||||||
TCPService: &dynamic.TCPService{
|
TCPService: &dynamic.TCPService{
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "127.0.0.2:2345",
|
Address: "127.0.0.2:2345",
|
||||||
|
@ -333,7 +333,7 @@ func TestHandler_TCP(t *testing.T) {
|
||||||
},
|
},
|
||||||
"foz@myprovider": {
|
"foz@myprovider": {
|
||||||
TCPService: &dynamic.TCPService{
|
TCPService: &dynamic.TCPService{
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "127.0.0.2:2345",
|
Address: "127.0.0.2:2345",
|
||||||
|
@ -359,7 +359,7 @@ func TestHandler_TCP(t *testing.T) {
|
||||||
TCPServices: map[string]*runtime.TCPServiceInfo{
|
TCPServices: map[string]*runtime.TCPServiceInfo{
|
||||||
"bar@myprovider": {
|
"bar@myprovider": {
|
||||||
TCPService: &dynamic.TCPService{
|
TCPService: &dynamic.TCPService{
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "127.0.0.1:2345",
|
Address: "127.0.0.1:2345",
|
||||||
|
@ -372,7 +372,7 @@ func TestHandler_TCP(t *testing.T) {
|
||||||
},
|
},
|
||||||
"baz@myprovider": {
|
"baz@myprovider": {
|
||||||
TCPService: &dynamic.TCPService{
|
TCPService: &dynamic.TCPService{
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "127.0.0.2:2345",
|
Address: "127.0.0.2:2345",
|
||||||
|
@ -385,7 +385,7 @@ func TestHandler_TCP(t *testing.T) {
|
||||||
},
|
},
|
||||||
"foz@myprovider": {
|
"foz@myprovider": {
|
||||||
TCPService: &dynamic.TCPService{
|
TCPService: &dynamic.TCPService{
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "127.0.0.2:2345",
|
Address: "127.0.0.2:2345",
|
||||||
|
@ -411,7 +411,7 @@ func TestHandler_TCP(t *testing.T) {
|
||||||
TCPServices: map[string]*runtime.TCPServiceInfo{
|
TCPServices: map[string]*runtime.TCPServiceInfo{
|
||||||
"bar@myprovider": {
|
"bar@myprovider": {
|
||||||
TCPService: &dynamic.TCPService{
|
TCPService: &dynamic.TCPService{
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "127.0.0.1:2345",
|
Address: "127.0.0.1:2345",
|
||||||
|
@ -423,7 +423,7 @@ func TestHandler_TCP(t *testing.T) {
|
||||||
},
|
},
|
||||||
"baz@myprovider": {
|
"baz@myprovider": {
|
||||||
TCPService: &dynamic.TCPService{
|
TCPService: &dynamic.TCPService{
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "127.0.0.2:2345",
|
Address: "127.0.0.2:2345",
|
||||||
|
@ -435,7 +435,7 @@ func TestHandler_TCP(t *testing.T) {
|
||||||
},
|
},
|
||||||
"test@myprovider": {
|
"test@myprovider": {
|
||||||
TCPService: &dynamic.TCPService{
|
TCPService: &dynamic.TCPService{
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "127.0.0.3:2345",
|
Address: "127.0.0.3:2345",
|
||||||
|
@ -459,7 +459,7 @@ func TestHandler_TCP(t *testing.T) {
|
||||||
TCPServices: map[string]*runtime.TCPServiceInfo{
|
TCPServices: map[string]*runtime.TCPServiceInfo{
|
||||||
"bar@myprovider": {
|
"bar@myprovider": {
|
||||||
TCPService: &dynamic.TCPService{
|
TCPService: &dynamic.TCPService{
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "127.0.0.1:2345",
|
Address: "127.0.0.1:2345",
|
||||||
|
@ -483,7 +483,7 @@ func TestHandler_TCP(t *testing.T) {
|
||||||
TCPServices: map[string]*runtime.TCPServiceInfo{
|
TCPServices: map[string]*runtime.TCPServiceInfo{
|
||||||
"bar@myprovider": {
|
"bar@myprovider": {
|
||||||
TCPService: &dynamic.TCPService{
|
TCPService: &dynamic.TCPService{
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "127.0.0.1:2345",
|
Address: "127.0.0.1:2345",
|
||||||
|
|
|
@ -91,7 +91,7 @@ func TestHandler_RawData(t *testing.T) {
|
||||||
TCPServices: map[string]*runtime.TCPServiceInfo{
|
TCPServices: map[string]*runtime.TCPServiceInfo{
|
||||||
"tcpfoo-service@myprovider": {
|
"tcpfoo-service@myprovider": {
|
||||||
TCPService: &dynamic.TCPService{
|
TCPService: &dynamic.TCPService{
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "127.0.0.1",
|
Address: "127.0.0.1",
|
||||||
|
|
|
@ -77,7 +77,7 @@ type WRRService struct {
|
||||||
Weight *int `json:"weight,omitempty" toml:"weight,omitempty" yaml:"weight,omitempty"`
|
Weight *int `json:"weight,omitempty" toml:"weight,omitempty" yaml:"weight,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetDefaults Default values for a ServersLoadBalancer.
|
// SetDefaults Default values for a WRRService.
|
||||||
func (w *WRRService) SetDefaults() {
|
func (w *WRRService) SetDefaults() {
|
||||||
defaultWeight := 1
|
defaultWeight := 1
|
||||||
w.Weight = &defaultWeight
|
w.Weight = &defaultWeight
|
||||||
|
|
|
@ -18,7 +18,29 @@ type TCPConfiguration struct {
|
||||||
|
|
||||||
// TCPService holds a tcp service configuration (can only be of one type at the same time).
|
// TCPService holds a tcp service configuration (can only be of one type at the same time).
|
||||||
type TCPService struct {
|
type TCPService struct {
|
||||||
LoadBalancer *TCPLoadBalancerService `json:"loadBalancer,omitempty" toml:"loadBalancer,omitempty" yaml:"loadBalancer,omitempty"`
|
LoadBalancer *TCPServersLoadBalancer `json:"loadBalancer,omitempty" toml:"loadBalancer,omitempty" yaml:"loadBalancer,omitempty"`
|
||||||
|
Weighted *TCPWeightedRoundRobin `json:"weighted,omitempty" toml:"weighted,omitempty" yaml:"weighted,omitempty" label:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen=true
|
||||||
|
|
||||||
|
// TCPWeightedRoundRobin is a weighted round robin tcp load-balancer of services.
|
||||||
|
type TCPWeightedRoundRobin struct {
|
||||||
|
Services []TCPWRRService `json:"services,omitempty" toml:"services,omitempty" yaml:"services,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen=true
|
||||||
|
|
||||||
|
// TCPWRRService is a reference to a tcp service load-balanced with weighted round robin.
|
||||||
|
type TCPWRRService 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 TCPWRRService.
|
||||||
|
func (w *TCPWRRService) SetDefaults() {
|
||||||
|
defaultWeight := 1
|
||||||
|
w.Weight = &defaultWeight
|
||||||
}
|
}
|
||||||
|
|
||||||
// +k8s:deepcopy-gen=true
|
// +k8s:deepcopy-gen=true
|
||||||
|
@ -43,8 +65,8 @@ type RouterTCPTLSConfig struct {
|
||||||
|
|
||||||
// +k8s:deepcopy-gen=true
|
// +k8s:deepcopy-gen=true
|
||||||
|
|
||||||
// TCPLoadBalancerService holds the LoadBalancerService configuration.
|
// TCPServersLoadBalancer holds the LoadBalancerService configuration.
|
||||||
type TCPLoadBalancerService struct {
|
type TCPServersLoadBalancer struct {
|
||||||
// TerminationDelay, corresponds to the deadline that the proxy sets, after one
|
// TerminationDelay, corresponds to the deadline that the proxy sets, after one
|
||||||
// of its connected peers indicates it has closed the writing capability of its
|
// of its connected peers indicates it has closed the writing capability of its
|
||||||
// connection, to close the reading capability as well, hence fully terminating the
|
// connection, to close the reading capability as well, hence fully terminating the
|
||||||
|
@ -54,14 +76,14 @@ type TCPLoadBalancerService struct {
|
||||||
Servers []TCPServer `json:"servers,omitempty" toml:"servers,omitempty" yaml:"servers,omitempty" label-slice-as-struct:"server"`
|
Servers []TCPServer `json:"servers,omitempty" toml:"servers,omitempty" yaml:"servers,omitempty" label-slice-as-struct:"server"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetDefaults Default values for a TCPLoadBalancerService
|
// SetDefaults Default values for a TCPServersLoadBalancer
|
||||||
func (l *TCPLoadBalancerService) SetDefaults() {
|
func (l *TCPServersLoadBalancer) SetDefaults() {
|
||||||
defaultTerminationDelay := 100 // in milliseconds
|
defaultTerminationDelay := 100 // in milliseconds
|
||||||
l.TerminationDelay = &defaultTerminationDelay
|
l.TerminationDelay = &defaultTerminationDelay
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mergeable tells if the given service is mergeable.
|
// Mergeable tells if the given service is mergeable.
|
||||||
func (l *TCPLoadBalancerService) Mergeable(loadBalancer *TCPLoadBalancerService) bool {
|
func (l *TCPServersLoadBalancer) Mergeable(loadBalancer *TCPServersLoadBalancer) bool {
|
||||||
savedServers := l.Servers
|
savedServers := l.Servers
|
||||||
defer func() {
|
defer func() {
|
||||||
l.Servers = savedServers
|
l.Servers = savedServers
|
||||||
|
|
|
@ -1152,32 +1152,6 @@ func (in *TCPConfiguration) DeepCopy() *TCPConfiguration {
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *TCPLoadBalancerService) DeepCopyInto(out *TCPLoadBalancerService) {
|
|
||||||
*out = *in
|
|
||||||
if in.TerminationDelay != nil {
|
|
||||||
in, out := &in.TerminationDelay, &out.TerminationDelay
|
|
||||||
*out = new(int)
|
|
||||||
**out = **in
|
|
||||||
}
|
|
||||||
if in.Servers != nil {
|
|
||||||
in, out := &in.Servers, &out.Servers
|
|
||||||
*out = make([]TCPServer, len(*in))
|
|
||||||
copy(*out, *in)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TCPLoadBalancerService.
|
|
||||||
func (in *TCPLoadBalancerService) DeepCopy() *TCPLoadBalancerService {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(TCPLoadBalancerService)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *TCPRouter) DeepCopyInto(out *TCPRouter) {
|
func (in *TCPRouter) DeepCopyInto(out *TCPRouter) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
@ -1220,12 +1194,43 @@ func (in *TCPServer) DeepCopy() *TCPServer {
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *TCPServersLoadBalancer) DeepCopyInto(out *TCPServersLoadBalancer) {
|
||||||
|
*out = *in
|
||||||
|
if in.TerminationDelay != nil {
|
||||||
|
in, out := &in.TerminationDelay, &out.TerminationDelay
|
||||||
|
*out = new(int)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
if in.Servers != nil {
|
||||||
|
in, out := &in.Servers, &out.Servers
|
||||||
|
*out = make([]TCPServer, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TCPServersLoadBalancer.
|
||||||
|
func (in *TCPServersLoadBalancer) DeepCopy() *TCPServersLoadBalancer {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(TCPServersLoadBalancer)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *TCPService) DeepCopyInto(out *TCPService) {
|
func (in *TCPService) DeepCopyInto(out *TCPService) {
|
||||||
*out = *in
|
*out = *in
|
||||||
if in.LoadBalancer != nil {
|
if in.LoadBalancer != nil {
|
||||||
in, out := &in.LoadBalancer, &out.LoadBalancer
|
in, out := &in.LoadBalancer, &out.LoadBalancer
|
||||||
*out = new(TCPLoadBalancerService)
|
*out = new(TCPServersLoadBalancer)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
|
if in.Weighted != nil {
|
||||||
|
in, out := &in.Weighted, &out.Weighted
|
||||||
|
*out = new(TCPWeightedRoundRobin)
|
||||||
(*in).DeepCopyInto(*out)
|
(*in).DeepCopyInto(*out)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
@ -1241,6 +1246,50 @@ func (in *TCPService) DeepCopy() *TCPService {
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *TCPWRRService) DeepCopyInto(out *TCPWRRService) {
|
||||||
|
*out = *in
|
||||||
|
if in.Weight != nil {
|
||||||
|
in, out := &in.Weight, &out.Weight
|
||||||
|
*out = new(int)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TCPWRRService.
|
||||||
|
func (in *TCPWRRService) DeepCopy() *TCPWRRService {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(TCPWRRService)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *TCPWeightedRoundRobin) DeepCopyInto(out *TCPWeightedRoundRobin) {
|
||||||
|
*out = *in
|
||||||
|
if in.Services != nil {
|
||||||
|
in, out := &in.Services, &out.Services
|
||||||
|
*out = make([]TCPWRRService, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TCPWeightedRoundRobin.
|
||||||
|
func (in *TCPWeightedRoundRobin) DeepCopy() *TCPWeightedRoundRobin {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(TCPWeightedRoundRobin)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *TLSCLientCertificateDNInfo) DeepCopyInto(out *TLSCLientCertificateDNInfo) {
|
func (in *TLSCLientCertificateDNInfo) DeepCopyInto(out *TLSCLientCertificateDNInfo) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
|
|
@ -208,7 +208,7 @@ func TestDecodeConfiguration(t *testing.T) {
|
||||||
},
|
},
|
||||||
Services: map[string]*dynamic.TCPService{
|
Services: map[string]*dynamic.TCPService{
|
||||||
"Service0": {
|
"Service0": {
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Port: "42",
|
Port: "42",
|
||||||
|
@ -218,7 +218,7 @@ func TestDecodeConfiguration(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Service1": {
|
"Service1": {
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Port: "42",
|
Port: "42",
|
||||||
|
@ -614,7 +614,7 @@ func TestEncodeConfiguration(t *testing.T) {
|
||||||
},
|
},
|
||||||
Services: map[string]*dynamic.TCPService{
|
Services: map[string]*dynamic.TCPService{
|
||||||
"Service0": {
|
"Service0": {
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Port: "42",
|
Port: "42",
|
||||||
|
@ -623,7 +623,7 @@ func TestEncodeConfiguration(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Service1": {
|
"Service1": {
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Port: "42",
|
Port: "42",
|
||||||
|
|
|
@ -488,7 +488,7 @@ func TestPopulateUsedBy(t *testing.T) {
|
||||||
TCPServices: map[string]*runtime.TCPServiceInfo{
|
TCPServices: map[string]*runtime.TCPServiceInfo{
|
||||||
"foo-service@myprovider": {
|
"foo-service@myprovider": {
|
||||||
TCPService: &dynamic.TCPService{
|
TCPService: &dynamic.TCPService{
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "127.0.0.1",
|
Address: "127.0.0.1",
|
||||||
|
@ -522,7 +522,7 @@ func TestPopulateUsedBy(t *testing.T) {
|
||||||
TCPServices: map[string]*runtime.TCPServiceInfo{
|
TCPServices: map[string]*runtime.TCPServiceInfo{
|
||||||
"foo-service@myprovider": {
|
"foo-service@myprovider": {
|
||||||
TCPService: &dynamic.TCPService{
|
TCPService: &dynamic.TCPService{
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "127.0.0.1",
|
Address: "127.0.0.1",
|
||||||
|
@ -598,7 +598,7 @@ func TestPopulateUsedBy(t *testing.T) {
|
||||||
TCPServices: map[string]*runtime.TCPServiceInfo{
|
TCPServices: map[string]*runtime.TCPServiceInfo{
|
||||||
"foo-service@myprovider": {
|
"foo-service@myprovider": {
|
||||||
TCPService: &dynamic.TCPService{
|
TCPService: &dynamic.TCPService{
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "127.0.0.1",
|
Address: "127.0.0.1",
|
||||||
|
@ -614,7 +614,7 @@ func TestPopulateUsedBy(t *testing.T) {
|
||||||
},
|
},
|
||||||
"bar-service@myprovider": {
|
"bar-service@myprovider": {
|
||||||
TCPService: &dynamic.TCPService{
|
TCPService: &dynamic.TCPService{
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "127.0.0.1",
|
Address: "127.0.0.1",
|
||||||
|
|
|
@ -78,7 +78,7 @@ func (p *Provider) buildTCPServiceConfiguration(ctx context.Context, container d
|
||||||
|
|
||||||
if len(configuration.Services) == 0 {
|
if len(configuration.Services) == 0 {
|
||||||
configuration.Services = make(map[string]*dynamic.TCPService)
|
configuration.Services = make(map[string]*dynamic.TCPService)
|
||||||
lb := &dynamic.TCPLoadBalancerService{}
|
lb := &dynamic.TCPServersLoadBalancer{}
|
||||||
lb.SetDefaults()
|
lb.SetDefaults()
|
||||||
configuration.Services[serviceName] = &dynamic.TCPService{
|
configuration.Services[serviceName] = &dynamic.TCPService{
|
||||||
LoadBalancer: lb,
|
LoadBalancer: lb,
|
||||||
|
@ -145,7 +145,7 @@ func (p *Provider) keepContainer(ctx context.Context, container dockerData) bool
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provider) addServerTCP(ctx context.Context, container dockerData, loadBalancer *dynamic.TCPLoadBalancerService) error {
|
func (p *Provider) addServerTCP(ctx context.Context, container dockerData, loadBalancer *dynamic.TCPServersLoadBalancer) error {
|
||||||
serverPort := ""
|
serverPort := ""
|
||||||
if loadBalancer != nil && len(loadBalancer.Servers) > 0 {
|
if loadBalancer != nil && len(loadBalancer.Servers) > 0 {
|
||||||
serverPort = loadBalancer.Servers[0].Port
|
serverPort = loadBalancer.Servers[0].Port
|
||||||
|
|
|
@ -2088,7 +2088,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||||
},
|
},
|
||||||
Services: map[string]*dynamic.TCPService{
|
Services: map[string]*dynamic.TCPService{
|
||||||
"Test": {
|
"Test": {
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "127.0.0.1:80",
|
Address: "127.0.0.1:80",
|
||||||
|
@ -2133,7 +2133,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||||
Routers: map[string]*dynamic.TCPRouter{},
|
Routers: map[string]*dynamic.TCPRouter{},
|
||||||
Services: map[string]*dynamic.TCPService{
|
Services: map[string]*dynamic.TCPService{
|
||||||
"Test": {
|
"Test": {
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "127.0.0.1:80",
|
Address: "127.0.0.1:80",
|
||||||
|
@ -2188,7 +2188,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||||
},
|
},
|
||||||
Services: map[string]*dynamic.TCPService{
|
Services: map[string]*dynamic.TCPService{
|
||||||
"foo": {
|
"foo": {
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "127.0.0.1:8080",
|
Address: "127.0.0.1:8080",
|
||||||
|
@ -2264,7 +2264,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||||
},
|
},
|
||||||
Services: map[string]*dynamic.TCPService{
|
Services: map[string]*dynamic.TCPService{
|
||||||
"foo": {
|
"foo": {
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "127.0.0.1:8080",
|
Address: "127.0.0.1:8080",
|
||||||
|
@ -2331,7 +2331,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||||
Routers: map[string]*dynamic.TCPRouter{},
|
Routers: map[string]*dynamic.TCPRouter{},
|
||||||
Services: map[string]*dynamic.TCPService{
|
Services: map[string]*dynamic.TCPService{
|
||||||
"foo": {
|
"foo": {
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "127.0.0.1:8080",
|
Address: "127.0.0.1:8080",
|
||||||
|
@ -2377,7 +2377,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||||
Routers: map[string]*dynamic.TCPRouter{},
|
Routers: map[string]*dynamic.TCPRouter{},
|
||||||
Services: map[string]*dynamic.TCPService{
|
Services: map[string]*dynamic.TCPService{
|
||||||
"foo": {
|
"foo": {
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "127.0.0.1:8080",
|
Address: "127.0.0.1:8080",
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: IngressRouteTCP
|
||||||
|
metadata:
|
||||||
|
name: test.route
|
||||||
|
namespace: default
|
||||||
|
|
||||||
|
spec:
|
||||||
|
entryPoints:
|
||||||
|
- foo
|
||||||
|
|
||||||
|
routes:
|
||||||
|
- match: HostSNI(`foo.com`)
|
||||||
|
services:
|
||||||
|
- name: whoamitcp
|
||||||
|
port: 8000
|
||||||
|
terminationDelay: 500
|
|
@ -13,6 +13,7 @@ spec:
|
||||||
services:
|
services:
|
||||||
- name: whoamitcp
|
- name: whoamitcp
|
||||||
port: 8000
|
port: 8000
|
||||||
|
weight: 2
|
||||||
- name: whoamitcp2
|
- name: whoamitcp2
|
||||||
port: 8080
|
port: 8080
|
||||||
|
weight: 3
|
||||||
|
|
|
@ -73,6 +73,8 @@ func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Cli
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If there is only one service defined, we skip the creation of the load balancer of services,
|
||||||
|
// i.e. the service on top is directly a load balancer of servers.
|
||||||
if len(route.Services) == 1 {
|
if len(route.Services) == 1 {
|
||||||
conf.Services[serviceName] = balancerServerHTTP
|
conf.Services[serviceName] = balancerServerHTTP
|
||||||
break
|
break
|
||||||
|
|
|
@ -49,9 +49,16 @@ func (p *Provider) loadIngressRouteTCPConfiguration(ctx context.Context, client
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
var allServers []dynamic.TCPServer
|
key, err := makeServiceKey(route.Match, ingressName)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceName := makeID(ingressRouteTCP.Namespace, key)
|
||||||
|
|
||||||
for _, service := range route.Services {
|
for _, service := range route.Services {
|
||||||
servers, err := loadTCPServers(client, ingressRouteTCP.Namespace, service)
|
balancerServerTCP, err := createLoadBalancerServerTCP(client, ingressRouteTCP.Namespace, service)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.
|
logger.
|
||||||
WithField("serviceName", service.Name).
|
WithField("serviceName", service.Name).
|
||||||
|
@ -60,16 +67,28 @@ func (p *Provider) loadIngressRouteTCPConfiguration(ctx context.Context, client
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
allServers = append(allServers, servers...)
|
// If there is only one service defined, we skip the creation of the load balancer of services,
|
||||||
|
// i.e. the service on top is directly a load balancer of servers.
|
||||||
|
if len(route.Services) == 1 {
|
||||||
|
conf.Services[serviceName] = balancerServerTCP
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceKey := fmt.Sprintf("%s-%s-%d", serviceName, service.Name, service.Port)
|
||||||
|
conf.Services[serviceKey] = balancerServerTCP
|
||||||
|
|
||||||
|
srv := dynamic.TCPWRRService{Name: serviceKey}
|
||||||
|
srv.SetDefaults()
|
||||||
|
if service.Weight != nil {
|
||||||
|
srv.Weight = service.Weight
|
||||||
|
}
|
||||||
|
|
||||||
|
if conf.Services[serviceName] == nil {
|
||||||
|
conf.Services[serviceName] = &dynamic.TCPService{Weighted: &dynamic.TCPWeightedRoundRobin{}}
|
||||||
|
}
|
||||||
|
conf.Services[serviceName].Weighted.Services = append(conf.Services[serviceName].Weighted.Services, srv)
|
||||||
}
|
}
|
||||||
|
|
||||||
key, e := makeServiceKey(route.Match, ingressName)
|
|
||||||
if e != nil {
|
|
||||||
logger.Error(e)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
serviceName := makeID(ingressRouteTCP.Namespace, key)
|
|
||||||
conf.Routers[serviceName] = &dynamic.TCPRouter{
|
conf.Routers[serviceName] = &dynamic.TCPRouter{
|
||||||
EntryPoints: ingressRouteTCP.Spec.EntryPoints,
|
EntryPoints: ingressRouteTCP.Spec.EntryPoints,
|
||||||
Rule: route.Match,
|
Rule: route.Match,
|
||||||
|
@ -83,37 +102,53 @@ func (p *Provider) loadIngressRouteTCPConfiguration(ctx context.Context, client
|
||||||
Domains: ingressRouteTCP.Spec.TLS.Domains,
|
Domains: ingressRouteTCP.Spec.TLS.Domains,
|
||||||
}
|
}
|
||||||
|
|
||||||
if ingressRouteTCP.Spec.TLS.Options != nil && len(ingressRouteTCP.Spec.TLS.Options.Name) > 0 {
|
if ingressRouteTCP.Spec.TLS.Options == nil || len(ingressRouteTCP.Spec.TLS.Options.Name) == 0 {
|
||||||
tlsOptionsName := ingressRouteTCP.Spec.TLS.Options.Name
|
continue
|
||||||
// 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
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
return conf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createLoadBalancerServerTCP(client Client, namespace string, service v1alpha1.ServiceTCP) (*dynamic.TCPService, error) {
|
||||||
|
servers, err := loadTCPServers(client, namespace, service)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tcpService := &dynamic.TCPService{
|
||||||
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
|
Servers: servers,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if service.TerminationDelay != nil {
|
||||||
|
tcpService.LoadBalancer.TerminationDelay = service.TerminationDelay
|
||||||
|
}
|
||||||
|
|
||||||
|
return tcpService, nil
|
||||||
|
}
|
||||||
|
|
||||||
func loadTCPServers(client Client, namespace string, svc v1alpha1.ServiceTCP) ([]dynamic.TCPServer, error) {
|
func loadTCPServers(client Client, namespace string, svc v1alpha1.ServiceTCP) ([]dynamic.TCPServer, error) {
|
||||||
service, exists, err := client.GetService(namespace, svc.Name)
|
service, exists, err := client.GetService(namespace, svc.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -55,7 +55,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
|
||||||
},
|
},
|
||||||
Services: map[string]*dynamic.TCPService{
|
Services: map[string]*dynamic.TCPService{
|
||||||
"default/test.route-fdd3e9338e47a45efefc": {
|
"default/test.route-fdd3e9338e47a45efefc": {
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "10.10.0.1:8000",
|
Address: "10.10.0.1:8000",
|
||||||
|
@ -92,7 +92,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
|
||||||
},
|
},
|
||||||
Services: map[string]*dynamic.TCPService{
|
Services: map[string]*dynamic.TCPService{
|
||||||
"default/test.route-fdd3e9338e47a45efefc": {
|
"default/test.route-fdd3e9338e47a45efefc": {
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "10.10.0.1:8000",
|
Address: "10.10.0.1:8000",
|
||||||
|
@ -106,7 +106,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"default/test.route-f44ce589164e656d231c": {
|
"default/test.route-f44ce589164e656d231c": {
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "10.10.0.1:8000",
|
Address: "10.10.0.1:8000",
|
||||||
|
@ -130,7 +130,7 @@ func TestLoadIngressRouteTCPs(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{"tcp/services.yml", "tcp/with_two_services.yml"},
|
paths: []string{"tcp/services.yml", "tcp/with_two_services.yml"},
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
TCP: &dynamic.TCPConfiguration{
|
TCP: &dynamic.TCPConfiguration{
|
||||||
|
@ -143,27 +143,44 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
|
||||||
},
|
},
|
||||||
Services: map[string]*dynamic.TCPService{
|
Services: map[string]*dynamic.TCPService{
|
||||||
"default/test.route-fdd3e9338e47a45efefc": {
|
"default/test.route-fdd3e9338e47a45efefc": {
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
Weighted: &dynamic.TCPWeightedRoundRobin{
|
||||||
Servers: []dynamic.TCPServer{
|
Services: []dynamic.TCPWRRService{
|
||||||
{
|
{
|
||||||
Address: "10.10.0.1:8000",
|
Name: "default/test.route-fdd3e9338e47a45efefc-whoamitcp-8000",
|
||||||
Port: "",
|
Weight: func(i int) *int { return &i }(2),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Address: "10.10.0.2:8000",
|
Name: "default/test.route-fdd3e9338e47a45efefc-whoamitcp2-8080",
|
||||||
Port: "",
|
Weight: func(i int) *int { return &i }(3),
|
||||||
},
|
|
||||||
{
|
|
||||||
Address: "10.10.0.3:8080",
|
|
||||||
Port: "",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Address: "10.10.0.4:8080",
|
|
||||||
Port: "",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}},
|
},
|
||||||
|
"default/test.route-fdd3e9338e47a45efefc-whoamitcp-8000": {
|
||||||
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
|
Servers: []dynamic.TCPServer{
|
||||||
|
{
|
||||||
|
Address: "10.10.0.1:8000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Address: "10.10.0.2:8000",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"default/test.route-fdd3e9338e47a45efefc-whoamitcp2-8080": {
|
||||||
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
|
Servers: []dynamic.TCPServer{
|
||||||
|
{
|
||||||
|
Address: "10.10.0.3:8080",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Address: "10.10.0.4:8080",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Routers: map[string]*dynamic.Router{},
|
Routers: map[string]*dynamic.Router{},
|
||||||
|
@ -247,7 +264,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
|
||||||
},
|
},
|
||||||
Services: map[string]*dynamic.TCPService{
|
Services: map[string]*dynamic.TCPService{
|
||||||
"default/test.route-fdd3e9338e47a45efefc": {
|
"default/test.route-fdd3e9338e47a45efefc": {
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "10.10.0.1:8000",
|
Address: "10.10.0.1:8000",
|
||||||
|
@ -286,7 +303,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
|
||||||
},
|
},
|
||||||
Services: map[string]*dynamic.TCPService{
|
Services: map[string]*dynamic.TCPService{
|
||||||
"default/test.route-fdd3e9338e47a45efefc": {
|
"default/test.route-fdd3e9338e47a45efefc": {
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "10.10.0.1:8000",
|
Address: "10.10.0.1:8000",
|
||||||
|
@ -345,7 +362,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
|
||||||
},
|
},
|
||||||
Services: map[string]*dynamic.TCPService{
|
Services: map[string]*dynamic.TCPService{
|
||||||
"default/test.route-fdd3e9338e47a45efefc": {
|
"default/test.route-fdd3e9338e47a45efefc": {
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "10.10.0.1:8000",
|
Address: "10.10.0.1:8000",
|
||||||
|
@ -403,7 +420,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
|
||||||
},
|
},
|
||||||
Services: map[string]*dynamic.TCPService{
|
Services: map[string]*dynamic.TCPService{
|
||||||
"default/test.route-fdd3e9338e47a45efefc": {
|
"default/test.route-fdd3e9338e47a45efefc": {
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "10.10.0.1:8000",
|
Address: "10.10.0.1:8000",
|
||||||
|
@ -460,7 +477,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
|
||||||
},
|
},
|
||||||
Services: map[string]*dynamic.TCPService{
|
Services: map[string]*dynamic.TCPService{
|
||||||
"default/test.route-fdd3e9338e47a45efefc": {
|
"default/test.route-fdd3e9338e47a45efefc": {
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "10.10.0.1:8000",
|
Address: "10.10.0.1:8000",
|
||||||
|
@ -506,7 +523,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
|
||||||
},
|
},
|
||||||
Services: map[string]*dynamic.TCPService{
|
Services: map[string]*dynamic.TCPService{
|
||||||
"default/test.route-fdd3e9338e47a45efefc": {
|
"default/test.route-fdd3e9338e47a45efefc": {
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "10.10.0.1:8000",
|
Address: "10.10.0.1:8000",
|
||||||
|
@ -552,7 +569,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
|
||||||
},
|
},
|
||||||
Services: map[string]*dynamic.TCPService{
|
Services: map[string]*dynamic.TCPService{
|
||||||
"default/test.route-fdd3e9338e47a45efefc": {
|
"default/test.route-fdd3e9338e47a45efefc": {
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "10.10.0.1:8000",
|
Address: "10.10.0.1:8000",
|
||||||
|
@ -589,7 +606,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
|
||||||
},
|
},
|
||||||
Services: map[string]*dynamic.TCPService{
|
Services: map[string]*dynamic.TCPService{
|
||||||
"default/test.route-fdd3e9338e47a45efefc": {
|
"default/test.route-fdd3e9338e47a45efefc": {
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "10.10.0.1:8000",
|
Address: "10.10.0.1:8000",
|
||||||
|
@ -612,6 +629,44 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
|
||||||
TLS: &dynamic.TLSConfiguration{},
|
TLS: &dynamic.TLSConfiguration{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
desc: "TCP with terminationDelay",
|
||||||
|
paths: []string{"tcp/services.yml", "tcp/with_termination_delay.yml"},
|
||||||
|
expected: &dynamic.Configuration{
|
||||||
|
TLS: &dynamic.TLSConfiguration{},
|
||||||
|
TCP: &dynamic.TCPConfiguration{
|
||||||
|
Routers: map[string]*dynamic.TCPRouter{
|
||||||
|
"default/test.route-fdd3e9338e47a45efefc": {
|
||||||
|
EntryPoints: []string{"foo"},
|
||||||
|
Service: "default/test.route-fdd3e9338e47a45efefc",
|
||||||
|
Rule: "HostSNI(`foo.com`)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Services: map[string]*dynamic.TCPService{
|
||||||
|
"default/test.route-fdd3e9338e47a45efefc": {
|
||||||
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
|
Servers: []dynamic.TCPServer{
|
||||||
|
{
|
||||||
|
Address: "10.10.0.1:8000",
|
||||||
|
Port: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Address: "10.10.0.2:8000",
|
||||||
|
Port: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TerminationDelay: Int(500),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
|
Routers: map[string]*dynamic.Router{},
|
||||||
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
|
Services: map[string]*dynamic.Service{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range testCases {
|
for _, test := range testCases {
|
||||||
|
|
|
@ -44,8 +44,10 @@ type TLSOptionTCPRef struct {
|
||||||
|
|
||||||
// ServiceTCP defines an upstream to proxy traffic.
|
// ServiceTCP defines an upstream to proxy traffic.
|
||||||
type ServiceTCP struct {
|
type ServiceTCP struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Port int32 `json:"port"`
|
Port int32 `json:"port"`
|
||||||
|
Weight *int `json:"weight,omitempty"`
|
||||||
|
TerminationDelay *int `json:"terminationDelay,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// +genclient
|
// +genclient
|
||||||
|
|
|
@ -612,7 +612,9 @@ func (in *RouteTCP) DeepCopyInto(out *RouteTCP) {
|
||||||
if in.Services != nil {
|
if in.Services != nil {
|
||||||
in, out := &in.Services, &out.Services
|
in, out := &in.Services, &out.Services
|
||||||
*out = make([]ServiceTCP, len(*in))
|
*out = make([]ServiceTCP, len(*in))
|
||||||
copy(*out, *in)
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -666,6 +668,16 @@ func (in *Service) DeepCopy() *Service {
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *ServiceTCP) DeepCopyInto(out *ServiceTCP) {
|
func (in *ServiceTCP) DeepCopyInto(out *ServiceTCP) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
if in.Weight != nil {
|
||||||
|
in, out := &in.Weight, &out.Weight
|
||||||
|
*out = new(int)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
if in.TerminationDelay != nil {
|
||||||
|
in, out := &in.TerminationDelay, &out.TerminationDelay
|
||||||
|
*out = new(int)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -140,7 +140,7 @@ func (p *Provider) buildTCPServiceConfiguration(ctx context.Context, app maratho
|
||||||
|
|
||||||
if len(conf.Services) == 0 {
|
if len(conf.Services) == 0 {
|
||||||
conf.Services = make(map[string]*dynamic.TCPService)
|
conf.Services = make(map[string]*dynamic.TCPService)
|
||||||
lb := &dynamic.TCPLoadBalancerService{}
|
lb := &dynamic.TCPServersLoadBalancer{}
|
||||||
lb.SetDefaults()
|
lb.SetDefaults()
|
||||||
conf.Services[appName] = &dynamic.TCPService{
|
conf.Services[appName] = &dynamic.TCPService{
|
||||||
LoadBalancer: lb,
|
LoadBalancer: lb,
|
||||||
|
|
|
@ -1236,7 +1236,7 @@ func TestBuildConfiguration(t *testing.T) {
|
||||||
},
|
},
|
||||||
Services: map[string]*dynamic.TCPService{
|
Services: map[string]*dynamic.TCPService{
|
||||||
"app": {
|
"app": {
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "localhost:80",
|
Address: "localhost:80",
|
||||||
|
@ -1268,7 +1268,7 @@ func TestBuildConfiguration(t *testing.T) {
|
||||||
Routers: map[string]*dynamic.TCPRouter{},
|
Routers: map[string]*dynamic.TCPRouter{},
|
||||||
Services: map[string]*dynamic.TCPService{
|
Services: map[string]*dynamic.TCPService{
|
||||||
"app": {
|
"app": {
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "localhost:80",
|
Address: "localhost:80",
|
||||||
|
@ -1308,7 +1308,7 @@ func TestBuildConfiguration(t *testing.T) {
|
||||||
},
|
},
|
||||||
Services: map[string]*dynamic.TCPService{
|
Services: map[string]*dynamic.TCPService{
|
||||||
"foo": {
|
"foo": {
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "localhost:8080",
|
Address: "localhost:8080",
|
||||||
|
@ -1349,7 +1349,7 @@ func TestBuildConfiguration(t *testing.T) {
|
||||||
},
|
},
|
||||||
Services: map[string]*dynamic.TCPService{
|
Services: map[string]*dynamic.TCPService{
|
||||||
"foo": {
|
"foo": {
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "localhost:8080",
|
Address: "localhost:8080",
|
||||||
|
@ -1390,7 +1390,7 @@ func TestBuildConfiguration(t *testing.T) {
|
||||||
},
|
},
|
||||||
Services: map[string]*dynamic.TCPService{
|
Services: map[string]*dynamic.TCPService{
|
||||||
"foo": {
|
"foo": {
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "localhost:8080",
|
Address: "localhost:8080",
|
||||||
|
|
|
@ -74,7 +74,7 @@ func (p *Provider) buildTCPServiceConfiguration(ctx context.Context, service ran
|
||||||
|
|
||||||
if len(configuration.Services) == 0 {
|
if len(configuration.Services) == 0 {
|
||||||
configuration.Services = make(map[string]*dynamic.TCPService)
|
configuration.Services = make(map[string]*dynamic.TCPService)
|
||||||
lb := &dynamic.TCPLoadBalancerService{}
|
lb := &dynamic.TCPServersLoadBalancer{}
|
||||||
lb.SetDefaults()
|
lb.SetDefaults()
|
||||||
configuration.Services[serviceName] = &dynamic.TCPService{
|
configuration.Services[serviceName] = &dynamic.TCPService{
|
||||||
LoadBalancer: lb,
|
LoadBalancer: lb,
|
||||||
|
@ -146,7 +146,7 @@ func (p *Provider) keepService(ctx context.Context, service rancherData) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provider) addServerTCP(ctx context.Context, service rancherData, loadBalancer *dynamic.TCPLoadBalancerService) error {
|
func (p *Provider) addServerTCP(ctx context.Context, service rancherData, loadBalancer *dynamic.TCPServersLoadBalancer) error {
|
||||||
log.FromContext(ctx).Debugf("Trying to add servers for service %s \n", service.Name)
|
log.FromContext(ctx).Debugf("Trying to add servers for service %s \n", service.Name)
|
||||||
|
|
||||||
serverPort := ""
|
serverPort := ""
|
||||||
|
|
|
@ -508,7 +508,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||||
},
|
},
|
||||||
Services: map[string]*dynamic.TCPService{
|
Services: map[string]*dynamic.TCPService{
|
||||||
"Test": {
|
"Test": {
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "127.0.0.1:80",
|
Address: "127.0.0.1:80",
|
||||||
|
@ -545,7 +545,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||||
Routers: map[string]*dynamic.TCPRouter{},
|
Routers: map[string]*dynamic.TCPRouter{},
|
||||||
Services: map[string]*dynamic.TCPService{
|
Services: map[string]*dynamic.TCPService{
|
||||||
"Test": {
|
"Test": {
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "127.0.0.1:80",
|
Address: "127.0.0.1:80",
|
||||||
|
@ -588,7 +588,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||||
},
|
},
|
||||||
Services: map[string]*dynamic.TCPService{
|
Services: map[string]*dynamic.TCPService{
|
||||||
"foo": {
|
"foo": {
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "127.0.0.1:8080",
|
Address: "127.0.0.1:8080",
|
||||||
|
@ -634,7 +634,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||||
},
|
},
|
||||||
Services: map[string]*dynamic.TCPService{
|
Services: map[string]*dynamic.TCPService{
|
||||||
"foo": {
|
"foo": {
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "127.0.0.1:8080",
|
Address: "127.0.0.1:8080",
|
||||||
|
@ -693,7 +693,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||||
Routers: map[string]*dynamic.TCPRouter{},
|
Routers: map[string]*dynamic.TCPRouter{},
|
||||||
Services: map[string]*dynamic.TCPService{
|
Services: map[string]*dynamic.TCPService{
|
||||||
"foo": {
|
"foo": {
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "127.0.0.1:8080",
|
Address: "127.0.0.1:8080",
|
||||||
|
@ -731,7 +731,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||||
Routers: map[string]*dynamic.TCPRouter{},
|
Routers: map[string]*dynamic.TCPRouter{},
|
||||||
Services: map[string]*dynamic.TCPService{
|
Services: map[string]*dynamic.TCPService{
|
||||||
"foo": {
|
"foo": {
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "127.0.0.1:8080",
|
Address: "127.0.0.1:8080",
|
||||||
|
|
|
@ -23,7 +23,7 @@ func TestRuntimeConfiguration(t *testing.T) {
|
||||||
serviceConfig: map[string]*runtime.TCPServiceInfo{
|
serviceConfig: map[string]*runtime.TCPServiceInfo{
|
||||||
"foo-service": {
|
"foo-service": {
|
||||||
TCPService: &dynamic.TCPService{
|
TCPService: &dynamic.TCPService{
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Port: "8085",
|
Port: "8085",
|
||||||
|
@ -70,7 +70,7 @@ func TestRuntimeConfiguration(t *testing.T) {
|
||||||
serviceConfig: map[string]*runtime.TCPServiceInfo{
|
serviceConfig: map[string]*runtime.TCPServiceInfo{
|
||||||
"foo-service": {
|
"foo-service": {
|
||||||
TCPService: &dynamic.TCPService{
|
TCPService: &dynamic.TCPService{
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "127.0.0.1:80",
|
Address: "127.0.0.1:80",
|
||||||
|
@ -104,7 +104,7 @@ func TestRuntimeConfiguration(t *testing.T) {
|
||||||
serviceConfig: map[string]*runtime.TCPServiceInfo{
|
serviceConfig: map[string]*runtime.TCPServiceInfo{
|
||||||
"foo-service": {
|
"foo-service": {
|
||||||
TCPService: &dynamic.TCPService{
|
TCPService: &dynamic.TCPService{
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "127.0.0.1:80",
|
Address: "127.0.0.1:80",
|
||||||
|
@ -137,7 +137,7 @@ func TestRuntimeConfiguration(t *testing.T) {
|
||||||
serviceConfig: map[string]*runtime.TCPServiceInfo{
|
serviceConfig: map[string]*runtime.TCPServiceInfo{
|
||||||
"foo-service": {
|
"foo-service": {
|
||||||
TCPService: &dynamic.TCPService{
|
TCPService: &dynamic.TCPService{
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "127.0.0.1:80",
|
Address: "127.0.0.1:80",
|
||||||
|
|
|
@ -93,7 +93,9 @@ func (m *Manager) BuildHTTP(rootCtx context.Context, serviceName string, respons
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if count > 1 {
|
if count > 1 {
|
||||||
return nil, errors.New("cannot create service: multi-types service not supported, consider declaring two different pieces of service instead")
|
err := errors.New("cannot create service: multi-types service not supported, consider declaring two different pieces of service instead")
|
||||||
|
conf.AddError(err, true)
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var lb http.Handler
|
var lb http.Handler
|
||||||
|
|
|
@ -2,6 +2,7 @@ package tcp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
@ -30,41 +31,59 @@ func (m *Manager) BuildTCP(rootCtx context.Context, serviceName string) (tcp.Han
|
||||||
ctx := internal.AddProviderInContext(rootCtx, serviceQualifiedName)
|
ctx := internal.AddProviderInContext(rootCtx, serviceQualifiedName)
|
||||||
ctx = log.With(ctx, log.Str(log.ServiceName, serviceName))
|
ctx = log.With(ctx, log.Str(log.ServiceName, serviceName))
|
||||||
|
|
||||||
// FIXME Check if the service is declared multiple times with different types
|
|
||||||
conf, ok := m.configs[serviceQualifiedName]
|
conf, ok := m.configs[serviceQualifiedName]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("the service %q does not exist", serviceQualifiedName)
|
return nil, fmt.Errorf("the service %q does not exist", serviceQualifiedName)
|
||||||
}
|
}
|
||||||
if conf.LoadBalancer == nil {
|
|
||||||
err := fmt.Errorf("the service %q doesn't have any TCP load balancer", serviceQualifiedName)
|
if conf.LoadBalancer != nil && conf.Weighted != nil {
|
||||||
|
err := errors.New("cannot create service: multi-types service not supported, consider declaring two different pieces of service instead")
|
||||||
conf.AddError(err, true)
|
conf.AddError(err, true)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
logger := log.FromContext(ctx)
|
logger := log.FromContext(ctx)
|
||||||
|
switch {
|
||||||
|
case conf.LoadBalancer != nil:
|
||||||
|
loadBalancer := tcp.NewWRRLoadBalancer()
|
||||||
|
|
||||||
loadBalancer := tcp.NewRRLoadBalancer()
|
if conf.LoadBalancer.TerminationDelay == nil {
|
||||||
|
defaultTerminationDelay := 100
|
||||||
if conf.LoadBalancer.TerminationDelay == nil {
|
conf.LoadBalancer.TerminationDelay = &defaultTerminationDelay
|
||||||
defaultTerminationDelay := 100
|
|
||||||
conf.LoadBalancer.TerminationDelay = &defaultTerminationDelay
|
|
||||||
}
|
|
||||||
duration := time.Millisecond * time.Duration(*conf.LoadBalancer.TerminationDelay)
|
|
||||||
|
|
||||||
for name, server := range conf.LoadBalancer.Servers {
|
|
||||||
if _, _, err := net.SplitHostPort(server.Address); err != nil {
|
|
||||||
logger.Errorf("In service %q: %v", serviceQualifiedName, err)
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
duration := time.Millisecond * time.Duration(*conf.LoadBalancer.TerminationDelay)
|
||||||
|
|
||||||
handler, err := tcp.NewProxy(server.Address, duration)
|
for name, server := range conf.LoadBalancer.Servers {
|
||||||
if err != nil {
|
if _, _, err := net.SplitHostPort(server.Address); err != nil {
|
||||||
logger.Errorf("In service %q server %q: %v", serviceQualifiedName, server.Address, err)
|
logger.Errorf("In service %q: %v", serviceQualifiedName, err)
|
||||||
continue
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
handler, err := tcp.NewProxy(server.Address, duration)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("In service %q server %q: %v", serviceQualifiedName, server.Address, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
loadBalancer.AddServer(handler)
|
||||||
|
logger.WithField(log.ServerName, name).Debugf("Creating TCP server %d at %s", name, server.Address)
|
||||||
}
|
}
|
||||||
|
return loadBalancer, nil
|
||||||
loadBalancer.AddServer(handler)
|
case conf.Weighted != nil:
|
||||||
logger.WithField(log.ServerName, name).Debugf("Creating TCP server %d at %s", name, server.Address)
|
loadBalancer := tcp.NewWRRLoadBalancer()
|
||||||
|
for _, service := range conf.Weighted.Services {
|
||||||
|
handler, err := m.BuildTCP(rootCtx, service.Name)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("In service %q: %v", serviceQualifiedName, err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
loadBalancer.AddWeightServer(handler, service.Weight)
|
||||||
|
}
|
||||||
|
return loadBalancer, nil
|
||||||
|
default:
|
||||||
|
err := fmt.Errorf("the service %q doesn't have any TCP load balancer", serviceQualifiedName)
|
||||||
|
conf.AddError(err, true)
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
return loadBalancer, nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ func TestManager_BuildTCP(t *testing.T) {
|
||||||
configs: map[string]*runtime.TCPServiceInfo{
|
configs: map[string]*runtime.TCPServiceInfo{
|
||||||
"test": {
|
"test": {
|
||||||
TCPService: &dynamic.TCPService{
|
TCPService: &dynamic.TCPService{
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{Address: "test:31"},
|
{Address: "test:31"},
|
||||||
},
|
},
|
||||||
|
@ -56,7 +56,7 @@ func TestManager_BuildTCP(t *testing.T) {
|
||||||
configs: map[string]*runtime.TCPServiceInfo{
|
configs: map[string]*runtime.TCPServiceInfo{
|
||||||
"test": {
|
"test": {
|
||||||
TCPService: &dynamic.TCPService{
|
TCPService: &dynamic.TCPService{
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{Address: "foobar"},
|
{Address: "foobar"},
|
||||||
},
|
},
|
||||||
|
@ -71,7 +71,7 @@ func TestManager_BuildTCP(t *testing.T) {
|
||||||
configs: map[string]*runtime.TCPServiceInfo{
|
configs: map[string]*runtime.TCPServiceInfo{
|
||||||
"serviceName": {
|
"serviceName": {
|
||||||
TCPService: &dynamic.TCPService{
|
TCPService: &dynamic.TCPService{
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{},
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -82,7 +82,7 @@ func TestManager_BuildTCP(t *testing.T) {
|
||||||
configs: map[string]*runtime.TCPServiceInfo{
|
configs: map[string]*runtime.TCPServiceInfo{
|
||||||
"serviceName@provider-1": {
|
"serviceName@provider-1": {
|
||||||
TCPService: &dynamic.TCPService{
|
TCPService: &dynamic.TCPService{
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{},
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -93,7 +93,7 @@ func TestManager_BuildTCP(t *testing.T) {
|
||||||
configs: map[string]*runtime.TCPServiceInfo{
|
configs: map[string]*runtime.TCPServiceInfo{
|
||||||
"serviceName@provider-1": {
|
"serviceName@provider-1": {
|
||||||
TCPService: &dynamic.TCPService{
|
TCPService: &dynamic.TCPService{
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{},
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -105,7 +105,7 @@ func TestManager_BuildTCP(t *testing.T) {
|
||||||
configs: map[string]*runtime.TCPServiceInfo{
|
configs: map[string]*runtime.TCPServiceInfo{
|
||||||
"serviceName@provider-1": {
|
"serviceName@provider-1": {
|
||||||
TCPService: &dynamic.TCPService{
|
TCPService: &dynamic.TCPService{
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "foobar.com:80",
|
Address: "foobar.com:80",
|
||||||
|
@ -123,7 +123,7 @@ func TestManager_BuildTCP(t *testing.T) {
|
||||||
configs: map[string]*runtime.TCPServiceInfo{
|
configs: map[string]*runtime.TCPServiceInfo{
|
||||||
"serviceName@provider-1": {
|
"serviceName@provider-1": {
|
||||||
TCPService: &dynamic.TCPService{
|
TCPService: &dynamic.TCPService{
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "192.168.0.12:80",
|
Address: "192.168.0.12:80",
|
||||||
|
@ -141,7 +141,7 @@ func TestManager_BuildTCP(t *testing.T) {
|
||||||
configs: map[string]*runtime.TCPServiceInfo{
|
configs: map[string]*runtime.TCPServiceInfo{
|
||||||
"serviceName@provider-1": {
|
"serviceName@provider-1": {
|
||||||
TCPService: &dynamic.TCPService{
|
TCPService: &dynamic.TCPService{
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "foobar.com",
|
Address: "foobar.com",
|
||||||
|
@ -159,7 +159,7 @@ func TestManager_BuildTCP(t *testing.T) {
|
||||||
configs: map[string]*runtime.TCPServiceInfo{
|
configs: map[string]*runtime.TCPServiceInfo{
|
||||||
"serviceName@provider-1": {
|
"serviceName@provider-1": {
|
||||||
TCPService: &dynamic.TCPService{
|
TCPService: &dynamic.TCPService{
|
||||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
Servers: []dynamic.TCPServer{
|
Servers: []dynamic.TCPServer{
|
||||||
{
|
{
|
||||||
Address: "192.168.0.12",
|
Address: "192.168.0.12",
|
||||||
|
|
|
@ -58,13 +58,14 @@ func (p Proxy) connCopy(dst, src WriteCloser, errCh chan error) {
|
||||||
|
|
||||||
errClose := dst.CloseWrite()
|
errClose := dst.CloseWrite()
|
||||||
if errClose != nil {
|
if errClose != nil {
|
||||||
log.WithoutContext().Errorf("Error while terminating connection: %v", errClose)
|
log.WithoutContext().Debugf("Error while terminating connection: %v", errClose)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.terminationDelay >= 0 {
|
if p.terminationDelay >= 0 {
|
||||||
err := dst.SetReadDeadline(time.Now().Add(p.terminationDelay))
|
err := dst.SetReadDeadline(time.Now().Add(p.terminationDelay))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithoutContext().Errorf("Error while setting deadline: %v", err)
|
log.WithoutContext().Debugf("Error while setting deadline: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
package tcp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/containous/traefik/v2/pkg/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
// RRLoadBalancer is a naive RoundRobin load balancer for TCP services
|
|
||||||
type RRLoadBalancer struct {
|
|
||||||
servers []Handler
|
|
||||||
lock sync.RWMutex
|
|
||||||
current int
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewRRLoadBalancer creates a new RRLoadBalancer
|
|
||||||
func NewRRLoadBalancer() *RRLoadBalancer {
|
|
||||||
return &RRLoadBalancer{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServeTCP forwards the connection to the right service
|
|
||||||
func (r *RRLoadBalancer) ServeTCP(conn WriteCloser) {
|
|
||||||
if len(r.servers) == 0 {
|
|
||||||
log.WithoutContext().Error("no available server")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
r.next().ServeTCP(conn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddServer appends a server to the existing list
|
|
||||||
func (r *RRLoadBalancer) AddServer(server Handler) {
|
|
||||||
r.servers = append(r.servers, server)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *RRLoadBalancer) next() Handler {
|
|
||||||
r.lock.Lock()
|
|
||||||
defer r.lock.Unlock()
|
|
||||||
|
|
||||||
if r.current >= len(r.servers) {
|
|
||||||
r.current = 0
|
|
||||||
log.WithoutContext().Debugf("Load balancer: going back to the first available server")
|
|
||||||
}
|
|
||||||
|
|
||||||
handler := r.servers[r.current]
|
|
||||||
r.current++
|
|
||||||
return handler
|
|
||||||
}
|
|
122
pkg/tcp/wrr_load_balancer.go
Normal file
122
pkg/tcp/wrr_load_balancer.go
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
package tcp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/containous/traefik/v2/pkg/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type server struct {
|
||||||
|
Handler
|
||||||
|
weight int
|
||||||
|
}
|
||||||
|
|
||||||
|
// WRRLoadBalancer is a naive RoundRobin load balancer for TCP services
|
||||||
|
type WRRLoadBalancer struct {
|
||||||
|
servers []server
|
||||||
|
lock sync.RWMutex
|
||||||
|
currentWeight int
|
||||||
|
index int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWRRLoadBalancer creates a new WRRLoadBalancer
|
||||||
|
func NewWRRLoadBalancer() *WRRLoadBalancer {
|
||||||
|
return &WRRLoadBalancer{
|
||||||
|
index: -1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServeTCP forwards the connection to the right service
|
||||||
|
func (b *WRRLoadBalancer) ServeTCP(conn WriteCloser) {
|
||||||
|
if len(b.servers) == 0 {
|
||||||
|
log.WithoutContext().Error("no available server")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
next, err := b.next()
|
||||||
|
if err != nil {
|
||||||
|
log.WithoutContext().Errorf("Error during load balancing: %v", err)
|
||||||
|
conn.Close()
|
||||||
|
}
|
||||||
|
next.ServeTCP(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddServer appends a server to the existing list
|
||||||
|
func (b *WRRLoadBalancer) AddServer(serverHandler Handler) {
|
||||||
|
w := 1
|
||||||
|
b.AddWeightServer(serverHandler, &w)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddWeightServer appends a server to the existing list with a weight
|
||||||
|
func (b *WRRLoadBalancer) AddWeightServer(serverHandler Handler, weight *int) {
|
||||||
|
w := 1
|
||||||
|
if weight != nil {
|
||||||
|
w = *weight
|
||||||
|
}
|
||||||
|
b.servers = append(b.servers, server{Handler: serverHandler, weight: w})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *WRRLoadBalancer) maxWeight() int {
|
||||||
|
max := -1
|
||||||
|
for _, s := range b.servers {
|
||||||
|
if s.weight > max {
|
||||||
|
max = s.weight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return max
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *WRRLoadBalancer) weightGcd() int {
|
||||||
|
divisor := -1
|
||||||
|
for _, s := range b.servers {
|
||||||
|
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 *WRRLoadBalancer) next() (Handler, error) {
|
||||||
|
b.lock.Lock()
|
||||||
|
defer b.lock.Unlock()
|
||||||
|
|
||||||
|
if len(b.servers) == 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.servers)
|
||||||
|
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.servers[b.index]
|
||||||
|
if srv.weight >= b.currentWeight {
|
||||||
|
return srv, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
131
pkg/tcp/wrr_load_balancer_test.go
Normal file
131
pkg/tcp/wrr_load_balancer_test.go
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
package tcp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
type fakeConn struct {
|
||||||
|
call map[string]int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fakeConn) Read(b []byte) (n int, err error) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fakeConn) Write(b []byte) (n int, err error) {
|
||||||
|
f.call[string(b)]++
|
||||||
|
return len(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fakeConn) Close() error {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fakeConn) LocalAddr() net.Addr {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fakeConn) RemoteAddr() net.Addr {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fakeConn) SetDeadline(t time.Time) error {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fakeConn) SetReadDeadline(t time.Time) error {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fakeConn) SetWriteDeadline(t time.Time) error {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fakeConn) CloseWrite() error {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadBalancing(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
serversWeight map[string]int
|
||||||
|
totalCall int
|
||||||
|
expected map[string]int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "RoundRobin",
|
||||||
|
serversWeight: map[string]int{
|
||||||
|
"h1": 1,
|
||||||
|
"h2": 1,
|
||||||
|
},
|
||||||
|
totalCall: 4,
|
||||||
|
expected: map[string]int{
|
||||||
|
"h1": 2,
|
||||||
|
"h2": 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "WeighedRoundRobin",
|
||||||
|
serversWeight: map[string]int{
|
||||||
|
"h1": 3,
|
||||||
|
"h2": 1,
|
||||||
|
},
|
||||||
|
totalCall: 4,
|
||||||
|
expected: map[string]int{
|
||||||
|
"h1": 3,
|
||||||
|
"h2": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "WeighedRoundRobin with more call",
|
||||||
|
serversWeight: map[string]int{
|
||||||
|
"h1": 3,
|
||||||
|
"h2": 1,
|
||||||
|
},
|
||||||
|
totalCall: 16,
|
||||||
|
expected: map[string]int{
|
||||||
|
"h1": 12,
|
||||||
|
"h2": 4,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "WeighedRoundRobin with 0 weight server",
|
||||||
|
serversWeight: map[string]int{
|
||||||
|
"h1": 3,
|
||||||
|
"h2": 0,
|
||||||
|
},
|
||||||
|
totalCall: 16,
|
||||||
|
expected: map[string]int{
|
||||||
|
"h1": 16,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
balancer := NewWRRLoadBalancer()
|
||||||
|
for server, weight := range test.serversWeight {
|
||||||
|
server := server
|
||||||
|
balancer.AddWeightServer(HandlerFunc(func(conn WriteCloser) {
|
||||||
|
_, err := conn.Write([]byte(server))
|
||||||
|
require.NoError(t, err)
|
||||||
|
}), &weight)
|
||||||
|
}
|
||||||
|
|
||||||
|
conn := &fakeConn{call: make(map[string]int)}
|
||||||
|
for i := 0; i < test.totalCall; i++ {
|
||||||
|
balancer.ServeTCP(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, test.expected, conn.call)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue