implement Node affinity and NodeSelector

This commit is contained in:
Kevin
2016-01-26 23:03:18 +00:00
committed by root
parent b2600a65f5
commit c8c82c1d8f
20 changed files with 33502 additions and 28298 deletions

View File

@@ -20,8 +20,10 @@ import (
"bytes"
"fmt"
"sort"
"strconv"
"strings"
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/util/sets"
"k8s.io/kubernetes/pkg/util/validation"
)
@@ -70,6 +72,8 @@ const (
NotEqualsOperator Operator = "!="
NotInOperator Operator = "notin"
ExistsOperator Operator = "exists"
GreaterThanOperator Operator = "Gt"
LessThanOperator Operator = "Lt"
)
func NewSelector() Selector {
@@ -104,7 +108,8 @@ type Requirement struct {
// (2) If the operator is In or NotIn, the values set must be non-empty.
// (3) If the operator is Equals, DoubleEquals, or NotEquals, the values set must contain one value.
// (4) If the operator is Exists or DoesNotExist, the value set must be empty.
// (5) The key is invalid due to its length, or sequence
// (5) If the operator is Gt or Lt, the values set must contain only one value.
// (6) The key is invalid due to its length, or sequence
// of characters. See validateLabelKey for more details.
//
// The empty string is a valid value in the input values set.
@@ -125,6 +130,15 @@ func NewRequirement(key string, op Operator, vals sets.String) (*Requirement, er
if len(vals) != 0 {
return nil, fmt.Errorf("values set must be empty for exists and does not exist")
}
case GreaterThanOperator, LessThanOperator:
if len(vals) != 1 {
return nil, fmt.Errorf("for 'Gt', 'Lt' operators, exactly one value is required")
}
for val := range vals {
if _, err := strconv.ParseFloat(val, 64); err != nil {
return nil, fmt.Errorf("for 'Gt', 'Lt' operators, the value must be a number")
}
}
default:
return nil, fmt.Errorf("operator '%v' is not recognized", op)
}
@@ -162,6 +176,31 @@ func (r *Requirement) Matches(ls Labels) bool {
return ls.Has(r.key)
case DoesNotExistOperator:
return !ls.Has(r.key)
case GreaterThanOperator, LessThanOperator:
if !ls.Has(r.key) {
return false
}
lsValue, err := strconv.ParseFloat(ls.Get(r.key), 64)
if err != nil {
glog.V(10).Infof("Parse float failed for value %+v in label %+v, %+v", ls.Get(r.key), ls, err)
return false
}
// There should be only one strValue in r.strValues, and can be converted to a float number.
if len(r.strValues) != 1 {
glog.V(10).Infof("Invalid values count %+v of requirement %+v, for 'Gt', 'Lt' operators, exactly one value is required", len(r.strValues), r)
return false
}
var rValue float64
for strValue := range r.strValues {
rValue, err = strconv.ParseFloat(strValue, 64)
if err != nil {
glog.V(10).Infof("Parse float failed for value %+v in requirement %+v, for 'Gt', 'Lt' operators, the value must be a number", strValue, r)
return false
}
}
return (r.operator == GreaterThanOperator && lsValue > rValue) || (r.operator == LessThanOperator && lsValue < rValue)
default:
return false
}
@@ -210,6 +249,10 @@ func (r *Requirement) String() string {
buffer.WriteString(" in ")
case NotInOperator:
buffer.WriteString(" notin ")
case GreaterThanOperator:
buffer.WriteString(">")
case LessThanOperator:
buffer.WriteString("<")
case ExistsOperator, DoesNotExistOperator:
return buffer.String()
}
@@ -277,8 +320,10 @@ const (
DoesNotExistToken
DoubleEqualsToken
EqualsToken
GreaterThanToken
IdentifierToken // to represent keys and values
InToken
LessThanToken
NotEqualsToken
NotInToken
OpenParToken
@@ -292,7 +337,9 @@ var string2token = map[string]Token{
"!": DoesNotExistToken,
"==": DoubleEqualsToken,
"=": EqualsToken,
">": GreaterThanToken,
"in": InToken,
"<": LessThanToken,
"!=": NotEqualsToken,
"notin": NotInToken,
"(": OpenParToken,
@@ -312,7 +359,7 @@ func isWhitespace(ch byte) bool {
// isSpecialSymbol detect if the character ch can be an operator
func isSpecialSymbol(ch byte) bool {
switch ch {
case '=', '!', '(', ')', ',':
case '=', '!', '(', ')', ',', '>', '<':
return true
}
return false
@@ -526,7 +573,7 @@ func (p *Parser) parseRequirement() (*Requirement, error) {
switch operator {
case InOperator, NotInOperator:
values, err = p.parseValues()
case EqualsOperator, DoubleEqualsOperator, NotEqualsOperator:
case EqualsOperator, DoubleEqualsOperator, NotEqualsOperator, GreaterThanOperator, LessThanOperator:
values, err = p.parseExactValue()
}
if err != nil {
@@ -573,6 +620,10 @@ func (p *Parser) parseOperator() (op Operator, err error) {
op = EqualsOperator
case DoubleEqualsToken:
op = DoubleEqualsOperator
case GreaterThanToken:
op = GreaterThanOperator
case LessThanToken:
op = LessThanOperator
case NotInToken:
op = NotInOperator
case NotEqualsToken:

View File

@@ -34,11 +34,14 @@ func TestSelectorParse(t *testing.T) {
"x=,z= ",
"x= ,z= ",
"!x",
"x>1.1",
"x>1.1,z<5.3",
}
testBadStrings := []string{
"x=a||y=b",
"x==a==b",
"!x=a",
"x<a",
}
for _, test := range testGoodStrings {
lq, err := Parse(test)
@@ -107,12 +110,16 @@ func TestSelectorMatches(t *testing.T) {
expectMatch(t, "notin=in", Set{"notin": "in"}) // in and notin in exactMatch
expectMatch(t, "x", Set{"x": "z"})
expectMatch(t, "!x", Set{"y": "z"})
expectMatch(t, "x>1.1", Set{"x": "1.2"})
expectMatch(t, "x<1.1", Set{"x": "0.8"})
expectNoMatch(t, "x=z", Set{})
expectNoMatch(t, "x=y", Set{"x": "z"})
expectNoMatch(t, "x=y,z=w", Set{"x": "w", "z": "w"})
expectNoMatch(t, "x!=y,z!=w", Set{"x": "z", "z": "w"})
expectNoMatch(t, "x", Set{"y": "z"})
expectNoMatch(t, "!x", Set{"x": "z"})
expectNoMatch(t, "x>1.1", Set{"x": "0.8"})
expectNoMatch(t, "x<1.1", Set{"x": "1.1"})
labelset := Set{
"foo": "bar",
@@ -184,6 +191,8 @@ func TestLexer(t *testing.T) {
{"in", InToken},
{"=", EqualsToken},
{"==", DoubleEqualsToken},
{">", GreaterThanToken},
{"<", LessThanToken},
//Note that Lex returns the longest valid token found
{"!", DoesNotExistToken},
{"!=", NotEqualsToken},
@@ -226,6 +235,8 @@ func TestLexerSequence(t *testing.T) {
{"()", []Token{OpenParToken, ClosedParToken}},
{"x in (),y", []Token{IdentifierToken, InToken, OpenParToken, ClosedParToken, CommaToken, IdentifierToken}},
{"== != (), = notin", []Token{DoubleEqualsToken, NotEqualsToken, OpenParToken, ClosedParToken, CommaToken, EqualsToken, NotInToken}},
{"key>1.1", []Token{IdentifierToken, GreaterThanToken, IdentifierToken}},
{"key<0.8", []Token{IdentifierToken, LessThanToken, IdentifierToken}},
}
for _, v := range testcases {
var literals []string
@@ -263,6 +274,8 @@ func TestParserLookahead(t *testing.T) {
{"", []Token{EndOfStringToken}},
{"x in (),y", []Token{IdentifierToken, InToken, OpenParToken, ClosedParToken, CommaToken, IdentifierToken, EndOfStringToken}},
{"== != (), = notin", []Token{DoubleEqualsToken, NotEqualsToken, OpenParToken, ClosedParToken, CommaToken, EqualsToken, NotInToken, EndOfStringToken}},
{"key>1.1", []Token{IdentifierToken, GreaterThanToken, IdentifierToken, EndOfStringToken}},
{"key<0.8", []Token{IdentifierToken, LessThanToken, IdentifierToken, EndOfStringToken}},
}
for _, v := range testcases {
p := &Parser{l: &Lexer{s: v.s, pos: 0}, position: 0}
@@ -299,6 +312,10 @@ func TestRequirementConstructor(t *testing.T) {
{"x", DoesNotExistOperator, nil, true},
{"1foo", InOperator, sets.NewString("bar"), true},
{"1234", InOperator, sets.NewString("bar"), true},
{"y", GreaterThanOperator, sets.NewString("1.1"), true},
{"z", LessThanOperator, sets.NewString("5.3"), true},
{"foo", GreaterThanOperator, sets.NewString("bar"), false},
{"barz", LessThanOperator, sets.NewString("blah"), false},
{strings.Repeat("a", 254), ExistsOperator, nil, false}, //breaks DNS rule that len(key) <= 253
}
for _, rc := range requirementConstructorTests {
@@ -343,6 +360,11 @@ func TestToString(t *testing.T) {
getRequirement("z", NotEqualsOperator, sets.NewString("a"), t),
getRequirement("z", ExistsOperator, nil, t)},
"x=abc,y==jkl,z!=a,z", true},
{&internalSelector{
getRequirement("x", GreaterThanOperator, sets.NewString("2.4"), t),
getRequirement("y", LessThanOperator, sets.NewString("7.1"), t),
getRequirement("z", ExistsOperator, nil, t)},
"x>2.4,y<7.1,z", true},
}
for _, ts := range toStringTests {
if out := ts.In.String(); out == "" && ts.Valid {
@@ -386,6 +408,12 @@ func TestRequirementSelectorMatching(t *testing.T) {
{Set{"y": "baz"}, &internalSelector{
getRequirement("x", InOperator, sets.NewString(""), t),
}, false},
{Set{"z": "1.2"}, &internalSelector{
getRequirement("z", GreaterThanOperator, sets.NewString("1.0"), t),
}, true},
{Set{"z": "v1.2"}, &internalSelector{
getRequirement("z", GreaterThanOperator, sets.NewString("1.0"), t),
}, false},
}
for _, lsm := range labelSelectorMatchingTests {
if match := lsm.Sel.Matches(lsm.Set); match != lsm.Match {
@@ -445,6 +473,12 @@ func TestSetSelectorParser(t *testing.T) {
{"x=a", internalSelector{
getRequirement("x", EqualsOperator, sets.NewString("a"), t),
}, true, true},
{"x>1.1", internalSelector{
getRequirement("x", GreaterThanOperator, sets.NewString("1.1"), t),
}, true, true},
{"x<7.1", internalSelector{
getRequirement("x", LessThanOperator, sets.NewString("7.1"), t),
}, true, true},
{"x=a,y!=b", internalSelector{
getRequirement("x", EqualsOperator, sets.NewString("a"), t),
getRequirement("y", NotEqualsOperator, sets.NewString("b"), t),