Add basic compatibility with marathon-lb

Add compatibility with labels: `HAPROXY_GROUP` and `HAPROXY_0_VHOST`.
* `HAPROXY_GROUP` become a new tag
* `HAPROXY_0_VHOST` become a rule `Host:`

https://github.com/mesosphere/marathon-lb
This commit is contained in:
Guilhem Lettron 2016-10-06 17:42:19 +02:00
parent b1b600e09e
commit 3f65503a79
3 changed files with 125 additions and 37 deletions

View file

@ -783,6 +783,13 @@ domain = "marathon.localhost"
# #
# groupsAsSubDomains = true # groupsAsSubDomains = true
# Enable compatibility with marathon-lb labels
#
# Optional
# Default: false
#
# marathonLBCompatibility = true
# Enable Marathon basic authentication # Enable Marathon basic authentication
# #
# Optional # Optional

View file

@ -26,14 +26,15 @@ var _ Provider = (*Marathon)(nil)
// Marathon holds configuration of the Marathon provider. // Marathon holds configuration of the Marathon provider.
type Marathon struct { type Marathon struct {
BaseProvider BaseProvider
Endpoint string `description:"Marathon server endpoint. You can also specify multiple endpoint for Marathon"` Endpoint string `description:"Marathon server endpoint. You can also specify multiple endpoint for Marathon"`
Domain string `description:"Default domain used"` Domain string `description:"Default domain used"`
ExposedByDefault bool `description:"Expose Marathon apps by default"` ExposedByDefault bool `description:"Expose Marathon apps by default"`
GroupsAsSubDomains bool `description:"Convert Marathon groups to subdomains"` GroupsAsSubDomains bool `description:"Convert Marathon groups to subdomains"`
DCOSToken string `description:"DCOSToken for DCOS environment, This will override the Authorization header"` DCOSToken string `description:"DCOSToken for DCOS environment, This will override the Authorization header"`
TLS *ClientTLS `description:"Enable Docker TLS support"` MarathonLBCompatibility bool `description:"Add compatibility with marathon-lb labels"`
Basic *MarathonBasic TLS *ClientTLS `description:"Enable Docker TLS support"`
marathonClient marathon.Marathon Basic *MarathonBasic
marathonClient marathon.Marathon
} }
// MarathonBasic holds basic authentication specific configurations // MarathonBasic holds basic authentication specific configurations
@ -194,6 +195,11 @@ func (provider *Marathon) taskFilter(task marathon.Task, applications *marathon.
} }
label, _ := provider.getLabel(application, "traefik.tags") label, _ := provider.getLabel(application, "traefik.tags")
constraintTags := strings.Split(label, ",") constraintTags := strings.Split(label, ",")
if provider.MarathonLBCompatibility {
if label, err := provider.getLabel(application, "HAPROXY_GROUP"); err == nil {
constraintTags = append(constraintTags, label)
}
}
if ok, failingConstraint := provider.MatchConstraints(constraintTags); !ok { if ok, failingConstraint := provider.MatchConstraints(constraintTags); !ok {
if failingConstraint != nil { if failingConstraint != nil {
log.Debugf("Application %v pruned by '%v' constraint", application.ID, failingConstraint.String()) log.Debugf("Application %v pruned by '%v' constraint", application.ID, failingConstraint.String())
@ -263,6 +269,11 @@ func (provider *Marathon) taskFilter(task marathon.Task, applications *marathon.
func (provider *Marathon) applicationFilter(app marathon.Application, filteredTasks []marathon.Task) bool { func (provider *Marathon) applicationFilter(app marathon.Application, filteredTasks []marathon.Task) bool {
label, _ := provider.getLabel(app, "traefik.tags") label, _ := provider.getLabel(app, "traefik.tags")
constraintTags := strings.Split(label, ",") constraintTags := strings.Split(label, ",")
if provider.MarathonLBCompatibility {
if label, err := provider.getLabel(app, "HAPROXY_GROUP"); err == nil {
constraintTags = append(constraintTags, label)
}
}
if ok, failingConstraint := provider.MatchConstraints(constraintTags); !ok { if ok, failingConstraint := provider.MatchConstraints(constraintTags); !ok {
if failingConstraint != nil { if failingConstraint != nil {
log.Debugf("Application %v pruned by '%v' constraint", app.ID, failingConstraint.String()) log.Debugf("Application %v pruned by '%v' constraint", app.ID, failingConstraint.String())
@ -384,6 +395,11 @@ func (provider *Marathon) getFrontendRule(application marathon.Application) stri
if label, err := provider.getLabel(application, "traefik.frontend.rule"); err == nil { if label, err := provider.getLabel(application, "traefik.frontend.rule"); err == nil {
return label return label
} }
if provider.MarathonLBCompatibility {
if label, err := provider.getLabel(application, "HAPROXY_0_VHOST"); err == nil {
return "Host:" + label
}
}
return "Host:" + provider.getSubDomain(application.ID) + "." + provider.Domain return "Host:" + provider.getSubDomain(application.ID) + "." + provider.Domain
} }

View file

@ -684,9 +684,10 @@ func TestMarathonTaskFilter(t *testing.T) {
func TestMarathonAppConstraints(t *testing.T) { func TestMarathonAppConstraints(t *testing.T) {
cases := []struct { cases := []struct {
application marathon.Application application marathon.Application
filteredTasks []marathon.Task filteredTasks []marathon.Task
expected bool expected bool
marathonLBCompatibility bool
}{ }{
{ {
application: marathon.Application{ application: marathon.Application{
@ -698,28 +699,48 @@ func TestMarathonAppConstraints(t *testing.T) {
AppID: "foo1", AppID: "foo1",
}, },
}, },
expected: false, marathonLBCompatibility: false,
expected: false,
}, },
{ {
application: marathon.Application{ application: marathon.Application{
ID: "foo", ID: "foo2",
Labels: &map[string]string{ Labels: &map[string]string{
"traefik.tags": "valid", "traefik.tags": "valid",
}, },
}, },
filteredTasks: []marathon.Task{ filteredTasks: []marathon.Task{
{ {
AppID: "foo", AppID: "foo2",
}, },
}, },
expected: true, marathonLBCompatibility: false,
expected: true,
},
{
application: marathon.Application{
ID: "foo3",
Labels: &map[string]string{
"HAPROXY_GROUP": "valid",
"traefik.tags": "notvalid",
},
},
filteredTasks: []marathon.Task{
{
AppID: "foo3",
},
},
marathonLBCompatibility: true,
expected: true,
}, },
} }
provider := &Marathon{}
constraint, _ := types.NewConstraint("tag==valid")
provider.Constraints = []types.Constraint{*constraint}
for _, c := range cases { for _, c := range cases {
provider := &Marathon{
MarathonLBCompatibility: c.marathonLBCompatibility,
}
constraint, _ := types.NewConstraint("tag==valid")
provider.Constraints = []types.Constraint{*constraint}
actual := provider.applicationFilter(c.application, c.filteredTasks) actual := provider.applicationFilter(c.application, c.filteredTasks)
if actual != c.expected { if actual != c.expected {
t.Fatalf("expected %v, got %v: %v", c.expected, actual, c.application) t.Fatalf("expected %v, got %v: %v", c.expected, actual, c.application)
@ -729,9 +750,10 @@ func TestMarathonAppConstraints(t *testing.T) {
} }
func TestMarathonTaskConstraints(t *testing.T) { func TestMarathonTaskConstraints(t *testing.T) {
cases := []struct { cases := []struct {
applications []marathon.Application applications []marathon.Application
filteredTask marathon.Task filteredTask marathon.Task
expected bool expected bool
marathonLBCompatibility bool
}{ }{
{ {
applications: []marathon.Application{ applications: []marathon.Application{
@ -749,7 +771,8 @@ func TestMarathonTaskConstraints(t *testing.T) {
AppID: "foo1", AppID: "foo1",
Ports: []int{80}, Ports: []int{80},
}, },
expected: false, marathonLBCompatibility: false,
expected: false,
}, },
{ {
applications: []marathon.Application{ applications: []marathon.Application{
@ -764,14 +787,40 @@ func TestMarathonTaskConstraints(t *testing.T) {
AppID: "foo2", AppID: "foo2",
Ports: []int{80}, Ports: []int{80},
}, },
expected: true, marathonLBCompatibility: false,
expected: true,
},
{
applications: []marathon.Application{
{
ID: "foo3",
Labels: &map[string]string{
"HAPROXY_GROUP": "valid",
"traefik.tags": "notvalid",
},
}, {
ID: "foo4",
Labels: &map[string]string{
"HAPROXY_GROUP": "notvalid",
"traefik.tags": "valid",
},
},
},
filteredTask: marathon.Task{
AppID: "foo3",
Ports: []int{80},
},
marathonLBCompatibility: true,
expected: true,
}, },
} }
provider := &Marathon{}
constraint, _ := types.NewConstraint("tag==valid")
provider.Constraints = []types.Constraint{*constraint}
for _, c := range cases { for _, c := range cases {
provider := &Marathon{
MarathonLBCompatibility: c.marathonLBCompatibility,
}
constraint, _ := types.NewConstraint("tag==valid")
provider.Constraints = []types.Constraint{*constraint}
apps := new(marathon.Applications) apps := new(marathon.Applications)
apps.Apps = c.applications apps.Apps = c.applications
actual := provider.taskFilter(c.filteredTask, apps, true) actual := provider.taskFilter(c.filteredTask, apps, true)
@ -1152,37 +1201,53 @@ func TestMarathonGetEntryPoints(t *testing.T) {
} }
func TestMarathonGetFrontendRule(t *testing.T) { func TestMarathonGetFrontendRule(t *testing.T) {
provider := &Marathon{
Domain: "docker.localhost",
}
applications := []struct { applications := []struct {
application marathon.Application application marathon.Application
expected string expected string
marathonLBCompatibility bool
}{ }{
{ {
application: marathon.Application{ application: marathon.Application{
Labels: &map[string]string{}}, Labels: &map[string]string{}},
expected: "Host:.docker.localhost", marathonLBCompatibility: true,
expected: "Host:.docker.localhost",
}, },
{ {
application: marathon.Application{ application: marathon.Application{
ID: "test", ID: "test",
Labels: &map[string]string{}, Labels: &map[string]string{
"HAPROXY_0_VHOST": "foo.bar",
},
}, },
expected: "Host:test.docker.localhost", marathonLBCompatibility: false,
expected: "Host:test.docker.localhost",
}, },
{ {
application: marathon.Application{ application: marathon.Application{
Labels: &map[string]string{ Labels: &map[string]string{
"traefik.frontend.rule": "Host:foo.bar", "traefik.frontend.rule": "Host:foo.bar",
"HAPROXY_0_VHOST": "notvalid",
}, },
}, },
expected: "Host:foo.bar", marathonLBCompatibility: true,
expected: "Host:foo.bar",
},
{
application: marathon.Application{
Labels: &map[string]string{
"HAPROXY_0_VHOST": "foo.bar",
},
},
marathonLBCompatibility: true,
expected: "Host:foo.bar",
}, },
} }
for _, a := range applications { for _, a := range applications {
provider := &Marathon{
Domain: "docker.localhost",
MarathonLBCompatibility: a.marathonLBCompatibility,
}
actual := provider.getFrontendRule(a.application) actual := provider.getFrontendRule(a.application)
if actual != a.expected { if actual != a.expected {
t.Fatalf("expected %q, got %q", a.expected, actual) t.Fatalf("expected %q, got %q", a.expected, actual)