1
0
Fork 0

Add ability for marathon provider to set maxconn values

Initial implementation: Force both to be present to trigger behavior.

add ability to see rendered template in debug

add support for loadbalancer and circuit breaker specification

add documentation for new configuration
This commit is contained in:
Bruce Lee 2016-08-13 12:55:15 -04:00
parent 4783c7f70a
commit 99ca5d0a03
5 changed files with 298 additions and 15 deletions

View file

@ -8,14 +8,16 @@ import (
"strings"
"text/template"
"math"
"net/http"
"time"
"github.com/BurntSushi/ty/fun"
log "github.com/Sirupsen/logrus"
"github.com/containous/traefik/safe"
"github.com/containous/traefik/types"
"github.com/emilevauge/backoff"
"github.com/gambol99/go-marathon"
"net/http"
"time"
)
// Marathon holds configuration of the Marathon provider.
@ -117,17 +119,24 @@ func (provider *Marathon) Provide(configurationChan chan<- types.ConfigMessage,
func (provider *Marathon) loadMarathonConfig() *types.Configuration {
var MarathonFuncMap = template.FuncMap{
"getBackend": provider.getBackend,
"getPort": provider.getPort,
"getWeight": provider.getWeight,
"getDomain": provider.getDomain,
"getProtocol": provider.getProtocol,
"getPassHostHeader": provider.getPassHostHeader,
"getPriority": provider.getPriority,
"getEntryPoints": provider.getEntryPoints,
"getFrontendRule": provider.getFrontendRule,
"getFrontendBackend": provider.getFrontendBackend,
"replace": replace,
"getBackend": provider.getBackend,
"getPort": provider.getPort,
"getWeight": provider.getWeight,
"getDomain": provider.getDomain,
"getProtocol": provider.getProtocol,
"getPassHostHeader": provider.getPassHostHeader,
"getPriority": provider.getPriority,
"getEntryPoints": provider.getEntryPoints,
"getFrontendRule": provider.getFrontendRule,
"getFrontendBackend": provider.getFrontendBackend,
"replace": replace,
"hasCircuitBreakerLabels": provider.hasCircuitBreakerLabels,
"hasLoadBalancerLabels": provider.hasLoadBalancerLabels,
"hasMaxConnLabels": provider.hasMaxConnLabels,
"getMaxConnExtractorFunc": provider.getMaxConnExtractorFunc,
"getMaxConnAmount": provider.getMaxConnAmount,
"getLoadBalancerMethod": provider.getLoadBalancerMethod,
"getCircuitBreakerExpression": provider.getCircuitBreakerExpression,
}
applications, err := provider.marathonClient.Applications(nil)
@ -375,3 +384,60 @@ func (provider *Marathon) getSubDomain(name string) string {
}
return strings.Replace(strings.TrimPrefix(name, "/"), "/", "-", -1)
}
func (provider *Marathon) hasCircuitBreakerLabels(application marathon.Application) bool {
if _, err := provider.getLabel(application, "traefik.backend.circuitbreaker.expression"); err != nil {
return false
}
return true
}
func (provider *Marathon) hasLoadBalancerLabels(application marathon.Application) bool {
if _, err := provider.getLabel(application, "traefik.backend.loadbalancer.method"); err != nil {
return false
}
return true
}
func (provider *Marathon) hasMaxConnLabels(application marathon.Application) bool {
if _, err := provider.getLabel(application, "traefik.backend.maxconn.amount"); err != nil {
return false
}
if _, err := provider.getLabel(application, "traefik.backend.maxconn.extractorfunc"); err != nil {
return false
}
return true
}
func (provider *Marathon) getMaxConnAmount(application marathon.Application) int64 {
if label, err := provider.getLabel(application, "traefik.backend.maxconn.amount"); err == nil {
i, errConv := strconv.ParseInt(label, 10, 64)
if errConv != nil {
log.Errorf("Unable to parse traefik.backend.maxconn.amount %s", label)
return math.MaxInt64
}
return i
}
return math.MaxInt64
}
func (provider *Marathon) getMaxConnExtractorFunc(application marathon.Application) string {
if label, err := provider.getLabel(application, "traefik.backend.maxconn.extractorfunc"); err == nil {
return label
}
return "request.host"
}
func (provider *Marathon) getLoadBalancerMethod(application marathon.Application) string {
if label, err := provider.getLabel(application, "traefik.backend.loadbalancer.method"); err == nil {
return label
}
return "wrr"
}
func (provider *Marathon) getCircuitBreakerExpression(application marathon.Application) string {
if label, err := provider.getLabel(application, "traefik.backend.circuitbreaker.expression"); err == nil {
return label
}
return "NetworkErrorRatio() > 1"
}

View file

@ -111,6 +111,200 @@ func TestMarathonLoadConfig(t *testing.T) {
},
},
},
{
applications: &marathon.Applications{
Apps: []marathon.Application{
{
ID: "/testLoadBalancerAndCircuitBreaker",
Ports: []int{80},
Labels: &map[string]string{
"traefik.backend.loadbalancer.method": "drr",
"traefik.backend.circuitbreaker.expression": "NetworkErrorRatio() > 0.5",
},
},
},
},
tasks: &marathon.Tasks{
Tasks: []marathon.Task{
{
ID: "testLoadBalancerAndCircuitBreaker",
AppID: "/testLoadBalancerAndCircuitBreaker",
Host: "127.0.0.1",
Ports: []int{80},
},
},
},
expectedFrontends: map[string]*types.Frontend{
`frontend-testLoadBalancerAndCircuitBreaker`: {
Backend: "backend-testLoadBalancerAndCircuitBreaker",
PassHostHeader: true,
EntryPoints: []string{},
Routes: map[string]types.Route{
`route-host-testLoadBalancerAndCircuitBreaker`: {
Rule: "Host:testLoadBalancerAndCircuitBreaker.docker.localhost",
},
},
},
},
expectedBackends: map[string]*types.Backend{
"backend-testLoadBalancerAndCircuitBreaker": {
Servers: map[string]types.Server{
"server-testLoadBalancerAndCircuitBreaker": {
URL: "http://127.0.0.1:80",
Weight: 0,
},
},
CircuitBreaker: &types.CircuitBreaker{
Expression: "NetworkErrorRatio() > 0.5",
},
LoadBalancer: &types.LoadBalancer{
Method: "drr",
},
},
},
},
{
applications: &marathon.Applications{
Apps: []marathon.Application{
{
ID: "/testMaxConn",
Ports: []int{80},
Labels: &map[string]string{
"traefik.backend.maxconn.amount": "1000",
"traefik.backend.maxconn.extractorfunc": "client.ip",
},
},
},
},
tasks: &marathon.Tasks{
Tasks: []marathon.Task{
{
ID: "testMaxConn",
AppID: "/testMaxConn",
Host: "127.0.0.1",
Ports: []int{80},
},
},
},
expectedFrontends: map[string]*types.Frontend{
`frontend-testMaxConn`: {
Backend: "backend-testMaxConn",
PassHostHeader: true,
EntryPoints: []string{},
Routes: map[string]types.Route{
`route-host-testMaxConn`: {
Rule: "Host:testMaxConn.docker.localhost",
},
},
},
},
expectedBackends: map[string]*types.Backend{
"backend-testMaxConn": {
Servers: map[string]types.Server{
"server-testMaxConn": {
URL: "http://127.0.0.1:80",
Weight: 0,
},
},
MaxConn: &types.MaxConn{
Amount: 1000,
ExtractorFunc: "client.ip",
},
},
},
},
{
applications: &marathon.Applications{
Apps: []marathon.Application{
{
ID: "/testMaxConnOnlySpecifyAmount",
Ports: []int{80},
Labels: &map[string]string{
"traefik.backend.maxconn.amount": "1000",
},
},
},
},
tasks: &marathon.Tasks{
Tasks: []marathon.Task{
{
ID: "testMaxConnOnlySpecifyAmount",
AppID: "/testMaxConnOnlySpecifyAmount",
Host: "127.0.0.1",
Ports: []int{80},
},
},
},
expectedFrontends: map[string]*types.Frontend{
`frontend-testMaxConnOnlySpecifyAmount`: {
Backend: "backend-testMaxConnOnlySpecifyAmount",
PassHostHeader: true,
EntryPoints: []string{},
Routes: map[string]types.Route{
`route-host-testMaxConnOnlySpecifyAmount`: {
Rule: "Host:testMaxConnOnlySpecifyAmount.docker.localhost",
},
},
},
},
expectedBackends: map[string]*types.Backend{
"backend-testMaxConnOnlySpecifyAmount": {
Servers: map[string]types.Server{
"server-testMaxConnOnlySpecifyAmount": {
URL: "http://127.0.0.1:80",
Weight: 0,
},
},
MaxConn: nil,
},
},
},
{
applications: &marathon.Applications{
Apps: []marathon.Application{
{
ID: "/testMaxConnOnlyExtractorFunc",
Ports: []int{80},
Labels: &map[string]string{
"traefik.backend.maxconn.extractorfunc": "client.ip",
},
},
},
},
tasks: &marathon.Tasks{
Tasks: []marathon.Task{
{
ID: "testMaxConnOnlyExtractorFunc",
AppID: "/testMaxConnOnlyExtractorFunc",
Host: "127.0.0.1",
Ports: []int{80},
},
},
},
expectedFrontends: map[string]*types.Frontend{
`frontend-testMaxConnOnlyExtractorFunc`: {
Backend: "backend-testMaxConnOnlyExtractorFunc",
PassHostHeader: true,
EntryPoints: []string{},
Routes: map[string]types.Route{
`route-host-testMaxConnOnlyExtractorFunc`: {
Rule: "Host:testMaxConnOnlyExtractorFunc.docker.localhost",
},
},
},
},
expectedBackends: map[string]*types.Backend{
"backend-testMaxConnOnlyExtractorFunc": {
Servers: map[string]types.Server{
"server-testMaxConnOnlyExtractorFunc": {
URL: "http://127.0.0.1:80",
Weight: 0,
},
},
MaxConn: nil,
},
},
},
}
for _, c := range cases {

View file

@ -10,12 +10,13 @@ import (
"crypto/tls"
"crypto/x509"
"fmt"
"os"
"github.com/BurntSushi/toml"
log "github.com/Sirupsen/logrus"
"github.com/containous/traefik/autogen"
"github.com/containous/traefik/safe"
"github.com/containous/traefik/types"
"os"
)
// Provider defines methods of a provider.
@ -80,7 +81,9 @@ func (p *BaseProvider) getConfiguration(defaultTemplateFile string, funcMap temp
return nil, err
}
if _, err := toml.Decode(buffer.String(), configuration); err != nil {
var renderedTemplate = buffer.String()
// log.Debugf("Rendering results of %s:\n%s", defaultTemplateFile, renderedTemplate)
if _, err := toml.Decode(renderedTemplate, configuration); err != nil {
return nil, err
}
return configuration, nil