Merge tag 'v1.4.0-rc5' into master
This commit is contained in:
commit
9faae7387e
52 changed files with 930 additions and 416 deletions
57
server/cookie/cookie.go
Normal file
57
server/cookie/cookie.go
Normal file
|
@ -0,0 +1,57 @@
|
|||
package cookie
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/containous/traefik/log"
|
||||
)
|
||||
|
||||
const cookieNameLength = 6
|
||||
|
||||
// GetName of a cookie
|
||||
func GetName(cookieName string, backendName string) string {
|
||||
if len(cookieName) != 0 {
|
||||
return sanitizeName(cookieName)
|
||||
}
|
||||
|
||||
return GenerateName(backendName)
|
||||
}
|
||||
|
||||
// GenerateName Generate a hashed name
|
||||
func GenerateName(backendName string) string {
|
||||
data := []byte("_TRAEFIK_BACKEND_" + backendName)
|
||||
|
||||
hash := sha1.New()
|
||||
_, err := hash.Write(data)
|
||||
if err != nil {
|
||||
// Impossible case
|
||||
log.Errorf("Fail to create cookie name: %v", err)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("_%x", hash.Sum(nil))[:cookieNameLength]
|
||||
}
|
||||
|
||||
// sanitizeName According to [RFC 2616](https://www.ietf.org/rfc/rfc2616.txt) section 2.2
|
||||
func sanitizeName(backend string) string {
|
||||
sanitizer := func(r rune) rune {
|
||||
switch r {
|
||||
case '!', '#', '$', '%', '&', '\'', '*', '+', '-', '.', '^', '`', '|', '~':
|
||||
return r
|
||||
}
|
||||
|
||||
switch {
|
||||
case 'a' <= r && r <= 'z':
|
||||
fallthrough
|
||||
case 'A' <= r && r <= 'Z':
|
||||
fallthrough
|
||||
case '0' <= r && r <= '9':
|
||||
return r
|
||||
default:
|
||||
return '_'
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Map(sanitizer, backend)
|
||||
}
|
83
server/cookie/cookie_test.go
Normal file
83
server/cookie/cookie_test.go
Normal file
|
@ -0,0 +1,83 @@
|
|||
package cookie
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetName(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
cookieName string
|
||||
backendName string
|
||||
expectedCookieName string
|
||||
}{
|
||||
{
|
||||
desc: "with backend name, without cookie name",
|
||||
cookieName: "",
|
||||
backendName: "/my/BACKEND-v1.0~rc1",
|
||||
expectedCookieName: "_5f7bc",
|
||||
},
|
||||
{
|
||||
desc: "without backend name, with cookie name",
|
||||
cookieName: "/my/BACKEND-v1.0~rc1",
|
||||
backendName: "",
|
||||
expectedCookieName: "_my_BACKEND-v1.0~rc1",
|
||||
},
|
||||
{
|
||||
desc: "with backend name, with cookie name",
|
||||
cookieName: "containous",
|
||||
backendName: "treafik",
|
||||
expectedCookieName: "containous",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cookieName := GetName(test.cookieName, test.backendName)
|
||||
|
||||
assert.Equal(t, test.expectedCookieName, cookieName)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_sanitizeName(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
srcName string
|
||||
expectedName string
|
||||
}{
|
||||
{
|
||||
desc: "with /",
|
||||
srcName: "/my/BACKEND-v1.0~rc1",
|
||||
expectedName: "_my_BACKEND-v1.0~rc1",
|
||||
},
|
||||
{
|
||||
desc: "some chars",
|
||||
srcName: "!#$%&'()*+-./:<=>?@[]^_`{|}~",
|
||||
expectedName: "!#$%&'__*+-._________^_`_|_~",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cookieName := sanitizeName(test.srcName)
|
||||
|
||||
assert.Equal(t, test.expectedName, cookieName, "Cookie name")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateName(t *testing.T) {
|
||||
cookieName := GenerateName("containous")
|
||||
|
||||
assert.Len(t, "_8a7bc", 6)
|
||||
assert.Equal(t, "_8a7bc", cookieName)
|
||||
}
|
|
@ -6,6 +6,7 @@ import (
|
|||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
|
@ -30,7 +31,9 @@ import (
|
|||
mauth "github.com/containous/traefik/middlewares/auth"
|
||||
"github.com/containous/traefik/provider"
|
||||
"github.com/containous/traefik/safe"
|
||||
"github.com/containous/traefik/server/cookie"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/containous/traefik/whitelist"
|
||||
"github.com/streamrail/concurrent-map"
|
||||
thoas_stats "github.com/thoas/stats"
|
||||
"github.com/urfave/negroni"
|
||||
|
@ -154,8 +157,8 @@ func createHTTPTransport(globalConfiguration configuration.GlobalConfiguration)
|
|||
transport.TLSClientConfig = &tls.Config{
|
||||
RootCAs: createRootCACertPool(globalConfiguration.RootCAs),
|
||||
}
|
||||
http2.ConfigureTransport(transport)
|
||||
}
|
||||
http2.ConfigureTransport(transport)
|
||||
|
||||
return transport
|
||||
}
|
||||
|
@ -335,11 +338,11 @@ func (server *Server) listenProviders(stop chan bool) {
|
|||
lastReceivedConfigurationValue := lastReceivedConfiguration.Get().(time.Time)
|
||||
providersThrottleDuration := time.Duration(server.globalConfiguration.ProvidersThrottleDuration)
|
||||
if time.Now().After(lastReceivedConfigurationValue.Add(providersThrottleDuration)) {
|
||||
log.Debugf("Last %s config received more than %s, OK", configMsg.ProviderName, server.globalConfiguration.ProvidersThrottleDuration.String())
|
||||
log.Debugf("Last %s config received more than %s, OK", configMsg.ProviderName, server.globalConfiguration.ProvidersThrottleDuration)
|
||||
// last config received more than n s ago
|
||||
server.configurationValidatedChan <- configMsg
|
||||
} else {
|
||||
log.Debugf("Last %s config received less than %s, waiting...", configMsg.ProviderName, server.globalConfiguration.ProvidersThrottleDuration.String())
|
||||
log.Debugf("Last %s config received less than %s, waiting...", configMsg.ProviderName, server.globalConfiguration.ProvidersThrottleDuration)
|
||||
safe.Go(func() {
|
||||
<-time.After(providersThrottleDuration)
|
||||
lastReceivedConfigurationValue := lastReceivedConfiguration.Get().(time.Time)
|
||||
|
@ -652,8 +655,22 @@ func (server *Server) prepareServer(entryPointName string, entryPoint *configura
|
|||
return nil, nil, err
|
||||
}
|
||||
|
||||
if entryPoint.ProxyProtocol {
|
||||
listener = &proxyproto.Listener{Listener: listener}
|
||||
if entryPoint.ProxyProtocol != nil {
|
||||
IPs, err := whitelist.NewIP(entryPoint.ProxyProtocol.TrustedIPs)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("Error creating whitelist: %s", err)
|
||||
}
|
||||
log.Infof("Enabling ProxyProtocol for trusted IPs %v", entryPoint.ProxyProtocol.TrustedIPs)
|
||||
listener = &proxyproto.Listener{
|
||||
Listener: listener,
|
||||
SourceCheck: func(addr net.Addr) (bool, error) {
|
||||
ip, ok := addr.(*net.TCPAddr)
|
||||
if !ok {
|
||||
return false, fmt.Errorf("Type error %v", addr)
|
||||
}
|
||||
return IPs.ContainsIP(ip.IP)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return &http.Server{
|
||||
|
@ -826,11 +843,10 @@ func (server *Server) loadConfig(configurations types.Configurations, globalConf
|
|||
continue frontend
|
||||
}
|
||||
|
||||
stickySession := config.Backends[frontend.Backend].LoadBalancer.Sticky
|
||||
cookieName := "_TRAEFIK_BACKEND_" + frontend.Backend
|
||||
var sticky *roundrobin.StickySession
|
||||
|
||||
if stickySession {
|
||||
var cookieName string
|
||||
if stickiness := config.Backends[frontend.Backend].LoadBalancer.Stickiness; stickiness != nil {
|
||||
cookieName = cookie.GetName(stickiness.CookieName, frontend.Backend)
|
||||
sticky = roundrobin.NewStickySession(cookieName)
|
||||
}
|
||||
|
||||
|
@ -839,7 +855,7 @@ func (server *Server) loadConfig(configurations types.Configurations, globalConf
|
|||
case types.Drr:
|
||||
log.Debugf("Creating load-balancer drr")
|
||||
rebalancer, _ := roundrobin.NewRebalancer(rr, roundrobin.RebalancerLogger(oxyLogger))
|
||||
if stickySession {
|
||||
if sticky != nil {
|
||||
log.Debugf("Sticky session with cookie %v", cookieName)
|
||||
rebalancer, _ = roundrobin.NewRebalancer(rr, roundrobin.RebalancerLogger(oxyLogger), roundrobin.RebalancerStickySession(sticky))
|
||||
}
|
||||
|
@ -856,7 +872,7 @@ func (server *Server) loadConfig(configurations types.Configurations, globalConf
|
|||
lb = middlewares.NewEmptyBackendHandler(rebalancer, lb)
|
||||
case types.Wrr:
|
||||
log.Debugf("Creating load-balancer wrr")
|
||||
if stickySession {
|
||||
if sticky != nil {
|
||||
log.Debugf("Sticky session with cookie %v", cookieName)
|
||||
if server.accessLoggerMiddleware != nil {
|
||||
rr, _ = roundrobin.New(saveFrontend, roundrobin.EnableStickySession(sticky))
|
||||
|
@ -1159,16 +1175,31 @@ func (server *Server) configureFrontends(frontends map[string]*types.Frontend) {
|
|||
|
||||
func (*Server) configureBackends(backends map[string]*types.Backend) {
|
||||
for backendName, backend := range backends {
|
||||
if backend.LoadBalancer != nil && backend.LoadBalancer.Sticky {
|
||||
log.Warn("Deprecated configuration found: %s. Please use %s.", "backend.LoadBalancer.Sticky", "backend.LoadBalancer.Stickiness")
|
||||
}
|
||||
|
||||
_, err := types.NewLoadBalancerMethod(backend.LoadBalancer)
|
||||
if err != nil {
|
||||
if err == nil {
|
||||
if backend.LoadBalancer != nil && backend.LoadBalancer.Stickiness == nil && backend.LoadBalancer.Sticky {
|
||||
backend.LoadBalancer.Stickiness = &types.Stickiness{}
|
||||
}
|
||||
} else {
|
||||
log.Debugf("Validation of load balancer method for backend %s failed: %s. Using default method wrr.", backendName, err)
|
||||
var sticky bool
|
||||
|
||||
var stickiness *types.Stickiness
|
||||
if backend.LoadBalancer != nil {
|
||||
sticky = backend.LoadBalancer.Sticky
|
||||
if backend.LoadBalancer.Stickiness != nil {
|
||||
stickiness = backend.LoadBalancer.Stickiness
|
||||
} else if backend.LoadBalancer.Sticky {
|
||||
if backend.LoadBalancer.Stickiness == nil {
|
||||
stickiness = &types.Stickiness{}
|
||||
}
|
||||
}
|
||||
}
|
||||
backend.LoadBalancer = &types.LoadBalancer{
|
||||
Method: "wrr",
|
||||
Sticky: sticky,
|
||||
Method: "wrr",
|
||||
Stickiness: stickiness,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -355,7 +355,7 @@ func TestNewServerWithWhitelistSourceRange(t *testing.T) {
|
|||
"foo",
|
||||
},
|
||||
middlewareConfigured: false,
|
||||
errMessage: "parsing CIDR whitelist <nil>: invalid CIDR address: foo",
|
||||
errMessage: "parsing CIDR whitelist [foo]: parsing CIDR whitelist <nil>: invalid CIDR address: foo",
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -422,52 +422,49 @@ func TestConfigureBackends(t *testing.T) {
|
|||
defaultMethod := "wrr"
|
||||
|
||||
tests := []struct {
|
||||
desc string
|
||||
lb *types.LoadBalancer
|
||||
wantMethod string
|
||||
wantSticky bool
|
||||
desc string
|
||||
lb *types.LoadBalancer
|
||||
wantMethod string
|
||||
wantStickiness *types.Stickiness
|
||||
}{
|
||||
{
|
||||
desc: "valid load balancer method with sticky enabled",
|
||||
lb: &types.LoadBalancer{
|
||||
Method: validMethod,
|
||||
Sticky: true,
|
||||
Method: validMethod,
|
||||
Stickiness: &types.Stickiness{},
|
||||
},
|
||||
wantMethod: validMethod,
|
||||
wantSticky: true,
|
||||
wantMethod: validMethod,
|
||||
wantStickiness: &types.Stickiness{},
|
||||
},
|
||||
{
|
||||
desc: "valid load balancer method with sticky disabled",
|
||||
lb: &types.LoadBalancer{
|
||||
Method: validMethod,
|
||||
Sticky: false,
|
||||
Method: validMethod,
|
||||
Stickiness: nil,
|
||||
},
|
||||
wantMethod: validMethod,
|
||||
wantSticky: false,
|
||||
},
|
||||
{
|
||||
desc: "invalid load balancer method with sticky enabled",
|
||||
lb: &types.LoadBalancer{
|
||||
Method: "Invalid",
|
||||
Sticky: true,
|
||||
Method: "Invalid",
|
||||
Stickiness: &types.Stickiness{},
|
||||
},
|
||||
wantMethod: defaultMethod,
|
||||
wantSticky: true,
|
||||
wantMethod: defaultMethod,
|
||||
wantStickiness: &types.Stickiness{},
|
||||
},
|
||||
{
|
||||
desc: "invalid load balancer method with sticky disabled",
|
||||
lb: &types.LoadBalancer{
|
||||
Method: "Invalid",
|
||||
Sticky: false,
|
||||
Method: "Invalid",
|
||||
Stickiness: nil,
|
||||
},
|
||||
wantMethod: defaultMethod,
|
||||
wantSticky: false,
|
||||
},
|
||||
{
|
||||
desc: "missing load balancer",
|
||||
lb: nil,
|
||||
wantMethod: defaultMethod,
|
||||
wantSticky: false,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -485,8 +482,8 @@ func TestConfigureBackends(t *testing.T) {
|
|||
})
|
||||
|
||||
wantLB := types.LoadBalancer{
|
||||
Method: test.wantMethod,
|
||||
Sticky: test.wantSticky,
|
||||
Method: test.wantMethod,
|
||||
Stickiness: test.wantStickiness,
|
||||
}
|
||||
if !reflect.DeepEqual(*backend.LoadBalancer, wantLB) {
|
||||
t.Errorf("got backend load-balancer\n%v\nwant\n%v\n", spew.Sdump(backend.LoadBalancer), spew.Sdump(wantLB))
|
||||
|
@ -539,7 +536,7 @@ func TestServerEntrypointWhitelistConfig(t *testing.T) {
|
|||
handler := srvEntryPoint.httpServer.Handler.(*negroni.Negroni)
|
||||
found := false
|
||||
for _, handler := range handler.Handlers() {
|
||||
if reflect.TypeOf(handler) == reflect.TypeOf((*middlewares.IPWhitelister)(nil)) {
|
||||
if reflect.TypeOf(handler) == reflect.TypeOf((*middlewares.IPWhiteLister)(nil)) {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
@ -719,6 +716,10 @@ func withServer(name, url string) func(backend *types.Backend) {
|
|||
|
||||
func withLoadBalancer(method string, sticky bool) func(*types.Backend) {
|
||||
return func(be *types.Backend) {
|
||||
be.LoadBalancer = &types.LoadBalancer{Method: method, Sticky: sticky}
|
||||
if sticky {
|
||||
be.LoadBalancer = &types.LoadBalancer{Method: method, Stickiness: &types.Stickiness{CookieName: "test"}}
|
||||
} else {
|
||||
be.LoadBalancer = &types.LoadBalancer{Method: method}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue