func TestAdd(t *testing.T) { testCases := []struct { sel Selector key string operator Operator values []string refSelector Selector }{ { LabelSelector{}, "key", InOperator, []string{"value"}, LabelSelector{Requirement{"key", InOperator, util.NewStringSet("value")}}, }, { LabelSelector{Requirement{"key", InOperator, util.NewStringSet("value")}}, "key2", EqualsOperator, []string{"value2"}, LabelSelector{ Requirement{"key", InOperator, util.NewStringSet("value")}, Requirement{"key2", EqualsOperator, util.NewStringSet("value2")}, }, }, } for _, ts := range testCases { ts.sel = ts.sel.Add(ts.key, ts.operator, ts.values) if !reflect.DeepEqual(ts.sel, ts.refSelector) { t.Errorf("Expected %t found %t", ts.refSelector, ts.sel) } } }
func TestToString(t *testing.T) { var req Requirement toStringTests := []struct { In *LabelSelector Out string Valid bool }{ {&LabelSelector{ getRequirement("x", InOperator, util.NewStringSet("abc", "def"), t), getRequirement("y", NotInOperator, util.NewStringSet("jkl"), t), getRequirement("z", ExistsOperator, nil, t)}, "x in (abc,def),y notin (jkl),z", true}, {&LabelSelector{ getRequirement("x", InOperator, util.NewStringSet("abc", "def"), t), req}, // adding empty req for the trailing ',' "x in (abc,def),", false}, {&LabelSelector{ getRequirement("x", NotInOperator, util.NewStringSet("abc"), t), getRequirement("y", InOperator, util.NewStringSet("jkl", "mno"), t), getRequirement("z", NotInOperator, util.NewStringSet(""), t)}, "x notin (abc),y in (jkl,mno),z notin ()", true}, {&LabelSelector{ getRequirement("x", EqualsOperator, util.NewStringSet("abc"), t), getRequirement("y", DoubleEqualsOperator, util.NewStringSet("jkl"), t), getRequirement("z", NotEqualsOperator, util.NewStringSet("a"), t)}, "x=abc,y==jkl,z!=a", true}, } for _, ts := range toStringTests { if out := ts.In.String(); out == "" && ts.Valid { t.Errorf("%+v.String() => '%v' expected no error", ts.In, out) } else if out != ts.Out { t.Errorf("%+v.String() => '%v' want '%v'", ts.In, out, ts.Out) } } }
// parseIdentifiersList parses a (possibly empty) list of // of comma separated (possibly empty) identifiers func (p *Parser) parseIdentifiersList() (util.StringSet, error) { s := util.NewStringSet() for { tok, lit := p.consume(Values) switch tok { case IdentifierToken: s.Insert(lit) tok2, lit2 := p.lookahead(Values) switch tok2 { case CommaToken: continue case ClosedParToken: return s, nil default: return nil, fmt.Errorf("found '%s', expected: ',' or ')'", lit2) } case CommaToken: // handled here since we can have "(," if s.Len() == 0 { s.Insert("") // to handle (, } tok2, _ := p.lookahead(Values) if tok2 == ClosedParToken { s.Insert("") // to handle ,) Double "" removed by StringSet return s, nil } if tok2 == CommaToken { p.consume(Values) s.Insert("") // to handle ,, Double "" removed by StringSet } default: // it can be operator return s, fmt.Errorf("found '%s', expected: ',', or identifier", lit) } } }
// parseExactValue parses the only value for exact match style func (p *Parser) parseExactValue() (util.StringSet, error) { s := util.NewStringSet() tok, lit := p.consume(Values) if tok == IdentifierToken { s.Insert(lit) return s, nil } return nil, fmt.Errorf("found '%s', expected: identifier", lit) }
// Add adds a requirement to the selector. It copies the current selector returning a new one func (lsel LabelSelector) Add(key string, operator Operator, values []string) Selector { var reqs []Requirement for _, item := range lsel { reqs = append(reqs, item) } if r, err := NewRequirement(key, operator, util.NewStringSet(values...)); err == nil { reqs = append(reqs, *r) } return LabelSelector(reqs) }
func TestRequirementLabelSelectorMatching(t *testing.T) { var req Requirement labelSelectorMatchingTests := []struct { Set Set Sel *LabelSelector Match bool }{ {Set{"x": "foo", "y": "baz"}, &LabelSelector{ req, }, false}, {Set{"x": "foo", "y": "baz"}, &LabelSelector{ getRequirement("x", InOperator, util.NewStringSet("foo"), t), getRequirement("y", NotInOperator, util.NewStringSet("alpha"), t), }, true}, {Set{"x": "foo", "y": "baz"}, &LabelSelector{ getRequirement("x", InOperator, util.NewStringSet("foo"), t), getRequirement("y", InOperator, util.NewStringSet("alpha"), t), }, false}, {Set{"y": ""}, &LabelSelector{ getRequirement("x", NotInOperator, util.NewStringSet(""), t), getRequirement("y", ExistsOperator, nil, t), }, true}, {Set{"y": "baz"}, &LabelSelector{ getRequirement("x", InOperator, util.NewStringSet(""), t), }, false}, } for _, lsm := range labelSelectorMatchingTests { if match := lsm.Sel.Matches(lsm.Set); match != lsm.Match { t.Errorf("%+v.Matches(%#v) => %v, want %v", lsm.Sel, lsm.Set, match, lsm.Match) } } }
func TestRequirementConstructor(t *testing.T) { requirementConstructorTests := []struct { Key string Op Operator Vals util.StringSet Success bool }{ {"x", InOperator, nil, false}, {"x", NotInOperator, util.NewStringSet(), false}, {"x", InOperator, util.NewStringSet("foo"), true}, {"x", NotInOperator, util.NewStringSet("foo"), true}, {"x", ExistsOperator, nil, true}, {"1foo", InOperator, util.NewStringSet("bar"), true}, {"1234", InOperator, util.NewStringSet("bar"), true}, {strings.Repeat("a", 254), ExistsOperator, nil, false}, //breaks DNS rule that len(key) <= 253 } for _, rc := range requirementConstructorTests { if _, err := NewRequirement(rc.Key, rc.Op, rc.Vals); err == nil && !rc.Success { t.Errorf("expected error with key:%#v op:%v vals:%v, got no error", rc.Key, rc.Op, rc.Vals) } else if err != nil && rc.Success { t.Errorf("expected no error with key:%#v op:%v vals:%v, got:%v", rc.Key, rc.Op, rc.Vals, err) } } }
// SelectorFromSet returns a Selector which will match exactly the given Set. A // nil and empty Sets are considered equivalent to Everything(). func SelectorFromSet(ls Set) Selector { if ls == nil { return LabelSelector{} } var requirements []Requirement for label, value := range ls { if r, err := NewRequirement(label, EqualsOperator, util.NewStringSet(value)); err != nil { //TODO: double check errors when input comes from serialization? return LabelSelector{} } else { requirements = append(requirements, *r) } } return LabelSelector(requirements) }
// parseValues parses the values for set based matching (x,y,z) func (p *Parser) parseValues() (util.StringSet, error) { tok, lit := p.consume(Values) if tok != OpenParToken { return nil, fmt.Errorf("found '%s' expected: '('", lit) } tok, lit = p.lookahead(Values) switch tok { case IdentifierToken, CommaToken: s, err := p.parseIdentifiersList() // handles general cases if err != nil { return s, err } if tok, _ = p.consume(Values); tok != ClosedParToken { return nil, fmt.Errorf("found '%s', expected: ')'", lit) } return s, nil case ClosedParToken: // handles "()" p.consume(Values) return util.NewStringSet(""), nil default: return nil, fmt.Errorf("found '%s', expected: ',', ')' or identifier", lit) } return util.NewStringSet(), nil }
func TestSetSelectorParser(t *testing.T) { setSelectorParserTests := []struct { In string Out Selector Match bool Valid bool }{ {"", LabelSelector(nil), true, true}, {"\rx", LabelSelector{ getRequirement("x", ExistsOperator, nil, t), }, true, true}, {"this-is-a-dns.domain.com/key-with-dash", LabelSelector{ getRequirement("this-is-a-dns.domain.com/key-with-dash", ExistsOperator, nil, t), }, true, true}, {"this-is-another-dns.domain.com/key-with-dash in (so,what)", LabelSelector{ getRequirement("this-is-another-dns.domain.com/key-with-dash", InOperator, util.NewStringSet("so", "what"), t), }, true, true}, {"0.1.2.domain/99 notin (10.10.100.1, tick.tack.clock)", LabelSelector{ getRequirement("0.1.2.domain/99", NotInOperator, util.NewStringSet("10.10.100.1", "tick.tack.clock"), t), }, true, true}, {"foo in (abc)", LabelSelector{ getRequirement("foo", InOperator, util.NewStringSet("abc"), t), }, true, true}, {"x notin\n (abc)", LabelSelector{ getRequirement("x", NotInOperator, util.NewStringSet("abc"), t), }, true, true}, {"x notin \t (abc,def)", LabelSelector{ getRequirement("x", NotInOperator, util.NewStringSet("abc", "def"), t), }, true, true}, {"x in (abc,def)", LabelSelector{ getRequirement("x", InOperator, util.NewStringSet("abc", "def"), t), }, true, true}, {"x in (abc,)", LabelSelector{ getRequirement("x", InOperator, util.NewStringSet("abc", ""), t), }, true, true}, {"x in ()", LabelSelector{ getRequirement("x", InOperator, util.NewStringSet(""), t), }, true, true}, {"x notin (abc,,def),bar,z in (),w", LabelSelector{ getRequirement("bar", ExistsOperator, nil, t), getRequirement("w", ExistsOperator, nil, t), getRequirement("x", NotInOperator, util.NewStringSet("abc", "", "def"), t), getRequirement("z", InOperator, util.NewStringSet(""), t), }, true, true}, {"x,y in (a)", LabelSelector{ getRequirement("y", InOperator, util.NewStringSet("a"), t), getRequirement("x", ExistsOperator, nil, t), }, false, true}, {"x=a", LabelSelector{ getRequirement("x", EqualsOperator, util.NewStringSet("a"), t), }, true, true}, {"x=a,y!=b", LabelSelector{ getRequirement("x", EqualsOperator, util.NewStringSet("a"), t), getRequirement("y", NotEqualsOperator, util.NewStringSet("b"), t), }, true, true}, {"x=a,y!=b,z in (h,i,j)", LabelSelector{ getRequirement("x", EqualsOperator, util.NewStringSet("a"), t), getRequirement("y", NotEqualsOperator, util.NewStringSet("b"), t), getRequirement("z", InOperator, util.NewStringSet("h", "i", "j"), t), }, true, true}, {"x=a||y=b", LabelSelector{}, false, false}, {"x,,y", nil, true, false}, {",x,y", nil, true, false}, {"x nott in (y)", nil, true, false}, {"x notin ( )", LabelSelector{ getRequirement("x", NotInOperator, util.NewStringSet(""), t), }, true, true}, {"x notin (, a)", LabelSelector{ getRequirement("x", NotInOperator, util.NewStringSet("", "a"), t), }, true, true}, {"a in (xyz),", nil, true, false}, {"a in (xyz)b notin ()", nil, true, false}, {"a ", LabelSelector{ getRequirement("a", ExistsOperator, nil, t), }, true, true}, {"a in (x,y,notin, z,in)", LabelSelector{ getRequirement("a", InOperator, util.NewStringSet("in", "notin", "x", "y", "z"), t), }, true, true}, // operator 'in' inside list of identifiers {"a in (xyz abc)", nil, false, false}, // no comma {"a notin(", nil, true, false}, // bad formed {"a (", nil, false, false}, // cpar {"(", nil, false, false}, // opar } for _, ssp := range setSelectorParserTests { if sel, err := Parse(ssp.In); err != nil && ssp.Valid { t.Errorf("Parse(%s) => %v expected no error", ssp.In, err) } else if err == nil && !ssp.Valid { t.Errorf("Parse(%s) => %+v expected error", ssp.In, sel) } else if ssp.Match && !reflect.DeepEqual(sel, ssp.Out) { t.Errorf("Parse(%s) => parse output '%t' doesn't match '%t' expected match", ssp.In, sel, ssp.Out) } } }