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:
parent
4783c7f70a
commit
99ca5d0a03
5 changed files with 298 additions and 15 deletions
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue