Fix backend reuse

This commit is contained in:
Arne Jørgensen 2018-06-06 17:56:03 +02:00 committed by Traefiker Bot
parent 9cf4e730e7
commit 51227241b7
4 changed files with 94 additions and 8 deletions

View file

@ -950,7 +950,15 @@ func (s *Server) loadConfig(configurations types.Configurations, globalConfigura
redirectHandlers[entryPointName] = handlerToUse
}
}
if backends[entryPointName+providerName+frontend.Backend] == nil {
frontendHash, err := frontend.Hash()
if err != nil {
log.Errorf("Error calculating hash value for frontend %s: %v", frontendName, err)
log.Errorf("Skipping frontend %s...", frontendName)
continue frontend
}
backendCacheKey := entryPointName + providerName + frontendHash
if backends[backendCacheKey] == nil {
log.Debugf("Creating backend %s", frontend.Backend)
roundTripper, err := s.getRoundTripper(entryPointName, globalConfiguration, frontend.PassTLSCert, entryPoint.TLS)
@ -1045,7 +1053,7 @@ func (s *Server) loadConfig(configurations types.Configurations, globalConfigura
if hcOpts != nil {
log.Debugf("Setting up backend health check %s", *hcOpts)
hcOpts.Transport = s.defaultForwardingRoundTripper
backendsHealthCheck[entryPointName+frontend.Backend] = healthcheck.NewBackendHealthCheck(*hcOpts, frontend.Backend)
backendsHealthCheck[backendCacheKey] = healthcheck.NewBackendHealthCheck(*hcOpts, frontend.Backend)
}
lb = middlewares.NewEmptyBackendHandler(rebalancer, lb)
case types.Wrr:
@ -1067,7 +1075,7 @@ func (s *Server) loadConfig(configurations types.Configurations, globalConfigura
if hcOpts != nil {
log.Debugf("Setting up backend health check %s", *hcOpts)
hcOpts.Transport = s.defaultForwardingRoundTripper
backendsHealthCheck[entryPointName+frontend.Backend] = healthcheck.NewBackendHealthCheck(*hcOpts, frontend.Backend)
backendsHealthCheck[backendCacheKey] = healthcheck.NewBackendHealthCheck(*hcOpts, frontend.Backend)
}
lb = middlewares.NewEmptyBackendHandler(rr, lb)
}
@ -1208,16 +1216,16 @@ func (s *Server) loadConfig(configurations types.Configurations, globalConfigura
} else {
n.UseHandler(lb)
}
backends[entryPointName+providerName+frontend.Backend] = n
backends[backendCacheKey] = n
} else {
log.Debugf("Reusing backend %s", frontend.Backend)
}
if frontend.Priority > 0 {
newServerRoute.Route.Priority(frontend.Priority)
}
s.wireFrontendBackend(newServerRoute, backends[entryPointName+providerName+frontend.Backend])
s.wireFrontendBackend(newServerRoute, backends[backendCacheKey])
err := newServerRoute.Route.GetError()
err = newServerRoute.Route.GetError()
if err != nil {
log.Errorf("Error building route: %s", err)
}

View file

@ -962,6 +962,65 @@ func TestServerResponseEmptyBackend(t *testing.T) {
}
}
func TestReuseBackend(t *testing.T) {
testServer := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
rw.WriteHeader(http.StatusOK)
}))
defer testServer.Close()
globalConfig := configuration.GlobalConfiguration{
DefaultEntryPoints: []string{"http"},
}
entryPoints := map[string]EntryPoint{
"http": {Configuration: &configuration.EntryPoint{
ForwardedHeaders: &configuration.ForwardedHeaders{Insecure: true},
}},
}
dynamicConfigs := types.Configurations{
"config": th.BuildConfiguration(
th.WithFrontends(
th.WithFrontend("backend",
th.WithFrontendName("frontend0"),
th.WithEntryPoints("http"),
th.WithRoutes(th.WithRoute("/ok", "Path: /ok"))),
th.WithFrontend("backend",
th.WithFrontendName("frontend1"),
th.WithEntryPoints("http"),
th.WithRoutes(th.WithRoute("/unauthorized", "Path: /unauthorized")),
th.WithBasicAuth("foo", "bar")),
),
th.WithBackends(th.WithBackendNew("backend",
th.WithLBMethod("wrr"),
th.WithServersNew(th.WithServerNew(testServer.URL))),
),
),
}
srv := NewServer(globalConfig, nil, entryPoints)
serverEntryPoints, err := srv.loadConfig(dynamicConfigs, globalConfig)
if err != nil {
t.Fatalf("error loading config: %s", err)
}
// Test that the /ok path returns a status 200.
responseRecorderOk := &httptest.ResponseRecorder{}
requestOk := httptest.NewRequest(http.MethodGet, testServer.URL+"/ok", nil)
serverEntryPoints["http"].httpRouter.ServeHTTP(responseRecorderOk, requestOk)
assert.Equal(t, http.StatusOK, responseRecorderOk.Result().StatusCode, "status code")
// Test that the /unauthorized path returns a 401 because of
// the basic authentication defined on the frontend.
responseRecorderUnauthorized := &httptest.ResponseRecorder{}
requestUnauthorized := httptest.NewRequest(http.MethodGet, testServer.URL+"/unauthorized", nil)
serverEntryPoints["http"].httpRouter.ServeHTTP(responseRecorderUnauthorized, requestUnauthorized)
assert.Equal(t, http.StatusUnauthorized, responseRecorderUnauthorized.Result().StatusCode, "status code")
}
func TestBuildRedirectHandler(t *testing.T) {
srv := Server{
globalConfiguration: configuration.GlobalConfiguration{},

View file

@ -137,6 +137,13 @@ func WithRoute(name string, rule string) func(*types.Route) string {
}
}
// WithBasicAuth is a helper to create a configuration
func WithBasicAuth(username string, password string) func(*types.Frontend) {
return func(fe *types.Frontend) {
fe.BasicAuth = []string{username + ":" + password}
}
}
// WithLBSticky is a helper to create a configuration
func WithLBSticky(cookieName string) func(*types.Backend) {
return func(b *types.Backend) {

View file

@ -16,6 +16,7 @@ import (
"github.com/containous/mux"
"github.com/containous/traefik/log"
traefiktls "github.com/containous/traefik/tls"
"github.com/mitchellh/hashstructure"
"github.com/ryanuber/go-glob"
)
@ -177,9 +178,9 @@ func (h *Headers) HasSecureHeadersDefined() bool {
// Frontend holds frontend configuration.
type Frontend struct {
EntryPoints []string `json:"entryPoints,omitempty"`
EntryPoints []string `json:"entryPoints,omitempty" hash:"ignore"`
Backend string `json:"backend,omitempty"`
Routes map[string]Route `json:"routes,omitempty"`
Routes map[string]Route `json:"routes,omitempty" hash:"ignore"`
PassHostHeader bool `json:"passHostHeader,omitempty"`
PassTLSCert bool `json:"passTLSCert,omitempty"`
Priority int `json:"priority"`
@ -192,6 +193,17 @@ type Frontend struct {
Redirect *Redirect `json:"redirect,omitempty"`
}
// Hash returns the hash value of a Frontend struct.
func (f *Frontend) Hash() (string, error) {
hash, err := hashstructure.Hash(f, nil)
if err != nil {
return "", err
}
return strconv.FormatUint(hash, 10), nil
}
// Redirect configures a redirection of an entry point to another, or to an URL
type Redirect struct {
EntryPoint string `json:"entryPoint,omitempty"`