1
0
Fork 0

Make Traefik health checks label-configurable with Marathon.

For the two existing health check parameters (path and interval), we add
support for Marathon labels.

Changes in detail:

- Extend the Marathon provider and template.
- Refactor Server.loadConfig to reduce duplication.
- Refactor the healthcheck package slightly to accommodate the changes
  and allow extending by future parameters.
- Update documentation.
This commit is contained in:
Timo Reimann 2017-03-15 19:16:06 +01:00
parent 441d5442a1
commit d57f83c31c
8 changed files with 371 additions and 45 deletions

View file

@ -24,8 +24,10 @@ import (
)
const (
labelPort = "traefik.port"
labelPortIndex = "traefik.portIndex"
labelPort = "traefik.port"
labelPortIndex = "traefik.portIndex"
labelBackendHealthCheckPath = "traefik.backend.healthcheck.path"
labelBackendHealthCheckInterval = "traefik.backend.healthcheck.interval"
)
var _ provider.Provider = (*Provider)(nil)
@ -157,6 +159,9 @@ func (p *Provider) loadMarathonConfig() *types.Configuration {
"getLoadBalancerMethod": p.getLoadBalancerMethod,
"getCircuitBreakerExpression": p.getCircuitBreakerExpression,
"getSticky": p.getSticky,
"hasHealthCheckLabels": p.hasHealthCheckLabels,
"getHealthCheckPath": p.getHealthCheckPath,
"getHealthCheckInterval": p.getHealthCheckInterval,
}
applications, err := p.marathonClient.Applications(nil)
@ -461,6 +466,24 @@ func (p *Provider) getCircuitBreakerExpression(application marathon.Application)
return "NetworkErrorRatio() > 1"
}
func (p *Provider) hasHealthCheckLabels(application marathon.Application) bool {
return p.getHealthCheckPath(application) != ""
}
func (p *Provider) getHealthCheckPath(application marathon.Application) string {
if label, ok := p.getLabel(application, labelBackendHealthCheckPath); ok {
return label
}
return ""
}
func (p *Provider) getHealthCheckInterval(application marathon.Application) string {
if label, ok := p.getLabel(application, labelBackendHealthCheckInterval); ok {
return label
}
return ""
}
func processPorts(application marathon.Application, task marathon.Task) (int, error) {
if portLabel, ok := (*application.Labels)[labelPort]; ok {
port, err := strconv.Atoi(portLabel)

View file

@ -361,10 +361,10 @@ func TestMarathonLoadConfig(t *testing.T) {
} else {
// Compare backends
if !reflect.DeepEqual(actualConfig.Backends, c.expectedBackends) {
t.Errorf("got backend %v, want %v", spew.Sdump(actualConfig.Backends), spew.Sdump(c.expectedBackends))
t.Errorf("got backend\n%v\nwant\n\n%v", spew.Sdump(actualConfig.Backends), spew.Sdump(c.expectedBackends))
}
if !reflect.DeepEqual(actualConfig.Frontends, c.expectedFrontends) {
t.Errorf("got frontend %v, want %v", spew.Sdump(actualConfig.Frontends), spew.Sdump(c.expectedFrontends))
t.Errorf("got frontend\n%v\nwant\n\n%v", spew.Sdump(actualConfig.Frontends), spew.Sdump(c.expectedFrontends))
}
}
})
@ -1430,6 +1430,125 @@ func TestMarathonGetSubDomain(t *testing.T) {
}
}
func TestMarathonHasHealthCheckLabels(t *testing.T) {
tests := []struct {
desc string
value *string
want bool
}{
{
desc: "label missing",
value: nil,
want: false,
},
{
desc: "empty path",
value: stringp(""),
want: false,
},
{
desc: "non-empty path",
value: stringp("/path"),
want: true,
},
}
for _, test := range tests {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
app := marathon.Application{
Labels: &map[string]string{},
}
if test.value != nil {
app.AddLabel(labelBackendHealthCheckPath, *test.value)
}
prov := &Provider{}
got := prov.hasHealthCheckLabels(app)
if got != test.want {
t.Errorf("got %t, want %t", got, test.want)
}
})
}
}
func TestMarathonGetHealthCheckPath(t *testing.T) {
tests := []struct {
desc string
value *string
want string
}{
{
desc: "label missing",
value: nil,
want: "",
},
{
desc: "path existing",
value: stringp("/path"),
want: "/path",
},
}
for _, test := range tests {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
app := marathon.Application{}
app.EmptyLabels()
if test.value != nil {
app.AddLabel(labelBackendHealthCheckPath, *test.value)
}
prov := &Provider{}
got := prov.getHealthCheckPath(app)
if got != test.want {
t.Errorf("got %s, want %s", got, test.want)
}
})
}
}
func TestMarathonGetHealthCheckInterval(t *testing.T) {
tests := []struct {
desc string
value *string
want string
}{
{
desc: "label missing",
value: nil,
want: "",
},
{
desc: "interval existing",
value: stringp("5m"),
want: "5m",
},
}
for _, test := range tests {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
app := marathon.Application{
Labels: &map[string]string{},
}
if test.value != nil {
app.AddLabel(labelBackendHealthCheckInterval, *test.value)
}
prov := &Provider{}
got := prov.getHealthCheckInterval(app)
if got != test.want {
t.Errorf("got %s, want %s", got, test.want)
}
})
}
}
func stringp(s string) *string {
return &s
}
func TestGetBackendServer(t *testing.T) {
appID := "appId"
host := "host"