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
# Enable compatibility with marathon-lb labels
#
# Optional
# Default: false
#
# marathonLBCompatibility = true
# Enable Marathon basic authentication
#
# Optional

View file

@ -26,14 +26,15 @@ var _ Provider = (*Marathon)(nil)
// Marathon holds configuration of the Marathon provider.
type Marathon struct {
BaseProvider
Endpoint string `description:"Marathon server endpoint. You can also specify multiple endpoint for Marathon"`
Domain string `description:"Default domain used"`
ExposedByDefault bool `description:"Expose Marathon apps by default"`
GroupsAsSubDomains bool `description:"Convert Marathon groups to subdomains"`
DCOSToken string `description:"DCOSToken for DCOS environment, This will override the Authorization header"`
TLS *ClientTLS `description:"Enable Docker TLS support"`
Basic *MarathonBasic
marathonClient marathon.Marathon
Endpoint string `description:"Marathon server endpoint. You can also specify multiple endpoint for Marathon"`
Domain string `description:"Default domain used"`
ExposedByDefault bool `description:"Expose Marathon apps by default"`
GroupsAsSubDomains bool `description:"Convert Marathon groups to subdomains"`
DCOSToken string `description:"DCOSToken for DCOS environment, This will override the Authorization header"`
MarathonLBCompatibility bool `description:"Add compatibility with marathon-lb labels"`
TLS *ClientTLS `description:"Enable Docker TLS support"`
Basic *MarathonBasic
marathonClient marathon.Marathon
}
// 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")
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 failingConstraint != nil {
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 {
label, _ := provider.getLabel(app, "traefik.tags")
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 failingConstraint != nil {
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 {
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
}

View file

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