Marathon constraints filtering

This commit is contained in:
Alex Antonov 2017-11-21 03:48:04 -06:00 committed by Traefiker
parent 7ddefcef72
commit 4b91204686
4 changed files with 65 additions and 20 deletions

View file

@ -68,6 +68,16 @@ domain = "marathon.localhost"
# #
# marathonLBCompatibility = true # marathonLBCompatibility = true
# Enable filtering using Marathon constraints..
# If enabled, Traefik will read Marathon constraints, as defined in https://mesosphere.github.io/marathon/docs/constraints.html
# Each individual constraint will be treated as a verbatim compounded tag.
# i.e. "rack_id:CLUSTER:rack-1", with all constraint groups concatenated together using ":"
#
# Optional
# Default: false
#
# filterMarathonConstraints = true
# Enable Marathon basic authentication. # Enable Marathon basic authentication.
# #
# Optional # Optional

View file

@ -44,6 +44,12 @@ func label(key, value string) func(*marathon.Application) {
} }
} }
func constraint(value string) func(*marathon.Application) {
return func(app *marathon.Application) {
app.AddConstraint(strings.Split(value, ":")...)
}
}
func labelWithService(key, value string, serviceName string) func(*marathon.Application) { func labelWithService(key, value string, serviceName string) func(*marathon.Application) {
if len(serviceName) == 0 { if len(serviceName) == 0 {
panic("serviceName can not be empty") panic("serviceName can not be empty")

View file

@ -53,20 +53,21 @@ var servicesPropertiesRegexp = regexp.MustCompile(`^traefik\.(?P<service_name>.+
// Provider holds configuration of the provider. // Provider holds configuration of the provider.
type Provider struct { type Provider struct {
provider.BaseProvider provider.BaseProvider
Endpoint string `description:"Marathon server endpoint. You can also specify multiple endpoint for Marathon" export:"true"` Endpoint string `description:"Marathon server endpoint. You can also specify multiple endpoint for Marathon" export:"true"`
Domain string `description:"Default domain used" export:"true"` Domain string `description:"Default domain used" export:"true"`
ExposedByDefault bool `description:"Expose Marathon apps by default" export:"true"` ExposedByDefault bool `description:"Expose Marathon apps by default" export:"true"`
GroupsAsSubDomains bool `description:"Convert Marathon groups to subdomains" export:"true"` GroupsAsSubDomains bool `description:"Convert Marathon groups to subdomains" export:"true"`
DCOSToken string `description:"DCOSToken for DCOS environment, This will override the Authorization header" export:"true"` DCOSToken string `description:"DCOSToken for DCOS environment, This will override the Authorization header" export:"true"`
MarathonLBCompatibility bool `description:"Add compatibility with marathon-lb labels" export:"true"` MarathonLBCompatibility bool `description:"Add compatibility with marathon-lb labels" export:"true"`
TLS *types.ClientTLS `description:"Enable Docker TLS support" export:"true"` FilterMarathonConstraints bool `description:"Enable use of Marathon constraints in constraint filtering" export:"true"`
DialerTimeout flaeg.Duration `description:"Set a non-default connection timeout for Marathon" export:"true"` TLS *types.ClientTLS `description:"Enable Docker TLS support" export:"true"`
KeepAlive flaeg.Duration `description:"Set a non-default TCP Keep Alive time in seconds" export:"true"` DialerTimeout flaeg.Duration `description:"Set a non-default connection timeout for Marathon" export:"true"`
ForceTaskHostname bool `description:"Force to use the task's hostname." export:"true"` KeepAlive flaeg.Duration `description:"Set a non-default TCP Keep Alive time in seconds" export:"true"`
Basic *Basic `description:"Enable basic authentication" export:"true"` ForceTaskHostname bool `description:"Force to use the task's hostname." export:"true"`
RespectReadinessChecks bool `description:"Filter out tasks with non-successful readiness checks during deployments" export:"true"` Basic *Basic `description:"Enable basic authentication" export:"true"`
readyChecker *readinessChecker RespectReadinessChecks bool `description:"Filter out tasks with non-successful readiness checks during deployments" export:"true"`
marathonClient marathon.Marathon readyChecker *readinessChecker
marathonClient marathon.Marathon
} }
// Basic holds basic authentication specific configurations // Basic holds basic authentication specific configurations
@ -247,6 +248,11 @@ func (p *Provider) applicationFilter(app marathon.Application) bool {
constraintTags = append(constraintTags, label) constraintTags = append(constraintTags, label)
} }
} }
if p.FilterMarathonConstraints && app.Constraints != nil {
for _, constraintParts := range *app.Constraints {
constraintTags = append(constraintTags, strings.Join(constraintParts, ":"))
}
}
if ok, failingConstraint := p.MatchConstraints(constraintTags); !ok { if ok, failingConstraint := p.MatchConstraints(constraintTags); !ok {
if failingConstraint != nil { if failingConstraint != nil {
log.Debugf("Filtering Marathon application %v pruned by '%v' constraint", app.ID, failingConstraint.String()) log.Debugf("Filtering Marathon application %v pruned by '%v' constraint", app.ID, failingConstraint.String())

View file

@ -519,10 +519,11 @@ func TestMarathonTaskFilter(t *testing.T) {
func TestMarathonApplicationFilterConstraints(t *testing.T) { func TestMarathonApplicationFilterConstraints(t *testing.T) {
cases := []struct { cases := []struct {
desc string desc string
application marathon.Application application marathon.Application
marathonLBCompatibility bool marathonLBCompatibility bool
expected bool filterMarathonConstraints bool
expected bool
}{ }{
{ {
desc: "tags missing", desc: "tags missing",
@ -536,6 +537,27 @@ func TestMarathonApplicationFilterConstraints(t *testing.T) {
marathonLBCompatibility: false, marathonLBCompatibility: false,
expected: true, expected: true,
}, },
{
desc: "constraint missing",
application: application(),
marathonLBCompatibility: false,
filterMarathonConstraints: true,
expected: false,
},
{
desc: "constraint invalid",
application: application(constraint("service_cluster:CLUSTER:test")),
marathonLBCompatibility: false,
filterMarathonConstraints: true,
expected: false,
},
{
desc: "constraint valid",
application: application(constraint("valid")),
marathonLBCompatibility: false,
filterMarathonConstraints: true,
expected: true,
},
{ {
desc: "LB compatibility tag matching", desc: "LB compatibility tag matching",
application: application( application: application(
@ -552,8 +574,9 @@ func TestMarathonApplicationFilterConstraints(t *testing.T) {
t.Run(c.desc, func(t *testing.T) { t.Run(c.desc, func(t *testing.T) {
t.Parallel() t.Parallel()
provider := &Provider{ provider := &Provider{
ExposedByDefault: true, ExposedByDefault: true,
MarathonLBCompatibility: c.marathonLBCompatibility, MarathonLBCompatibility: c.marathonLBCompatibility,
FilterMarathonConstraints: c.filterMarathonConstraints,
} }
constraint, err := types.NewConstraint("tag==valid") constraint, err := types.NewConstraint("tag==valid")
if err != nil { if err != nil {