diff --git a/provider/marathon.go b/provider/marathon.go index 27982b5ce..afb9c105e 100644 --- a/provider/marathon.go +++ b/provider/marathon.go @@ -153,12 +153,12 @@ func (provider *Marathon) loadMarathonConfig() *types.Configuration { //filter tasks filteredTasks := fun.Filter(func(task marathon.Task) bool { - return taskFilter(task, applications, provider.ExposedByDefault) + return provider.taskFilter(task, applications, provider.ExposedByDefault) }, tasks.Tasks).([]marathon.Task) //filter apps filteredApps := fun.Filter(func(app marathon.Application) bool { - return applicationFilter(app, filteredTasks) + return provider.applicationFilter(app, filteredTasks) }, applications.Apps).([]marathon.Application) templateObjects := struct { @@ -178,7 +178,7 @@ func (provider *Marathon) loadMarathonConfig() *types.Configuration { return configuration } -func taskFilter(task marathon.Task, applications *marathon.Applications, exposedByDefaultFlag bool) bool { +func (provider *Marathon) taskFilter(task marathon.Task, applications *marathon.Applications, exposedByDefaultFlag bool) bool { if len(task.Ports) == 0 { log.Debug("Filtering marathon task without port %s", task.AppID) return false @@ -188,6 +188,15 @@ func taskFilter(task marathon.Task, applications *marathon.Applications, exposed log.Errorf("Unable to get marathon application from task %s", task.AppID) return false } + if label, err := provider.getLabel(application, "traefik.tags"); err == nil { + constraintTags := strings.Split(label, ",") + if ok, failingConstraint := provider.MatchConstraints(constraintTags); !ok { + if failingConstraint != nil { + log.Debugf("Application %v pruned by '%v' constraint", application.ID, failingConstraint.String()) + } + return false + } + } if !isApplicationEnabled(application, exposedByDefaultFlag) { log.Debugf("Filtering disabled marathon task %s", task.AppID) @@ -248,7 +257,15 @@ func taskFilter(task marathon.Task, applications *marathon.Applications, exposed return true } -func applicationFilter(app marathon.Application, filteredTasks []marathon.Task) bool { +func (provider *Marathon) applicationFilter(app marathon.Application, filteredTasks []marathon.Task) bool { + constraintTags := strings.Split((*app.Labels)["traefik.tags"], ",") + if ok, failingConstraint := provider.MatchConstraints(constraintTags); !ok { + if failingConstraint != nil { + log.Debugf("Application %v pruned by '%v' constraint", app.ID, failingConstraint.String()) + } + return false + } + return fun.Exists(func(task marathon.Task) bool { return task.AppID == app.ID }, filteredTasks) diff --git a/provider/marathon_test.go b/provider/marathon_test.go index 94e91701e..cb4edea27 100644 --- a/provider/marathon_test.go +++ b/provider/marathon_test.go @@ -674,14 +674,114 @@ func TestMarathonTaskFilter(t *testing.T) { }, } + provider := &Marathon{} for _, c := range cases { - actual := taskFilter(c.task, c.applications, c.exposedByDefault) + actual := provider.taskFilter(c.task, c.applications, c.exposedByDefault) if actual != c.expected { t.Fatalf("expected %v, got %v", c.expected, actual) } } } +func TestMarathonAppConstraints(t *testing.T) { + cases := []struct { + application marathon.Application + filteredTasks []marathon.Task + expected bool + }{ + { + application: marathon.Application{ + ID: "foo1", + Labels: &map[string]string{}, + }, + filteredTasks: []marathon.Task{ + { + AppID: "foo1", + }, + }, + expected: false, + }, + { + application: marathon.Application{ + ID: "foo", + Labels: &map[string]string{ + "traefik.tags": "valid", + }, + }, + filteredTasks: []marathon.Task{ + { + AppID: "foo", + }, + }, + expected: true, + }, + } + + provider := &Marathon{} + constraint, _ := types.NewConstraint("tag==valid") + provider.Constraints = []types.Constraint{*constraint} + for _, c := range cases { + actual := provider.applicationFilter(c.application, c.filteredTasks) + if actual != c.expected { + t.Fatalf("expected %v, got %v: %v", c.expected, actual, c.application) + } + } + +} +func TestMarathonTaskConstraints(t *testing.T) { + cases := []struct { + applications []marathon.Application + filteredTask marathon.Task + expected bool + }{ + { + applications: []marathon.Application{ + { + ID: "bar1", + Labels: &map[string]string{}, + }, { + ID: "foo1", + Labels: &map[string]string{ + "traefik.tags": "other", + }, + }, + }, + filteredTask: marathon.Task{ + AppID: "foo1", + Ports: []int{80}, + }, + expected: false, + }, + { + applications: []marathon.Application{ + { + ID: "foo2", + Labels: &map[string]string{ + "traefik.tags": "valid", + }, + }, + }, + filteredTask: marathon.Task{ + AppID: "foo2", + Ports: []int{80}, + }, + expected: true, + }, + } + + provider := &Marathon{} + constraint, _ := types.NewConstraint("tag==valid") + provider.Constraints = []types.Constraint{*constraint} + for _, c := range cases { + apps := new(marathon.Applications) + apps.Apps = c.applications + actual := provider.taskFilter(c.filteredTask, apps, true) + if actual != c.expected { + t.Fatalf("expected %v, got %v: %v", c.expected, actual, c.filteredTask) + } + } +} + func TestMarathonApplicationFilter(t *testing.T) { cases := []struct { application marathon.Application @@ -689,7 +789,9 @@ func TestMarathonApplicationFilter(t *testing.T) { expected bool }{ { - application: marathon.Application{}, + application: marathon.Application{ + Labels: &map[string]string{}, + }, filteredTasks: []marathon.Task{}, expected: false, }, @@ -727,8 +829,9 @@ func TestMarathonApplicationFilter(t *testing.T) { }, } + provider := &Marathon{} for _, c := range cases { - actual := applicationFilter(c.application, c.filteredTasks) + actual := provider.applicationFilter(c.application, c.filteredTasks) if actual != c.expected { t.Fatalf("expected %v, got %v", c.expected, actual) }