Merge pull request #1538 from containous/merge-v1.2.3-master

Merge v1.2.3 master
This commit is contained in:
Ludovic Fernandez 2017-05-03 23:49:47 +02:00 committed by GitHub
commit 5535318cda
6 changed files with 506 additions and 284 deletions

View file

@ -1,5 +1,20 @@
# Change Log # 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) ## [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) [Full Changelog](https://github.com/containous/traefik/compare/v1.2.0...v1.2.1)

View file

@ -523,6 +523,11 @@ func (p *Provider) containerFilter(container dockerData) bool {
return false return false
} }
if len(p.getFrontendRule(container)) == 0 {
log.Debugf("Filtering container with empty frontend rule %s", container.Name)
return false
}
return true 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 { 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(labels["com.docker.compose.service"]+"."+labels["com.docker.compose.project"]) + "." + p.Domain
} }
if len(p.Domain) > 0 {
return "Host:" + p.getSubDomain(container.ServiceName) + "." + p.Domain return "Host:" + p.getSubDomain(container.ServiceName) + "." + p.Domain
}
return ""
} }
func (p *Provider) getBackend(container dockerData) string { func (p *Provider) getBackend(container dockerData) string {

View file

@ -8,6 +8,7 @@ import (
"github.com/containous/traefik/types" "github.com/containous/traefik/types"
docker "github.com/docker/engine-api/types" docker "github.com/docker/engine-api/types"
"github.com/docker/engine-api/types/container"
"github.com/docker/go-connections/nat" "github.com/docker/go-connections/nat"
) )
@ -488,127 +489,300 @@ func TestDockerGetLabels(t *testing.T) {
func TestDockerTraefikFilter(t *testing.T) { func TestDockerTraefikFilter(t *testing.T) {
containers := []struct { containers := []struct {
container docker.ContainerJSON container docker.ContainerJSON
exposedByDefault bool expected bool
expected bool provider *Provider
}{ }{
{ {
container: containerJSON(), container: docker.ContainerJSON{
exposedByDefault: true, ContainerJSONBase: &docker.ContainerJSONBase{
expected: false, Name: "container",
},
Config: &container.Config{},
NetworkSettings: &docker.NetworkSettings{},
},
expected: false,
provider: &Provider{
Domain: "test",
ExposedByDefault: true,
},
}, },
{ {
container: containerJSON( container: docker.ContainerJSON{
labels(map[string]string{ ContainerJSONBase: &docker.ContainerJSONBase{
"traefik.enable": "false", Name: "container",
}), },
ports(nat.PortMap{ Config: &container.Config{
"80/tcp": {}, Labels: map[string]string{
}), "traefik.enable": "false",
), },
exposedByDefault: true, },
expected: false, NetworkSettings: &docker.NetworkSettings{
NetworkSettingsBase: docker.NetworkSettingsBase{
Ports: nat.PortMap{
"80/tcp": {},
},
},
},
},
provider: &Provider{
Domain: "test",
ExposedByDefault: true,
},
expected: false,
}, },
{ {
container: containerJSON( container: docker.ContainerJSON{
labels(map[string]string{ ContainerJSONBase: &docker.ContainerJSONBase{
"traefik.frontend.rule": "Host:foo.bar", Name: "container",
}), },
ports(nat.PortMap{ Config: &container.Config{
"80/tcp": {}, Labels: map[string]string{
}), "traefik.frontend.rule": "Host:foo.bar",
), },
exposedByDefault: true, },
expected: true, NetworkSettings: &docker.NetworkSettings{
NetworkSettingsBase: docker.NetworkSettingsBase{
Ports: nat.PortMap{
"80/tcp": {},
},
},
},
},
provider: &Provider{
Domain: "test",
ExposedByDefault: true,
},
expected: true,
}, },
{ {
container: containerJSON( container: docker.ContainerJSON{
ports(nat.PortMap{ ContainerJSONBase: &docker.ContainerJSONBase{
"80/tcp": {}, Name: "container-multi-ports",
"443/tcp": {}, },
}), Config: &container.Config{},
), NetworkSettings: &docker.NetworkSettings{
exposedByDefault: true, NetworkSettingsBase: docker.NetworkSettingsBase{
expected: true, Ports: nat.PortMap{
"80/tcp": {},
"443/tcp": {},
},
},
},
},
provider: &Provider{
Domain: "test",
ExposedByDefault: true,
},
expected: true,
}, },
{ {
container: containerJSON( container: docker.ContainerJSON{
ports(nat.PortMap{ ContainerJSONBase: &docker.ContainerJSONBase{
"80/tcp": {}, Name: "container",
}), },
), Config: &container.Config{},
exposedByDefault: true, NetworkSettings: &docker.NetworkSettings{
expected: true, NetworkSettingsBase: docker.NetworkSettingsBase{
Ports: nat.PortMap{
"80/tcp": {},
},
},
},
},
provider: &Provider{
Domain: "test",
ExposedByDefault: true,
},
expected: true,
}, },
{ {
container: containerJSON( container: docker.ContainerJSON{
labels(map[string]string{ ContainerJSONBase: &docker.ContainerJSONBase{
"traefik.port": "80", Name: "container",
}), },
ports(nat.PortMap{ Config: &container.Config{
"80/tcp": {}, Labels: map[string]string{
"443/tcp": {}, "traefik.port": "80",
}), },
), },
exposedByDefault: true, NetworkSettings: &docker.NetworkSettings{
expected: true, NetworkSettingsBase: docker.NetworkSettingsBase{
Ports: nat.PortMap{
"80/tcp": {},
"443/tcp": {},
},
},
},
},
provider: &Provider{
Domain: "test",
ExposedByDefault: true,
},
expected: true,
}, },
{ {
container: containerJSON( container: docker.ContainerJSON{
labels(map[string]string{ ContainerJSONBase: &docker.ContainerJSONBase{
"traefik.enable": "true", Name: "container",
}), },
ports(nat.PortMap{ Config: &container.Config{
"80/tcp": {}, Labels: map[string]string{
}), "traefik.enable": "true",
), },
exposedByDefault: true, },
expected: true, NetworkSettings: &docker.NetworkSettings{
NetworkSettingsBase: docker.NetworkSettingsBase{
Ports: nat.PortMap{
"80/tcp": {},
},
},
},
},
provider: &Provider{
Domain: "test",
ExposedByDefault: true,
},
expected: true,
}, },
{ {
container: containerJSON( container: docker.ContainerJSON{
labels(map[string]string{ ContainerJSONBase: &docker.ContainerJSONBase{
"traefik.enable": "anything", Name: "container",
}), },
ports(nat.PortMap{ Config: &container.Config{
"80/tcp": {}, Labels: map[string]string{
}), "traefik.enable": "anything",
), },
exposedByDefault: true, },
expected: true, NetworkSettings: &docker.NetworkSettings{
NetworkSettingsBase: docker.NetworkSettingsBase{
Ports: nat.PortMap{
"80/tcp": {},
},
},
},
},
provider: &Provider{
Domain: "test",
ExposedByDefault: true,
},
expected: true,
}, },
{ {
container: containerJSON( container: docker.ContainerJSON{
labels(map[string]string{ ContainerJSONBase: &docker.ContainerJSONBase{
"traefik.frontend.rule": "Host:foo.bar", Name: "container",
}), },
ports(nat.PortMap{ Config: &container.Config{
"80/tcp": {}, Labels: map[string]string{
}), "traefik.frontend.rule": "Host:foo.bar",
), },
exposedByDefault: true, },
expected: true, NetworkSettings: &docker.NetworkSettings{
NetworkSettingsBase: docker.NetworkSettingsBase{
Ports: nat.PortMap{
"80/tcp": {},
},
},
},
},
provider: &Provider{
Domain: "test",
ExposedByDefault: true,
},
expected: true,
}, },
{ {
container: containerJSON( container: docker.ContainerJSON{
ports(nat.PortMap{ ContainerJSONBase: &docker.ContainerJSONBase{
"80/tcp": {}, Name: "container",
}), },
), Config: &container.Config{},
exposedByDefault: false, NetworkSettings: &docker.NetworkSettings{
expected: false, NetworkSettingsBase: docker.NetworkSettingsBase{
Ports: nat.PortMap{
"80/tcp": {},
},
},
},
},
provider: &Provider{
Domain: "test",
ExposedByDefault: false,
},
expected: false,
}, },
{ {
container: containerJSON( container: docker.ContainerJSON{
labels(map[string]string{ ContainerJSONBase: &docker.ContainerJSONBase{
"traefik.enable": "true", Name: "container",
}), },
ports(nat.PortMap{ Config: &container.Config{
"80/tcp": {}, Labels: map[string]string{
}), "traefik.enable": "true",
), },
exposedByDefault: false, },
expected: 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 e := e
t.Run(strconv.Itoa(containerID), func(t *testing.T) { t.Run(strconv.Itoa(containerID), func(t *testing.T) {
t.Parallel() t.Parallel()
provider := Provider{}
provider.ExposedByDefault = e.exposedByDefault
dockerData := parseContainer(e.container) dockerData := parseContainer(e.container)
actual := provider.containerFilter(dockerData) actual := e.provider.containerFilter(dockerData)
if actual != e.expected { 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)
} }
}) })
} }

View file

@ -133,7 +133,7 @@ func TestDockerGetServiceFrontendRule(t *testing.T) {
}{ }{
{ {
container: containerJSON(name("foo")), container: containerJSON(name("foo")),
expected: "Host:foo.", expected: "",
}, },
{ {
container: containerJSON(labels(map[string]string{ container: containerJSON(labels(map[string]string{

View file

@ -503,86 +503,122 @@ func TestSwarmGetLabels(t *testing.T) {
func TestSwarmTraefikFilter(t *testing.T) { func TestSwarmTraefikFilter(t *testing.T) {
services := []struct { services := []struct {
service swarm.Service service swarm.Service
exposedByDefault bool expected bool
expected bool networks map[string]*docker.NetworkResource
networks map[string]*docker.NetworkResource provider *Provider
}{ }{
{ {
service: swarmService(), service: swarmService(),
exposedByDefault: true, expected: false,
expected: false, networks: map[string]*docker.NetworkResource{},
networks: map[string]*docker.NetworkResource{}, provider: &Provider{
SwarmMode: true,
Domain: "test",
ExposedByDefault: true,
},
}, },
{ {
service: swarmService(serviceLabels(map[string]string{ service: swarmService(serviceLabels(map[string]string{
"traefik.enable": "false", "traefik.enable": "false",
"traefik.port": "80", "traefik.port": "80",
})), })),
exposedByDefault: true, expected: false,
expected: false, networks: map[string]*docker.NetworkResource{},
networks: map[string]*docker.NetworkResource{}, provider: &Provider{
SwarmMode: true,
Domain: "test",
ExposedByDefault: true,
},
}, },
{ {
service: swarmService(serviceLabels(map[string]string{ service: swarmService(serviceLabels(map[string]string{
"traefik.frontend.rule": "Host:foo.bar", "traefik.frontend.rule": "Host:foo.bar",
"traefik.port": "80", "traefik.port": "80",
})), })),
exposedByDefault: true, expected: true,
expected: true, networks: map[string]*docker.NetworkResource{},
networks: map[string]*docker.NetworkResource{}, provider: &Provider{
SwarmMode: true,
Domain: "test",
ExposedByDefault: true,
},
}, },
{ {
service: swarmService(serviceLabels(map[string]string{ service: swarmService(serviceLabels(map[string]string{
"traefik.port": "80", "traefik.port": "80",
})), })),
exposedByDefault: true, expected: true,
expected: true, networks: map[string]*docker.NetworkResource{},
networks: map[string]*docker.NetworkResource{}, provider: &Provider{
SwarmMode: true,
Domain: "test",
ExposedByDefault: true,
},
}, },
{ {
service: swarmService(serviceLabels(map[string]string{ service: swarmService(serviceLabels(map[string]string{
"traefik.enable": "true", "traefik.enable": "true",
"traefik.port": "80", "traefik.port": "80",
})), })),
exposedByDefault: true, expected: true,
expected: true, networks: map[string]*docker.NetworkResource{},
networks: map[string]*docker.NetworkResource{}, provider: &Provider{
SwarmMode: true,
Domain: "test",
ExposedByDefault: true,
},
}, },
{ {
service: swarmService(serviceLabels(map[string]string{ service: swarmService(serviceLabels(map[string]string{
"traefik.enable": "anything", "traefik.enable": "anything",
"traefik.port": "80", "traefik.port": "80",
})), })),
exposedByDefault: true, expected: true,
expected: true, networks: map[string]*docker.NetworkResource{},
networks: map[string]*docker.NetworkResource{}, provider: &Provider{
SwarmMode: true,
Domain: "test",
ExposedByDefault: true,
},
}, },
{ {
service: swarmService(serviceLabels(map[string]string{ service: swarmService(serviceLabels(map[string]string{
"traefik.frontend.rule": "Host:foo.bar", "traefik.frontend.rule": "Host:foo.bar",
"traefik.port": "80", "traefik.port": "80",
})), })),
exposedByDefault: true, expected: true,
expected: true, networks: map[string]*docker.NetworkResource{},
networks: map[string]*docker.NetworkResource{}, provider: &Provider{
SwarmMode: true,
Domain: "test",
ExposedByDefault: true,
},
}, },
{ {
service: swarmService(serviceLabels(map[string]string{ service: swarmService(serviceLabels(map[string]string{
"traefik.port": "80", "traefik.port": "80",
})), })),
exposedByDefault: false, expected: false,
expected: false, networks: map[string]*docker.NetworkResource{},
networks: map[string]*docker.NetworkResource{}, provider: &Provider{
SwarmMode: true,
Domain: "test",
ExposedByDefault: false,
},
}, },
{ {
service: swarmService(serviceLabels(map[string]string{ service: swarmService(serviceLabels(map[string]string{
"traefik.enable": "true", "traefik.enable": "true",
"traefik.port": "80", "traefik.port": "80",
})), })),
exposedByDefault: false, expected: true,
expected: true, networks: map[string]*docker.NetworkResource{},
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.Run(strconv.Itoa(serviceID), func(t *testing.T) {
t.Parallel() t.Parallel()
dockerData := parseService(e.service, e.networks) dockerData := parseService(e.service, e.networks)
provider := &Provider{ actual := e.provider.containerFilter(dockerData)
SwarmMode: true,
}
provider.ExposedByDefault = e.exposedByDefault
actual := provider.containerFilter(dockerData)
if actual != e.expected { if actual != e.expected {
t.Errorf("expected %v for %+v, got %+v", e.expected, e, actual) t.Errorf("expected %v for %+v, got %+v", e.expected, e, actual)
} }

View file

@ -315,15 +315,16 @@ func (server *Server) postLoadConfig() {
for _, frontend := range configuration.Frontends { for _, frontend := range configuration.Frontends {
// check if one of the frontend entrypoints is configured with TLS // 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 { for _, entrypoint := range frontend.EntryPoints {
if server.globalConfiguration.EntryPoints[entrypoint].TLS != nil { if server.globalConfiguration.ACME.EntryPoint == entrypoint && server.globalConfiguration.EntryPoints[entrypoint].TLS != nil {
TLSEnabled = true ACMEEnabled = true
break break
} }
} }
if TLSEnabled { if ACMEEnabled {
for _, route := range frontend.Routes { for _, route := range frontend.Routes {
rules := Rules{} rules := Rules{}
domains, err := rules.ParseDomains(route.Rule) domains, err := rules.ParseDomains(route.Rule)
@ -547,13 +548,11 @@ func (server *Server) buildEntryPoints(globalConfiguration GlobalConfiguration)
// provider configurations. // provider configurations.
func (server *Server) loadConfig(configurations configs, globalConfiguration GlobalConfiguration) (map[string]*serverEntryPoint, error) { func (server *Server) loadConfig(configurations configs, globalConfiguration GlobalConfiguration) (map[string]*serverEntryPoint, error) {
serverEntryPoints := server.buildEntryPoints(globalConfiguration) serverEntryPoints := server.buildEntryPoints(globalConfiguration)
redirectHandlers := make(map[string]http.Handler) redirectHandlers := make(map[string]negroni.Handler)
backends := map[string]http.Handler{} backends := map[string]http.Handler{}
backendsHealthcheck := map[string]*healthcheck.BackendHealthCheck{} backendsHealthcheck := map[string]*healthcheck.BackendHealthCheck{}
backend2FrontendMap := map[string]string{} backend2FrontendMap := map[string]string{}
for _, configuration := range configurations { for _, configuration := range configurations {
frontendNames := sortedFrontendNamesForConfig(configuration) frontendNames := sortedFrontendNamesForConfig(configuration)
frontend: frontend:
@ -595,172 +594,170 @@ func (server *Server) loadConfig(configurations configs, globalConfiguration Glo
} }
entryPoint := globalConfiguration.EntryPoints[entryPointName] entryPoint := globalConfiguration.EntryPoints[entryPointName]
negroni := negroni.New()
if entryPoint.Redirect != nil { if entryPoint.Redirect != nil {
if redirectHandlers[entryPointName] != nil { if redirectHandlers[entryPointName] != nil {
newServerRoute.route.Handler(redirectHandlers[entryPointName]) negroni.Use(redirectHandlers[entryPointName])
} else if handler, err := server.loadEntryPointConfig(entryPointName, entryPoint); err != nil { } else if handler, err := server.loadEntryPointConfig(entryPointName, entryPoint); err != nil {
log.Errorf("Error loading entrypoint configuration for frontend %s: %v", frontendName, err) log.Errorf("Error loading entrypoint configuration for frontend %s: %v", frontendName, err)
log.Errorf("Skipping frontend %s...", frontendName) log.Errorf("Skipping frontend %s...", frontendName)
continue frontend continue frontend
} else { } else {
newServerRoute.route.Handler(handler) negroni.Use(handler)
redirectHandlers[entryPointName] = handler redirectHandlers[entryPointName] = handler
} }
} else { }
if backends[frontend.Backend] == nil { if backends[entryPointName+frontend.Backend] == nil {
log.Debugf("Creating backend %s", frontend.Backend) log.Debugf("Creating backend %s", frontend.Backend)
var lb http.Handler var lb http.Handler
rr, _ := roundrobin.New(saveBackend) rr, _ := roundrobin.New(saveBackend)
if configuration.Backends[frontend.Backend] == nil { if configuration.Backends[frontend.Backend] == nil {
log.Errorf("Undefined backend '%s' for frontend %s", frontend.Backend, frontendName) log.Errorf("Undefined backend '%s' for frontend %s", frontend.Backend, frontendName)
log.Errorf("Skipping frontend %s...", frontendName) log.Errorf("Skipping frontend %s...", frontendName)
continue frontend continue frontend
} }
lbMethod, err := types.NewLoadBalancerMethod(configuration.Backends[frontend.Backend].LoadBalancer) lbMethod, err := types.NewLoadBalancerMethod(configuration.Backends[frontend.Backend].LoadBalancer)
if err != nil { if err != nil {
log.Errorf("Error loading load balancer method '%+v' for frontend %s: %v", configuration.Backends[frontend.Backend].LoadBalancer, frontendName, err) 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) log.Errorf("Skipping frontend %s...", frontendName)
continue frontend continue frontend
} }
stickysession := configuration.Backends[frontend.Backend].LoadBalancer.Sticky stickysession := configuration.Backends[frontend.Backend].LoadBalancer.Sticky
cookiename := "_TRAEFIK_BACKEND" cookiename := "_TRAEFIK_BACKEND"
var sticky *roundrobin.StickySession 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 { if stickysession {
sticky = roundrobin.NewStickySession(cookiename) log.Debugf("Sticky session with cookie %v", cookiename)
rebalancer, _ = roundrobin.NewRebalancer(rr, roundrobin.RebalancerLogger(oxyLogger), roundrobin.RebalancerStickySession(sticky))
} }
lb = rebalancer
switch lbMethod { for serverName, server := range configuration.Backends[frontend.Backend].Servers {
case types.Drr: url, err := url.Parse(server.URL)
log.Debugf("Creating load-balancer drr") if err != nil {
rebalancer, _ := roundrobin.NewRebalancer(rr, roundrobin.RebalancerLogger(oxyLogger)) log.Errorf("Error parsing server URL %s: %v", server.URL, err)
if stickysession { log.Errorf("Skipping frontend %s...", frontendName)
log.Debugf("Sticky session with cookie %v", cookiename) continue frontend
rebalancer, _ = roundrobin.NewRebalancer(rr, roundrobin.RebalancerLogger(oxyLogger), roundrobin.RebalancerStickySession(sticky))
} }
lb = rebalancer backend2FrontendMap[url.String()] = frontendName
for serverName, server := range configuration.Backends[frontend.Backend].Servers { log.Debugf("Creating server %s at %s with weight %d", serverName, url.String(), server.Weight)
url, err := url.Parse(server.URL) if err := rebalancer.UpsertServer(url, roundrobin.Weight(server.Weight)); err != nil {
if err != nil { log.Errorf("Error adding server %s to load balancer: %v", server.URL, err)
log.Errorf("Error parsing server URL %s: %v", server.URL, err) log.Errorf("Skipping frontend %s...", frontendName)
log.Errorf("Skipping frontend %s...", frontendName) continue frontend
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)
}
} }
case types.Wrr: hcOpts := parseHealthCheckOptions(rebalancer, frontend.Backend, configuration.Backends[frontend.Backend].HealthCheck, *globalConfiguration.HealthCheck)
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)
if hcOpts != nil { if hcOpts != nil {
log.Debugf("Setting up backend health check %s", *hcOpts) log.Debugf("Setting up backend health check %s", *hcOpts)
backendsHealthcheck[frontend.Backend] = healthcheck.NewBackendHealthCheck(*hcOpts) backendsHealthcheck[frontend.Backend] = healthcheck.NewBackendHealthCheck(*hcOpts)
} }
} }
maxConns := configuration.Backends[frontend.Backend].MaxConn case types.Wrr:
if maxConns != nil && maxConns.Amount != 0 { log.Debugf("Creating load-balancer wrr")
extractFunc, err := utils.NewExtractor(maxConns.ExtractorFunc) 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 { 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) log.Errorf("Skipping frontend %s...", frontendName)
continue frontend continue frontend
} }
log.Debugf("Creating load-balancer connlimit") backend2FrontendMap[url.String()] = frontendName
lb, err = connlimit.New(lb, extractFunc, maxConns.Amount, connlimit.Logger(oxyLogger)) log.Debugf("Creating server %s at %s with weight %d", serverName, url.String(), server.Weight)
if err != nil { if err := rr.UpsertServer(url, roundrobin.Weight(server.Weight)); err != nil {
log.Errorf("Error creating connlimit: %v", err) log.Errorf("Error adding server %s to load balancer: %v", server.URL, err)
log.Errorf("Skipping frontend %s...", frontendName) log.Errorf("Skipping frontend %s...", frontendName)
continue frontend continue frontend
} }
} }
// retry ? hcOpts := parseHealthCheckOptions(rr, frontend.Backend, configuration.Backends[frontend.Backend].HealthCheck, *globalConfiguration.HealthCheck)
if globalConfiguration.Retry != nil { if hcOpts != nil {
retries := len(configuration.Backends[frontend.Backend].Servers) log.Debugf("Setting up backend health check %s", *hcOpts)
if globalConfiguration.Retry.Attempts > 0 { backendsHealthcheck[frontend.Backend] = healthcheck.NewBackendHealthCheck(*hcOpts)
retries = globalConfiguration.Retry.Attempts }
} }
lb = middlewares.NewRetry(retries, lb) maxConns := configuration.Backends[frontend.Backend].MaxConn
log.Debugf("Creating retries max attempts %d", retries) 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() auth := &types.Auth{}
if server.globalConfiguration.Web != nil && server.globalConfiguration.Web.Metrics != nil { auth.Basic = &types.Basic{
if server.globalConfiguration.Web.Metrics.Prometheus != nil { Users: users,
metricsMiddlewareBackend := middlewares.NewMetricsWrapper(middlewares.NewPrometheus(frontend.Backend, server.globalConfiguration.Web.Metrics.Prometheus))
negroni.Use(metricsMiddlewareBackend)
}
} }
authMiddleware, err := middlewares.NewAuthenticator(auth)
if len(frontend.BasicAuth) > 0 { if err != nil {
users := types.Users{} log.Fatal("Error creating Auth: ", err)
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)
} }
negroni.Use(authMiddleware)
if configuration.Backends[frontend.Backend].CircuitBreaker != nil { }
log.Debugf("Creating circuit breaker %s", configuration.Backends[frontend.Backend].CircuitBreaker.Expression) if configuration.Backends[frontend.Backend].CircuitBreaker != nil {
cbreaker, err := middlewares.NewCircuitBreaker(lb, configuration.Backends[frontend.Backend].CircuitBreaker.Expression, cbreaker.Logger(oxyLogger)) log.Debugf("Creating circuit breaker %s", configuration.Backends[frontend.Backend].CircuitBreaker.Expression)
if err != nil { cbreaker, err := middlewares.NewCircuitBreaker(lb, configuration.Backends[frontend.Backend].CircuitBreaker.Expression, cbreaker.Logger(oxyLogger))
log.Errorf("Error creating circuit breaker: %v", err) if err != nil {
log.Errorf("Skipping frontend %s...", frontendName) log.Errorf("Error creating circuit breaker: %v", err)
continue frontend log.Errorf("Skipping frontend %s...", frontendName)
} continue frontend
negroni.Use(cbreaker)
} else {
negroni.UseHandler(lb)
} }
backends[frontend.Backend] = negroni negroni.Use(cbreaker)
} else { } else {
log.Debugf("Reusing backend %s", frontend.Backend) negroni.UseHandler(lb)
} }
if frontend.Priority > 0 { backends[entryPointName+frontend.Backend] = negroni
newServerRoute.route.Priority(frontend.Priority) } else {
} log.Debugf("Reusing backend %s", frontend.Backend)
server.wireFrontendBackend(newServerRoute, backends[frontend.Backend])
} }
if frontend.Priority > 0 {
newServerRoute.route.Priority(frontend.Priority)
}
server.wireFrontendBackend(newServerRoute, backends[entryPointName+frontend.Backend])
err := newServerRoute.route.GetError() err := newServerRoute.route.GetError()
if err != nil { if err != nil {
log.Errorf("Error building route: %s", err) log.Errorf("Error building route: %s", err)
@ -810,7 +807,7 @@ func (server *Server) wireFrontendBackend(serverRoute *serverRoute, handler http
serverRoute.route.Handler(handler) 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 regex := entryPoint.Redirect.Regex
replacement := entryPoint.Redirect.Replacement replacement := entryPoint.Redirect.Replacement
if len(entryPoint.Redirect.EntryPoint) > 0 { if len(entryPoint.Redirect.EntryPoint) > 0 {
@ -834,9 +831,8 @@ func (server *Server) loadEntryPointConfig(entryPointName string, entryPoint *En
return nil, err return nil, err
} }
log.Debugf("Creating entryPoint redirect %s -> %s : %s -> %s", entryPointName, entryPoint.Redirect.EntryPoint, regex, replacement) log.Debugf("Creating entryPoint redirect %s -> %s : %s -> %s", entryPointName, entryPoint.Redirect.EntryPoint, regex, replacement)
negroni := negroni.New()
negroni.Use(rewrite) return rewrite, nil
return negroni, nil
} }
func (server *Server) buildDefaultHTTPRouter() *mux.Router { func (server *Server) buildDefaultHTTPRouter() *mux.Router {