diff --git a/CHANGELOG.md b/CHANGELOG.md index 8eea2baff..addc308f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Change Log +## [v1.2.3](https://github.com/containous/traefik/tree/v1.2.3) (2017-04-13) +[Full Changelog](https://github.com/containous/traefik/compare/v1.2.2...v1.2.3) + +**Merged pull requests:** + +- Fix too many redirect [\#1433](https://github.com/containous/traefik/pull/1433) ([emilevauge](https://github.com/emilevauge)) + +## [v1.2.2](https://github.com/containous/traefik/tree/v1.2.2) (2017-04-11) +[Full Changelog](https://github.com/containous/traefik/compare/v1.2.1...v1.2.2) + +**Merged pull requests:** + +- Carry PR 1271 [\#1417](https://github.com/containous/traefik/pull/1417) ([emilevauge](https://github.com/emilevauge)) +- Fix postloadconfig acme & Docker filter empty rule [\#1401](https://github.com/containous/traefik/pull/1401) ([emilevauge](https://github.com/emilevauge)) + ## [v1.2.1](https://github.com/containous/traefik/tree/v1.2.1) (2017-03-27) [Full Changelog](https://github.com/containous/traefik/compare/v1.2.0...v1.2.1) diff --git a/provider/docker/docker.go b/provider/docker/docker.go index c31ff7a04..6ecbc775d 100644 --- a/provider/docker/docker.go +++ b/provider/docker/docker.go @@ -523,6 +523,11 @@ func (p *Provider) containerFilter(container dockerData) bool { return false } + if len(p.getFrontendRule(container)) == 0 { + log.Debugf("Filtering container with empty frontend rule %s", container.Name) + return false + } + return true } @@ -540,8 +545,10 @@ func (p *Provider) getFrontendRule(container dockerData) string { if labels, err := getLabels(container, []string{"com.docker.compose.project", "com.docker.compose.service"}); err == nil { return "Host:" + p.getSubDomain(labels["com.docker.compose.service"]+"."+labels["com.docker.compose.project"]) + "." + p.Domain } - - return "Host:" + p.getSubDomain(container.ServiceName) + "." + p.Domain + if len(p.Domain) > 0 { + return "Host:" + p.getSubDomain(container.ServiceName) + "." + p.Domain + } + return "" } func (p *Provider) getBackend(container dockerData) string { diff --git a/provider/docker/docker_test.go b/provider/docker/docker_test.go index 30746b73b..5f9fa42f0 100644 --- a/provider/docker/docker_test.go +++ b/provider/docker/docker_test.go @@ -8,6 +8,7 @@ import ( "github.com/containous/traefik/types" docker "github.com/docker/engine-api/types" + "github.com/docker/engine-api/types/container" "github.com/docker/go-connections/nat" ) @@ -488,127 +489,300 @@ func TestDockerGetLabels(t *testing.T) { func TestDockerTraefikFilter(t *testing.T) { containers := []struct { - container docker.ContainerJSON - exposedByDefault bool - expected bool + container docker.ContainerJSON + expected bool + provider *Provider }{ { - container: containerJSON(), - exposedByDefault: true, - expected: false, + container: docker.ContainerJSON{ + ContainerJSONBase: &docker.ContainerJSONBase{ + Name: "container", + }, + Config: &container.Config{}, + NetworkSettings: &docker.NetworkSettings{}, + }, + expected: false, + provider: &Provider{ + Domain: "test", + ExposedByDefault: true, + }, }, { - container: containerJSON( - labels(map[string]string{ - "traefik.enable": "false", - }), - ports(nat.PortMap{ - "80/tcp": {}, - }), - ), - exposedByDefault: true, - expected: false, + container: docker.ContainerJSON{ + ContainerJSONBase: &docker.ContainerJSONBase{ + Name: "container", + }, + Config: &container.Config{ + Labels: map[string]string{ + "traefik.enable": "false", + }, + }, + NetworkSettings: &docker.NetworkSettings{ + NetworkSettingsBase: docker.NetworkSettingsBase{ + Ports: nat.PortMap{ + "80/tcp": {}, + }, + }, + }, + }, + provider: &Provider{ + Domain: "test", + ExposedByDefault: true, + }, + expected: false, }, { - container: containerJSON( - labels(map[string]string{ - "traefik.frontend.rule": "Host:foo.bar", - }), - ports(nat.PortMap{ - "80/tcp": {}, - }), - ), - exposedByDefault: true, - expected: true, + container: docker.ContainerJSON{ + ContainerJSONBase: &docker.ContainerJSONBase{ + Name: "container", + }, + Config: &container.Config{ + Labels: map[string]string{ + "traefik.frontend.rule": "Host:foo.bar", + }, + }, + NetworkSettings: &docker.NetworkSettings{ + NetworkSettingsBase: docker.NetworkSettingsBase{ + Ports: nat.PortMap{ + "80/tcp": {}, + }, + }, + }, + }, + provider: &Provider{ + Domain: "test", + ExposedByDefault: true, + }, + expected: true, }, { - container: containerJSON( - ports(nat.PortMap{ - "80/tcp": {}, - "443/tcp": {}, - }), - ), - exposedByDefault: true, - expected: true, + container: docker.ContainerJSON{ + ContainerJSONBase: &docker.ContainerJSONBase{ + Name: "container-multi-ports", + }, + Config: &container.Config{}, + NetworkSettings: &docker.NetworkSettings{ + NetworkSettingsBase: docker.NetworkSettingsBase{ + Ports: nat.PortMap{ + "80/tcp": {}, + "443/tcp": {}, + }, + }, + }, + }, + provider: &Provider{ + Domain: "test", + ExposedByDefault: true, + }, + expected: true, }, { - container: containerJSON( - ports(nat.PortMap{ - "80/tcp": {}, - }), - ), - exposedByDefault: true, - expected: true, + container: docker.ContainerJSON{ + ContainerJSONBase: &docker.ContainerJSONBase{ + Name: "container", + }, + Config: &container.Config{}, + NetworkSettings: &docker.NetworkSettings{ + NetworkSettingsBase: docker.NetworkSettingsBase{ + Ports: nat.PortMap{ + "80/tcp": {}, + }, + }, + }, + }, + provider: &Provider{ + Domain: "test", + ExposedByDefault: true, + }, + expected: true, }, { - container: containerJSON( - labels(map[string]string{ - "traefik.port": "80", - }), - ports(nat.PortMap{ - "80/tcp": {}, - "443/tcp": {}, - }), - ), - exposedByDefault: true, - expected: true, + container: docker.ContainerJSON{ + ContainerJSONBase: &docker.ContainerJSONBase{ + Name: "container", + }, + Config: &container.Config{ + Labels: map[string]string{ + "traefik.port": "80", + }, + }, + NetworkSettings: &docker.NetworkSettings{ + NetworkSettingsBase: docker.NetworkSettingsBase{ + Ports: nat.PortMap{ + "80/tcp": {}, + "443/tcp": {}, + }, + }, + }, + }, + provider: &Provider{ + Domain: "test", + ExposedByDefault: true, + }, + expected: true, }, { - container: containerJSON( - labels(map[string]string{ - "traefik.enable": "true", - }), - ports(nat.PortMap{ - "80/tcp": {}, - }), - ), - exposedByDefault: true, - expected: true, + container: docker.ContainerJSON{ + ContainerJSONBase: &docker.ContainerJSONBase{ + Name: "container", + }, + Config: &container.Config{ + Labels: map[string]string{ + "traefik.enable": "true", + }, + }, + NetworkSettings: &docker.NetworkSettings{ + NetworkSettingsBase: docker.NetworkSettingsBase{ + Ports: nat.PortMap{ + "80/tcp": {}, + }, + }, + }, + }, + provider: &Provider{ + Domain: "test", + ExposedByDefault: true, + }, + expected: true, }, { - container: containerJSON( - labels(map[string]string{ - "traefik.enable": "anything", - }), - ports(nat.PortMap{ - "80/tcp": {}, - }), - ), - exposedByDefault: true, - expected: true, + container: docker.ContainerJSON{ + ContainerJSONBase: &docker.ContainerJSONBase{ + Name: "container", + }, + Config: &container.Config{ + Labels: map[string]string{ + "traefik.enable": "anything", + }, + }, + NetworkSettings: &docker.NetworkSettings{ + NetworkSettingsBase: docker.NetworkSettingsBase{ + Ports: nat.PortMap{ + "80/tcp": {}, + }, + }, + }, + }, + provider: &Provider{ + Domain: "test", + ExposedByDefault: true, + }, + expected: true, }, { - container: containerJSON( - labels(map[string]string{ - "traefik.frontend.rule": "Host:foo.bar", - }), - ports(nat.PortMap{ - "80/tcp": {}, - }), - ), - exposedByDefault: true, - expected: true, + container: docker.ContainerJSON{ + ContainerJSONBase: &docker.ContainerJSONBase{ + Name: "container", + }, + Config: &container.Config{ + Labels: map[string]string{ + "traefik.frontend.rule": "Host:foo.bar", + }, + }, + NetworkSettings: &docker.NetworkSettings{ + NetworkSettingsBase: docker.NetworkSettingsBase{ + Ports: nat.PortMap{ + "80/tcp": {}, + }, + }, + }, + }, + provider: &Provider{ + Domain: "test", + ExposedByDefault: true, + }, + expected: true, }, { - container: containerJSON( - ports(nat.PortMap{ - "80/tcp": {}, - }), - ), - exposedByDefault: false, - expected: false, + container: docker.ContainerJSON{ + ContainerJSONBase: &docker.ContainerJSONBase{ + Name: "container", + }, + Config: &container.Config{}, + NetworkSettings: &docker.NetworkSettings{ + NetworkSettingsBase: docker.NetworkSettingsBase{ + Ports: nat.PortMap{ + "80/tcp": {}, + }, + }, + }, + }, + provider: &Provider{ + Domain: "test", + ExposedByDefault: false, + }, + expected: false, }, { - container: containerJSON( - labels(map[string]string{ - "traefik.enable": "true", - }), - ports(nat.PortMap{ - "80/tcp": {}, - }), - ), - exposedByDefault: false, - expected: true, + container: docker.ContainerJSON{ + ContainerJSONBase: &docker.ContainerJSONBase{ + Name: "container", + }, + Config: &container.Config{ + Labels: map[string]string{ + "traefik.enable": "true", + }, + }, + NetworkSettings: &docker.NetworkSettings{ + NetworkSettingsBase: docker.NetworkSettingsBase{ + Ports: nat.PortMap{ + "80/tcp": {}, + }, + }, + }, + }, + provider: &Provider{ + Domain: "test", + ExposedByDefault: false, + }, + expected: true, + }, + { + container: docker.ContainerJSON{ + ContainerJSONBase: &docker.ContainerJSONBase{ + Name: "container", + }, + Config: &container.Config{ + Labels: map[string]string{ + "traefik.enable": "true", + }, + }, + NetworkSettings: &docker.NetworkSettings{ + NetworkSettingsBase: docker.NetworkSettingsBase{ + Ports: nat.PortMap{ + "80/tcp": {}, + }, + }, + }, + }, + provider: &Provider{ + ExposedByDefault: false, + }, + expected: false, + }, + { + container: docker.ContainerJSON{ + ContainerJSONBase: &docker.ContainerJSONBase{ + Name: "container", + }, + Config: &container.Config{ + Labels: map[string]string{ + "traefik.enable": "true", + "traefik.frontend.rule": "Host:i.love.this.host", + }, + }, + NetworkSettings: &docker.NetworkSettings{ + NetworkSettingsBase: docker.NetworkSettingsBase{ + Ports: nat.PortMap{ + "80/tcp": {}, + }, + }, + }, + }, + provider: &Provider{ + ExposedByDefault: false, + }, + expected: true, }, } @@ -616,12 +790,10 @@ func TestDockerTraefikFilter(t *testing.T) { e := e t.Run(strconv.Itoa(containerID), func(t *testing.T) { t.Parallel() - provider := Provider{} - provider.ExposedByDefault = e.exposedByDefault dockerData := parseContainer(e.container) - actual := provider.containerFilter(dockerData) + actual := e.provider.containerFilter(dockerData) if actual != e.expected { - t.Errorf("expected %v for %+v (%+v, %+v), got %+v", e.expected, e.container, e.container.NetworkSettings, e.container.ContainerJSONBase, actual) + t.Errorf("expected %v for %+v, got %+v", e.expected, e, actual) } }) } diff --git a/provider/docker/service_test.go b/provider/docker/service_test.go index 46749c553..a5f03b8fb 100644 --- a/provider/docker/service_test.go +++ b/provider/docker/service_test.go @@ -133,7 +133,7 @@ func TestDockerGetServiceFrontendRule(t *testing.T) { }{ { container: containerJSON(name("foo")), - expected: "Host:foo.", + expected: "", }, { container: containerJSON(labels(map[string]string{ diff --git a/provider/docker/swarm_test.go b/provider/docker/swarm_test.go index 56cfb8acf..b757118ba 100644 --- a/provider/docker/swarm_test.go +++ b/provider/docker/swarm_test.go @@ -503,86 +503,122 @@ func TestSwarmGetLabels(t *testing.T) { func TestSwarmTraefikFilter(t *testing.T) { services := []struct { - service swarm.Service - exposedByDefault bool - expected bool - networks map[string]*docker.NetworkResource + service swarm.Service + expected bool + networks map[string]*docker.NetworkResource + provider *Provider }{ { - service: swarmService(), - exposedByDefault: true, - expected: false, - networks: map[string]*docker.NetworkResource{}, + service: swarmService(), + expected: false, + networks: map[string]*docker.NetworkResource{}, + provider: &Provider{ + SwarmMode: true, + Domain: "test", + ExposedByDefault: true, + }, }, { service: swarmService(serviceLabels(map[string]string{ "traefik.enable": "false", "traefik.port": "80", })), - exposedByDefault: true, - expected: false, - networks: map[string]*docker.NetworkResource{}, + expected: false, + networks: map[string]*docker.NetworkResource{}, + provider: &Provider{ + SwarmMode: true, + Domain: "test", + ExposedByDefault: true, + }, }, { service: swarmService(serviceLabels(map[string]string{ "traefik.frontend.rule": "Host:foo.bar", "traefik.port": "80", })), - exposedByDefault: true, - expected: true, - networks: map[string]*docker.NetworkResource{}, + expected: true, + networks: map[string]*docker.NetworkResource{}, + provider: &Provider{ + SwarmMode: true, + Domain: "test", + ExposedByDefault: true, + }, }, { service: swarmService(serviceLabels(map[string]string{ "traefik.port": "80", })), - exposedByDefault: true, - expected: true, - networks: map[string]*docker.NetworkResource{}, + expected: true, + networks: map[string]*docker.NetworkResource{}, + provider: &Provider{ + SwarmMode: true, + Domain: "test", + ExposedByDefault: true, + }, }, { service: swarmService(serviceLabels(map[string]string{ "traefik.enable": "true", "traefik.port": "80", })), - exposedByDefault: true, - expected: true, - networks: map[string]*docker.NetworkResource{}, + expected: true, + networks: map[string]*docker.NetworkResource{}, + provider: &Provider{ + SwarmMode: true, + Domain: "test", + ExposedByDefault: true, + }, }, { service: swarmService(serviceLabels(map[string]string{ "traefik.enable": "anything", "traefik.port": "80", })), - exposedByDefault: true, - expected: true, - networks: map[string]*docker.NetworkResource{}, + expected: true, + networks: map[string]*docker.NetworkResource{}, + provider: &Provider{ + SwarmMode: true, + Domain: "test", + ExposedByDefault: true, + }, }, { service: swarmService(serviceLabels(map[string]string{ "traefik.frontend.rule": "Host:foo.bar", "traefik.port": "80", })), - exposedByDefault: true, - expected: true, - networks: map[string]*docker.NetworkResource{}, + expected: true, + networks: map[string]*docker.NetworkResource{}, + provider: &Provider{ + SwarmMode: true, + Domain: "test", + ExposedByDefault: true, + }, }, { service: swarmService(serviceLabels(map[string]string{ "traefik.port": "80", })), - exposedByDefault: false, - expected: false, - networks: map[string]*docker.NetworkResource{}, + expected: false, + networks: map[string]*docker.NetworkResource{}, + provider: &Provider{ + SwarmMode: true, + Domain: "test", + ExposedByDefault: false, + }, }, { service: swarmService(serviceLabels(map[string]string{ "traefik.enable": "true", "traefik.port": "80", })), - exposedByDefault: false, - expected: true, - networks: map[string]*docker.NetworkResource{}, + expected: true, + networks: map[string]*docker.NetworkResource{}, + provider: &Provider{ + SwarmMode: true, + Domain: "test", + ExposedByDefault: false, + }, }, } @@ -591,11 +627,7 @@ func TestSwarmTraefikFilter(t *testing.T) { t.Run(strconv.Itoa(serviceID), func(t *testing.T) { t.Parallel() dockerData := parseService(e.service, e.networks) - provider := &Provider{ - SwarmMode: true, - } - provider.ExposedByDefault = e.exposedByDefault - actual := provider.containerFilter(dockerData) + actual := e.provider.containerFilter(dockerData) if actual != e.expected { t.Errorf("expected %v for %+v, got %+v", e.expected, e, actual) } diff --git a/server/server.go b/server/server.go index b8f8b502d..da99e366d 100644 --- a/server/server.go +++ b/server/server.go @@ -315,15 +315,16 @@ func (server *Server) postLoadConfig() { for _, frontend := range configuration.Frontends { // check if one of the frontend entrypoints is configured with TLS - TLSEnabled := false + // and is configured with ACME + ACMEEnabled := false for _, entrypoint := range frontend.EntryPoints { - if server.globalConfiguration.EntryPoints[entrypoint].TLS != nil { - TLSEnabled = true + if server.globalConfiguration.ACME.EntryPoint == entrypoint && server.globalConfiguration.EntryPoints[entrypoint].TLS != nil { + ACMEEnabled = true break } } - if TLSEnabled { + if ACMEEnabled { for _, route := range frontend.Routes { rules := Rules{} domains, err := rules.ParseDomains(route.Rule) @@ -547,13 +548,11 @@ func (server *Server) buildEntryPoints(globalConfiguration GlobalConfiguration) // provider configurations. func (server *Server) loadConfig(configurations configs, globalConfiguration GlobalConfiguration) (map[string]*serverEntryPoint, error) { serverEntryPoints := server.buildEntryPoints(globalConfiguration) - redirectHandlers := make(map[string]http.Handler) - + redirectHandlers := make(map[string]negroni.Handler) backends := map[string]http.Handler{} - backendsHealthcheck := map[string]*healthcheck.BackendHealthCheck{} - backend2FrontendMap := map[string]string{} + for _, configuration := range configurations { frontendNames := sortedFrontendNamesForConfig(configuration) frontend: @@ -595,172 +594,170 @@ func (server *Server) loadConfig(configurations configs, globalConfiguration Glo } entryPoint := globalConfiguration.EntryPoints[entryPointName] + negroni := negroni.New() if entryPoint.Redirect != nil { if redirectHandlers[entryPointName] != nil { - newServerRoute.route.Handler(redirectHandlers[entryPointName]) + negroni.Use(redirectHandlers[entryPointName]) } else if handler, err := server.loadEntryPointConfig(entryPointName, entryPoint); err != nil { log.Errorf("Error loading entrypoint configuration for frontend %s: %v", frontendName, err) log.Errorf("Skipping frontend %s...", frontendName) continue frontend } else { - newServerRoute.route.Handler(handler) + negroni.Use(handler) redirectHandlers[entryPointName] = handler } - } else { - if backends[frontend.Backend] == nil { - log.Debugf("Creating backend %s", frontend.Backend) - var lb http.Handler - rr, _ := roundrobin.New(saveBackend) - if configuration.Backends[frontend.Backend] == nil { - log.Errorf("Undefined backend '%s' for frontend %s", frontend.Backend, frontendName) - log.Errorf("Skipping frontend %s...", frontendName) - continue frontend - } + } + if backends[entryPointName+frontend.Backend] == nil { + log.Debugf("Creating backend %s", frontend.Backend) + var lb http.Handler + rr, _ := roundrobin.New(saveBackend) + if configuration.Backends[frontend.Backend] == nil { + log.Errorf("Undefined backend '%s' for frontend %s", frontend.Backend, frontendName) + log.Errorf("Skipping frontend %s...", frontendName) + continue frontend + } - lbMethod, err := types.NewLoadBalancerMethod(configuration.Backends[frontend.Backend].LoadBalancer) - if err != nil { - log.Errorf("Error loading load balancer method '%+v' for frontend %s: %v", configuration.Backends[frontend.Backend].LoadBalancer, frontendName, err) - log.Errorf("Skipping frontend %s...", frontendName) - continue frontend - } + lbMethod, err := types.NewLoadBalancerMethod(configuration.Backends[frontend.Backend].LoadBalancer) + if err != nil { + log.Errorf("Error loading load balancer method '%+v' for frontend %s: %v", configuration.Backends[frontend.Backend].LoadBalancer, frontendName, err) + log.Errorf("Skipping frontend %s...", frontendName) + continue frontend + } - stickysession := configuration.Backends[frontend.Backend].LoadBalancer.Sticky - cookiename := "_TRAEFIK_BACKEND" - var sticky *roundrobin.StickySession + stickysession := configuration.Backends[frontend.Backend].LoadBalancer.Sticky + cookiename := "_TRAEFIK_BACKEND" + var sticky *roundrobin.StickySession + if stickysession { + sticky = roundrobin.NewStickySession(cookiename) + } + + switch lbMethod { + case types.Drr: + log.Debugf("Creating load-balancer drr") + rebalancer, _ := roundrobin.NewRebalancer(rr, roundrobin.RebalancerLogger(oxyLogger)) if stickysession { - sticky = roundrobin.NewStickySession(cookiename) + log.Debugf("Sticky session with cookie %v", cookiename) + rebalancer, _ = roundrobin.NewRebalancer(rr, roundrobin.RebalancerLogger(oxyLogger), roundrobin.RebalancerStickySession(sticky)) } - - switch lbMethod { - case types.Drr: - log.Debugf("Creating load-balancer drr") - rebalancer, _ := roundrobin.NewRebalancer(rr, roundrobin.RebalancerLogger(oxyLogger)) - if stickysession { - log.Debugf("Sticky session with cookie %v", cookiename) - rebalancer, _ = roundrobin.NewRebalancer(rr, roundrobin.RebalancerLogger(oxyLogger), roundrobin.RebalancerStickySession(sticky)) + lb = rebalancer + for serverName, server := range configuration.Backends[frontend.Backend].Servers { + url, err := url.Parse(server.URL) + if err != nil { + log.Errorf("Error parsing server URL %s: %v", server.URL, err) + log.Errorf("Skipping frontend %s...", frontendName) + continue frontend } - lb = rebalancer - for serverName, server := range configuration.Backends[frontend.Backend].Servers { - url, err := url.Parse(server.URL) - if err != nil { - log.Errorf("Error parsing server URL %s: %v", server.URL, err) - log.Errorf("Skipping frontend %s...", frontendName) - continue frontend - } - backend2FrontendMap[url.String()] = frontendName - log.Debugf("Creating server %s at %s with weight %d", serverName, url.String(), server.Weight) - if err := rebalancer.UpsertServer(url, roundrobin.Weight(server.Weight)); err != nil { - log.Errorf("Error adding server %s to load balancer: %v", server.URL, err) - log.Errorf("Skipping frontend %s...", frontendName) - continue frontend - } - hcOpts := parseHealthCheckOptions(rebalancer, frontend.Backend, configuration.Backends[frontend.Backend].HealthCheck, *globalConfiguration.HealthCheck) - if hcOpts != nil { - log.Debugf("Setting up backend health check %s", *hcOpts) - backendsHealthcheck[frontend.Backend] = healthcheck.NewBackendHealthCheck(*hcOpts) - } + backend2FrontendMap[url.String()] = frontendName + log.Debugf("Creating server %s at %s with weight %d", serverName, url.String(), server.Weight) + if err := rebalancer.UpsertServer(url, roundrobin.Weight(server.Weight)); err != nil { + log.Errorf("Error adding server %s to load balancer: %v", server.URL, err) + log.Errorf("Skipping frontend %s...", frontendName) + continue frontend } - case types.Wrr: - log.Debugf("Creating load-balancer wrr") - if stickysession { - log.Debugf("Sticky session with cookie %v", cookiename) - rr, _ = roundrobin.New(saveBackend, roundrobin.EnableStickySession(sticky)) - } - lb = rr - for serverName, server := range configuration.Backends[frontend.Backend].Servers { - url, err := url.Parse(server.URL) - if err != nil { - log.Errorf("Error parsing server URL %s: %v", server.URL, err) - log.Errorf("Skipping frontend %s...", frontendName) - continue frontend - } - backend2FrontendMap[url.String()] = frontendName - log.Debugf("Creating server %s at %s with weight %d", serverName, url.String(), server.Weight) - if err := rr.UpsertServer(url, roundrobin.Weight(server.Weight)); err != nil { - log.Errorf("Error adding server %s to load balancer: %v", server.URL, err) - log.Errorf("Skipping frontend %s...", frontendName) - continue frontend - } - } - hcOpts := parseHealthCheckOptions(rr, frontend.Backend, configuration.Backends[frontend.Backend].HealthCheck, *globalConfiguration.HealthCheck) + hcOpts := parseHealthCheckOptions(rebalancer, frontend.Backend, configuration.Backends[frontend.Backend].HealthCheck, *globalConfiguration.HealthCheck) if hcOpts != nil { log.Debugf("Setting up backend health check %s", *hcOpts) backendsHealthcheck[frontend.Backend] = healthcheck.NewBackendHealthCheck(*hcOpts) } } - maxConns := configuration.Backends[frontend.Backend].MaxConn - if maxConns != nil && maxConns.Amount != 0 { - extractFunc, err := utils.NewExtractor(maxConns.ExtractorFunc) + case types.Wrr: + log.Debugf("Creating load-balancer wrr") + if stickysession { + log.Debugf("Sticky session with cookie %v", cookiename) + rr, _ = roundrobin.New(saveBackend, roundrobin.EnableStickySession(sticky)) + } + lb = rr + for serverName, server := range configuration.Backends[frontend.Backend].Servers { + url, err := url.Parse(server.URL) if err != nil { - log.Errorf("Error creating connlimit: %v", err) + log.Errorf("Error parsing server URL %s: %v", server.URL, err) log.Errorf("Skipping frontend %s...", frontendName) continue frontend } - log.Debugf("Creating load-balancer connlimit") - lb, err = connlimit.New(lb, extractFunc, maxConns.Amount, connlimit.Logger(oxyLogger)) - if err != nil { - log.Errorf("Error creating connlimit: %v", err) + backend2FrontendMap[url.String()] = frontendName + log.Debugf("Creating server %s at %s with weight %d", serverName, url.String(), server.Weight) + if err := rr.UpsertServer(url, roundrobin.Weight(server.Weight)); err != nil { + log.Errorf("Error adding server %s to load balancer: %v", server.URL, err) log.Errorf("Skipping frontend %s...", frontendName) continue frontend } } - // retry ? - if globalConfiguration.Retry != nil { - retries := len(configuration.Backends[frontend.Backend].Servers) - if globalConfiguration.Retry.Attempts > 0 { - retries = globalConfiguration.Retry.Attempts - } - lb = middlewares.NewRetry(retries, lb) - log.Debugf("Creating retries max attempts %d", retries) + hcOpts := parseHealthCheckOptions(rr, frontend.Backend, configuration.Backends[frontend.Backend].HealthCheck, *globalConfiguration.HealthCheck) + if hcOpts != nil { + log.Debugf("Setting up backend health check %s", *hcOpts) + backendsHealthcheck[frontend.Backend] = healthcheck.NewBackendHealthCheck(*hcOpts) + } + } + maxConns := configuration.Backends[frontend.Backend].MaxConn + if maxConns != nil && maxConns.Amount != 0 { + extractFunc, err := utils.NewExtractor(maxConns.ExtractorFunc) + if err != nil { + log.Errorf("Error creating connlimit: %v", err) + log.Errorf("Skipping frontend %s...", frontendName) + continue frontend + } + log.Debugf("Creating load-balancer connlimit") + lb, err = connlimit.New(lb, extractFunc, maxConns.Amount, connlimit.Logger(oxyLogger)) + if err != nil { + log.Errorf("Error creating connlimit: %v", err) + log.Errorf("Skipping frontend %s...", frontendName) + continue frontend + } + } + // retry ? + if globalConfiguration.Retry != nil { + retries := len(configuration.Backends[frontend.Backend].Servers) + if globalConfiguration.Retry.Attempts > 0 { + retries = globalConfiguration.Retry.Attempts + } + lb = middlewares.NewRetry(retries, lb) + log.Debugf("Creating retries max attempts %d", retries) + } + + if server.globalConfiguration.Web != nil && server.globalConfiguration.Web.Metrics != nil { + if server.globalConfiguration.Web.Metrics.Prometheus != nil { + metricsMiddlewareBackend := middlewares.NewMetricsWrapper(middlewares.NewPrometheus(frontend.Backend, server.globalConfiguration.Web.Metrics.Prometheus)) + negroni.Use(metricsMiddlewareBackend) + } + } + if len(frontend.BasicAuth) > 0 { + users := types.Users{} + for _, user := range frontend.BasicAuth { + users = append(users, user) } - var negroni = negroni.New() - if server.globalConfiguration.Web != nil && server.globalConfiguration.Web.Metrics != nil { - if server.globalConfiguration.Web.Metrics.Prometheus != nil { - metricsMiddlewareBackend := middlewares.NewMetricsWrapper(middlewares.NewPrometheus(frontend.Backend, server.globalConfiguration.Web.Metrics.Prometheus)) - negroni.Use(metricsMiddlewareBackend) - } + auth := &types.Auth{} + auth.Basic = &types.Basic{ + Users: users, } - - if len(frontend.BasicAuth) > 0 { - users := types.Users{} - for _, user := range frontend.BasicAuth { - users = append(users, user) - } - - auth := &types.Auth{} - auth.Basic = &types.Basic{ - Users: users, - } - authMiddleware, err := middlewares.NewAuthenticator(auth) - if err != nil { - log.Fatal("Error creating Auth: ", err) - } - negroni.Use(authMiddleware) + authMiddleware, err := middlewares.NewAuthenticator(auth) + if err != nil { + log.Fatal("Error creating Auth: ", err) } - - if configuration.Backends[frontend.Backend].CircuitBreaker != nil { - log.Debugf("Creating circuit breaker %s", configuration.Backends[frontend.Backend].CircuitBreaker.Expression) - cbreaker, err := middlewares.NewCircuitBreaker(lb, configuration.Backends[frontend.Backend].CircuitBreaker.Expression, cbreaker.Logger(oxyLogger)) - if err != nil { - log.Errorf("Error creating circuit breaker: %v", err) - log.Errorf("Skipping frontend %s...", frontendName) - continue frontend - } - negroni.Use(cbreaker) - } else { - negroni.UseHandler(lb) + negroni.Use(authMiddleware) + } + if configuration.Backends[frontend.Backend].CircuitBreaker != nil { + log.Debugf("Creating circuit breaker %s", configuration.Backends[frontend.Backend].CircuitBreaker.Expression) + cbreaker, err := middlewares.NewCircuitBreaker(lb, configuration.Backends[frontend.Backend].CircuitBreaker.Expression, cbreaker.Logger(oxyLogger)) + if err != nil { + log.Errorf("Error creating circuit breaker: %v", err) + log.Errorf("Skipping frontend %s...", frontendName) + continue frontend } - backends[frontend.Backend] = negroni + negroni.Use(cbreaker) } else { - log.Debugf("Reusing backend %s", frontend.Backend) + negroni.UseHandler(lb) } - if frontend.Priority > 0 { - newServerRoute.route.Priority(frontend.Priority) - } - server.wireFrontendBackend(newServerRoute, backends[frontend.Backend]) + backends[entryPointName+frontend.Backend] = negroni + } else { + log.Debugf("Reusing backend %s", frontend.Backend) } + if frontend.Priority > 0 { + newServerRoute.route.Priority(frontend.Priority) + } + server.wireFrontendBackend(newServerRoute, backends[entryPointName+frontend.Backend]) + err := newServerRoute.route.GetError() if err != nil { log.Errorf("Error building route: %s", err) @@ -810,7 +807,7 @@ func (server *Server) wireFrontendBackend(serverRoute *serverRoute, handler http serverRoute.route.Handler(handler) } -func (server *Server) loadEntryPointConfig(entryPointName string, entryPoint *EntryPoint) (http.Handler, error) { +func (server *Server) loadEntryPointConfig(entryPointName string, entryPoint *EntryPoint) (negroni.Handler, error) { regex := entryPoint.Redirect.Regex replacement := entryPoint.Redirect.Replacement if len(entryPoint.Redirect.EntryPoint) > 0 { @@ -834,9 +831,8 @@ func (server *Server) loadEntryPointConfig(entryPointName string, entryPoint *En return nil, err } log.Debugf("Creating entryPoint redirect %s -> %s : %s -> %s", entryPointName, entryPoint.Redirect.EntryPoint, regex, replacement) - negroni := negroni.New() - negroni.Use(rewrite) - return negroni, nil + + return rewrite, nil } func (server *Server) buildDefaultHTTPRouter() *mux.Router {