test(constraint): unit tests + integration tests + make validate

This commit is contained in:
Samuel BERTHE 2016-05-20 17:17:38 +02:00
parent cd2100ed84
commit f46accc74d
7 changed files with 362 additions and 40 deletions

View file

@ -7,6 +7,7 @@ import (
"text/template"
"time"
"github.com/BurntSushi/ty/fun"
log "github.com/Sirupsen/logrus"
"github.com/cenkalti/backoff"
"github.com/containous/traefik/safe"
@ -88,28 +89,22 @@ func (provider *ConsulCatalog) healthyNodes(service string) (catalogUpdate, erro
return catalogUpdate{}, err
}
set := map[string]bool{}
tags := []string{}
nodes := []*api.ServiceEntry{}
for _, node := range data {
nodes := fun.Filter(func(node *api.ServiceEntry) bool {
constraintTags := provider.getContraintTags(node.Service.Tags)
if ok, failingConstraint, err := provider.MatchConstraints(constraintTags); err != nil {
return catalogUpdate{}, err
} else if ok == true {
nodes = append(nodes, node)
// merge tags of every nodes in a single slice
// only if node match constraint
for _, tag := range node.Service.Tags {
if _, ok := set[tag]; ok == false {
set[tag] = true
tags = append(tags, tag)
}
}
} else if ok == false && failingConstraint != nil {
ok, failingConstraint := provider.MatchConstraints(constraintTags)
if ok == false && failingConstraint != nil {
log.Debugf("Service %v pruned by '%v' constraint", service, failingConstraint.String())
}
return ok
}, data).([]*api.ServiceEntry)
}
//Merge tags of nodes matching constraints, in a single slice.
tags := fun.Foldl(func(node *api.ServiceEntry, set []string) []string {
return fun.Keys(fun.Union(
fun.Set(set),
fun.Set(node.Service.Tags),
).(map[string]bool)).([]string)
}, []string{}, nodes).([]string)
return catalogUpdate{
Service: &serviceUpdate{

View file

@ -29,22 +29,21 @@ type BaseProvider struct {
// MatchConstraints must match with EVERY single contraint
// returns first constraint that do not match or nil
// returns errors for future use (regex)
func (p *BaseProvider) MatchConstraints(tags []string) (bool, *types.Constraint, error) {
func (p *BaseProvider) MatchConstraints(tags []string) (bool, *types.Constraint) {
// if there is no tags and no contraints, filtering is disabled
if len(tags) == 0 && len(p.Constraints) == 0 {
return true, nil, nil
return true, nil
}
for _, constraint := range p.Constraints {
if ok := constraint.MatchConstraintWithAtLeastOneTag(tags); xor(ok == true, constraint.MustMatch == true) {
return false, constraint, nil
// xor: if ok and constraint.MustMatch are equal, then no tag is currently matching with the constraint
if ok := constraint.MatchConstraintWithAtLeastOneTag(tags); ok != constraint.MustMatch {
return false, constraint
}
}
// If no constraint or every constraints matching
return true, nil, nil
>>>>>>> e844462... feat(constraints): Implementation of constraints (cmd + toml + matching functions), implementation proposal with consul
return true, nil
}
func (p *BaseProvider) getConfiguration(defaultTemplateFile string, funcMap template.FuncMap, templateObjects interface{}) (*types.Configuration, error) {
@ -98,8 +97,3 @@ func normalize(name string) string {
// get function
return strings.Join(strings.FieldsFunc(name, fargs), "-")
}
// golang does not support ^ operator
func xor(cond1 bool, cond2 bool) bool {
return cond1 != cond2
}

View file

@ -6,6 +6,8 @@ import (
"strings"
"testing"
"text/template"
"github.com/containous/traefik/types"
)
type myProvider struct {
@ -206,3 +208,100 @@ func TestGetConfigurationReturnsCorrectMaxConnConfiguration(t *testing.T) {
t.Fatalf("Configuration did not parse MaxConn.ExtractorFunc properly")
}
}
func TestMatchingConstraints(t *testing.T) {
cases := []struct {
constraints []*types.Constraint
tags []string
expected bool
}{
// simple test: must match
{
constraints: []*types.Constraint{
{
Key: "tag",
MustMatch: true,
Regex: "us-east-1",
},
},
tags: []string{
"us-east-1",
},
expected: true,
},
// simple test: must match but does not match
{
constraints: []*types.Constraint{
{
Key: "tag",
MustMatch: true,
Regex: "us-east-1",
},
},
tags: []string{
"us-east-2",
},
expected: false,
},
// simple test: must not match
{
constraints: []*types.Constraint{
{
Key: "tag",
MustMatch: false,
Regex: "us-east-1",
},
},
tags: []string{
"us-east-1",
},
expected: false,
},
// complex test: globbing
{
constraints: []*types.Constraint{
{
Key: "tag",
MustMatch: true,
Regex: "us-east-*",
},
},
tags: []string{
"us-east-1",
},
expected: true,
},
// complex test: multiple constraints
{
constraints: []*types.Constraint{
{
Key: "tag",
MustMatch: true,
Regex: "us-east-*",
},
{
Key: "tag",
MustMatch: false,
Regex: "api",
},
},
tags: []string{
"api",
"us-east-1",
},
expected: false,
},
}
for i, c := range cases {
provider := myProvider{
BaseProvider{
Constraints: c.constraints,
},
}
actual, _ := provider.MatchConstraints(c.tags)
if actual != c.expected {
t.Fatalf("test #%v: expected %q, got %q, for %q", i, c.expected, actual, c.constraints)
}
}
}