package constraints import ( "errors" "regexp" "strings" "github.com/vulcand/predicate" ) // MarathonConstraintPrefix is the prefix for each label's key created from a Marathon application constraint. // It is used in order to create a specific and unique pattern for these labels. const MarathonConstraintPrefix = "Traefik-Marathon-505F9E15-BDC7-45E7-828D-C06C7BAB8091" type constraintLabelFunc func(map[string]string) bool // MatchLabels reports whether the expression matches with the given labels. // The expression must match any logical boolean combination of: // - `Label(labelName, labelValue)` // - `LabelRegex(labelName, regexValue)` // - `MarathonConstraint(field:operator:value)`. func MatchLabels(labels map[string]string, expr string) (bool, error) { if expr == "" { return true, nil } p, err := predicate.NewParser(predicate.Def{ Operators: predicate.Operators{ AND: andLabelFunc, NOT: notLabelFunc, OR: orLabelFunc, }, Functions: map[string]interface{}{ "Label": labelFn, "LabelRegex": labelRegexFn, "MarathonConstraint": marathonFn, }, }) if err != nil { return false, err } parse, err := p.Parse(expr) if err != nil { return false, err } fn, ok := parse.(constraintLabelFunc) if !ok { return false, errors.New("not a constraintLabelFunc") } return fn(labels), nil } func labelFn(name, value string) constraintLabelFunc { return func(labels map[string]string) bool { return labels[name] == value } } func labelRegexFn(name, expr string) constraintLabelFunc { return func(labels map[string]string) bool { matched, err := regexp.MatchString(expr, labels[name]) if err != nil { return false } return matched } } func marathonFn(value string) constraintLabelFunc { return func(labels map[string]string) bool { for k, v := range labels { if strings.HasPrefix(k, MarathonConstraintPrefix) { if v == value { return true } } } return false } } func andLabelFunc(a, b constraintLabelFunc) constraintLabelFunc { return func(labels map[string]string) bool { return a(labels) && b(labels) } } func orLabelFunc(a, b constraintLabelFunc) constraintLabelFunc { return func(labels map[string]string) bool { return a(labels) || b(labels) } } func notLabelFunc(a constraintLabelFunc) constraintLabelFunc { return func(labels map[string]string) bool { return !a(labels) } }