Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
K
k3s
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
k3s
Commits
8959e0cc
Commit
8959e0cc
authored
Oct 13, 2015
by
k8s-merge-robot
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #15509 from bgrant0607/labels
Auto commit by PR queue bot
parents
3e3833b0
1bac67f9
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
68 additions
and
22 deletions
+68
-22
labels.md
docs/user-guide/labels.md
+0
-0
selector.go
pkg/labels/selector.go
+39
-19
selector_test.go
pkg/labels/selector_test.go
+29
-3
No files found.
docs/user-guide/labels.md
View file @
8959e0cc
This diff is collapsed.
Click to expand it.
pkg/labels/selector.go
View file @
8959e0cc
...
...
@@ -52,6 +52,7 @@ func Everything() Selector {
type
Operator
string
const
(
DoesNotExistOperator
Operator
=
"!"
EqualsOperator
Operator
=
"="
DoubleEqualsOperator
Operator
=
"=="
InOperator
Operator
=
"in"
...
...
@@ -85,10 +86,11 @@ type Requirement struct {
// NewRequirement is the constructor for a Requirement.
// If any of these rules is violated, an error is returned:
// (1) The operator can only be In, NotIn or Exists.
// (2) If the operator is In or NotIn, the values set must
// be non-empty.
// (3) The key is invalid due to its length, or sequence
// (1) The operator can only be In, NotIn, Equals, DoubleEquals, NotEquals, Exists, or DoesNotExist.
// (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
// of characters. See validateLabelKey for more details.
//
// The empty string is a valid value in the input values set.
...
...
@@ -103,9 +105,12 @@ func NewRequirement(key string, op Operator, vals sets.String) (*Requirement, er
}
case
EqualsOperator
,
DoubleEqualsOperator
,
NotEqualsOperator
:
if
len
(
vals
)
!=
1
{
return
nil
,
fmt
.
Errorf
(
"exact match compatibility requires one single value"
)
return
nil
,
fmt
.
Errorf
(
"exact-match compatibility requires one single value"
)
}
case
ExistsOperator
,
DoesNotExistOperator
:
if
len
(
vals
)
!=
0
{
return
nil
,
fmt
.
Errorf
(
"values set must be empty for exists and does not exist"
)
}
case
ExistsOperator
:
default
:
return
nil
,
fmt
.
Errorf
(
"operator '%v' is not recognized"
,
op
)
}
...
...
@@ -125,7 +130,7 @@ func NewRequirement(key string, op Operator, vals sets.String) (*Requirement, er
// value for that key is in Requirement's value set.
// (3) The operator is NotIn, Labels has the Requirement's key and
// Labels' value for that key is not in Requirement's value set.
// (4) The operator is NotIn and Labels does not have the
// (4) The operator is
DoesNotExist or
NotIn and Labels does not have the
// Requirement's key.
func
(
r
*
Requirement
)
Matches
(
ls
Labels
)
bool
{
switch
r
.
operator
{
...
...
@@ -141,6 +146,8 @@ func (r *Requirement) Matches(ls Labels) bool {
return
!
r
.
strValues
.
Has
(
ls
.
Get
(
r
.
key
))
case
ExistsOperator
:
return
ls
.
Has
(
r
.
key
)
case
DoesNotExistOperator
:
return
!
ls
.
Has
(
r
.
key
)
default
:
return
false
}
...
...
@@ -173,6 +180,9 @@ func (lsel LabelSelector) Empty() bool {
// returned. See NewRequirement for creating a valid Requirement.
func
(
r
*
Requirement
)
String
()
string
{
var
buffer
bytes
.
Buffer
if
r
.
operator
==
DoesNotExistOperator
{
buffer
.
WriteString
(
"!"
)
}
buffer
.
WriteString
(
r
.
key
)
switch
r
.
operator
{
...
...
@@ -186,7 +196,7 @@ func (r *Requirement) String() string {
buffer
.
WriteString
(
" in "
)
case
NotInOperator
:
buffer
.
WriteString
(
" notin "
)
case
ExistsOperator
:
case
ExistsOperator
,
DoesNotExistOperator
:
return
buffer
.
String
()
}
...
...
@@ -249,6 +259,7 @@ const (
EndOfStringToken
ClosedParToken
CommaToken
DoesNotExistToken
DoubleEqualsToken
EqualsToken
IdentifierToken
// to represent keys and values
...
...
@@ -263,6 +274,7 @@ const (
var
string2token
=
map
[
string
]
Token
{
")"
:
ClosedParToken
,
","
:
CommaToken
,
"!"
:
DoesNotExistToken
,
"=="
:
DoubleEqualsToken
,
"="
:
EqualsToken
,
"in"
:
InToken
,
...
...
@@ -457,7 +469,7 @@ func (p *Parser) parse() ([]Requirement, error) {
for
{
tok
,
lit
:=
p
.
lookahead
(
Values
)
switch
tok
{
case
IdentifierToken
:
case
IdentifierToken
,
DoesNotExistToken
:
r
,
err
:=
p
.
parseRequirement
()
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"unable to parse requirement: %v"
,
err
)
...
...
@@ -469,7 +481,7 @@ func (p *Parser) parse() ([]Requirement, error) {
return
requirements
,
nil
case
CommaToken
:
t2
,
l2
:=
p
.
lookahead
(
Values
)
if
t2
!=
IdentifierToken
{
if
t2
!=
IdentifierToken
&&
t2
!=
DoesNotExistToken
{
return
nil
,
fmt
.
Errorf
(
"found '%s', expected: identifier after ','"
,
l2
)
}
default
:
...
...
@@ -478,7 +490,7 @@ func (p *Parser) parse() ([]Requirement, error) {
case
EndOfStringToken
:
return
requirements
,
nil
default
:
return
nil
,
fmt
.
Errorf
(
"found '%s', expected:
identifier
or 'end of string'"
,
lit
)
return
nil
,
fmt
.
Errorf
(
"found '%s', expected:
!, identifier,
or 'end of string'"
,
lit
)
}
}
}
...
...
@@ -488,7 +500,7 @@ func (p *Parser) parseRequirement() (*Requirement, error) {
if
err
!=
nil
{
return
nil
,
err
}
if
operator
==
ExistsOperator
{
// operator Exists
found lookahead set checked
if
operator
==
ExistsOperator
||
operator
==
DoesNotExistOperator
{
// operator
found lookahead set checked
return
NewRequirement
(
key
,
operator
,
nil
)
}
operator
,
err
=
p
.
parseOperator
()
...
...
@@ -510,10 +522,15 @@ func (p *Parser) parseRequirement() (*Requirement, error) {
}
// parseKeyAndInferOperator parse literals.
// in case of no operator 'in, notin, ==, =, !=' are found
// the 'exists' operat
t
or is inferred
// in case of no operator '
!,
in, notin, ==, =, !=' are found
// the 'exists' operator is inferred
func
(
p
*
Parser
)
parseKeyAndInferOperator
()
(
string
,
Operator
,
error
)
{
var
operator
Operator
tok
,
literal
:=
p
.
consume
(
Values
)
if
tok
==
DoesNotExistToken
{
operator
=
DoesNotExistOperator
tok
,
literal
=
p
.
consume
(
Values
)
}
if
tok
!=
IdentifierToken
{
err
:=
fmt
.
Errorf
(
"found '%s', expected: identifier"
,
literal
)
return
""
,
""
,
err
...
...
@@ -521,9 +538,10 @@ func (p *Parser) parseKeyAndInferOperator() (string, Operator, error) {
if
err
:=
validateLabelKey
(
literal
);
err
!=
nil
{
return
""
,
""
,
err
}
var
operator
Operator
if
t
,
_
:=
p
.
lookahead
(
Values
);
t
==
EndOfStringToken
||
t
==
CommaToken
{
operator
=
ExistsOperator
if
operator
!=
DoesNotExistOperator
{
operator
=
ExistsOperator
}
}
return
literal
,
operator
,
nil
}
...
...
@@ -533,6 +551,7 @@ func (p *Parser) parseKeyAndInferOperator() (string, Operator, error) {
func
(
p
*
Parser
)
parseOperator
()
(
op
Operator
,
err
error
)
{
tok
,
lit
:=
p
.
consume
(
KeyAndOperator
)
switch
tok
{
// DoesNotExistToken shouldn't be here because it's a unary operator, not a binary operator
case
InToken
:
op
=
InOperator
case
EqualsToken
:
...
...
@@ -633,7 +652,7 @@ func (p *Parser) parseExactValue() (sets.String, error) {
// The input will cause an error if it does not follow this form:
//
// <selector-syntax> ::= <requirement> | <requirement> "," <selector-syntax> ]
// <requirement> ::=
KEY [ <set-based-restriction> | <exact-match-restriction>
// <requirement> ::=
[!] KEY [ <set-based-restriction> | <exact-match-restriction> ]
// <set-based-restriction> ::= "" | <inclusion-exclusion> <value-set>
// <inclusion-exclusion> ::= <inclusion> | <exclusion>
// <exclusion> ::= "notin"
...
...
@@ -648,13 +667,14 @@ func (p *Parser) parseExactValue() (sets.String, error) {
// "x in (foo,,baz),y,z notin ()"
//
// Note:
// (1) Inclusion - " in " - denotes that the KEY is equal to any of the
// (1) Inclusion - " in " - denotes that the KEY
exists and
is equal to any of the
// VALUEs in its requirement
// (2) Exclusion - " notin " - denotes that the KEY is not equal to any
// of the VALUEs in its requirement
// of the VALUEs in its requirement
or does not exist
// (3) The empty string is a valid VALUE
// (4) A requirement with just a KEY - as in "y" above - denotes that
// the KEY exists and can be any VALUE.
// (5) A requirement with just !KEY requires that the KEY not exist.
//
func
Parse
(
selector
string
)
(
Selector
,
error
)
{
p
:=
&
Parser
{
l
:
&
Lexer
{
s
:
selector
,
pos
:
0
}}
...
...
pkg/labels/selector_test.go
View file @
8959e0cc
...
...
@@ -33,10 +33,12 @@ func TestSelectorParse(t *testing.T) {
"x= "
,
"x=,z= "
,
"x= ,z= "
,
"!x"
,
}
testBadStrings
:=
[]
string
{
"x=a||y=b"
,
"x==a==b"
,
"!x=a"
,
}
for
_
,
test
:=
range
testGoodStrings
{
lq
,
err
:=
Parse
(
test
)
...
...
@@ -103,10 +105,14 @@ func TestSelectorMatches(t *testing.T) {
expectMatch
(
t
,
"x=y,z=w"
,
Set
{
"x"
:
"y"
,
"z"
:
"w"
})
expectMatch
(
t
,
"x!=y,z!=w"
,
Set
{
"x"
:
"z"
,
"z"
:
"a"
})
expectMatch
(
t
,
"notin=in"
,
Set
{
"notin"
:
"in"
})
// in and notin in exactMatch
expectMatch
(
t
,
"x"
,
Set
{
"x"
:
"z"
})
expectMatch
(
t
,
"!x"
,
Set
{
"y"
:
"z"
})
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"
})
labelset
:=
Set
{
"foo"
:
"bar"
,
...
...
@@ -178,11 +184,14 @@ func TestLexer(t *testing.T) {
{
"in"
,
InToken
},
{
"="
,
EqualsToken
},
{
"=="
,
DoubleEqualsToken
},
//Note that Lex returns the longest valid token found
{
"!"
,
DoesNotExistToken
},
{
"!="
,
NotEqualsToken
},
{
"("
,
OpenParToken
},
{
")"
,
ClosedParToken
},
//Non-"special" characters are considered part of an identifier
{
"~"
,
IdentifierToken
},
{
"||"
,
IdentifierToken
},
{
"!"
,
ErrorToken
},
}
for
_
,
v
:=
range
testcases
{
l
:=
&
Lexer
{
s
:
v
.
s
,
pos
:
0
}
...
...
@@ -213,6 +222,7 @@ func TestLexerSequence(t *testing.T) {
{
"key notin ( value )"
,
[]
Token
{
IdentifierToken
,
NotInToken
,
OpenParToken
,
IdentifierToken
,
ClosedParToken
}},
{
"key in ( value1, value2 )"
,
[]
Token
{
IdentifierToken
,
InToken
,
OpenParToken
,
IdentifierToken
,
CommaToken
,
IdentifierToken
,
ClosedParToken
}},
{
"key"
,
[]
Token
{
IdentifierToken
}},
{
"!key"
,
[]
Token
{
DoesNotExistToken
,
IdentifierToken
}},
{
"()"
,
[]
Token
{
OpenParToken
,
ClosedParToken
}},
{
"x in (),y"
,
[]
Token
{
IdentifierToken
,
InToken
,
OpenParToken
,
ClosedParToken
,
CommaToken
,
IdentifierToken
}},
{
"== != (), = notin"
,
[]
Token
{
DoubleEqualsToken
,
NotEqualsToken
,
OpenParToken
,
ClosedParToken
,
CommaToken
,
EqualsToken
,
NotInToken
}},
...
...
@@ -248,6 +258,7 @@ func TestParserLookahead(t *testing.T) {
{
"key notin ( value )"
,
[]
Token
{
IdentifierToken
,
NotInToken
,
OpenParToken
,
IdentifierToken
,
ClosedParToken
,
EndOfStringToken
}},
{
"key in ( value1, value2 )"
,
[]
Token
{
IdentifierToken
,
InToken
,
OpenParToken
,
IdentifierToken
,
CommaToken
,
IdentifierToken
,
ClosedParToken
,
EndOfStringToken
}},
{
"key"
,
[]
Token
{
IdentifierToken
,
EndOfStringToken
}},
{
"!key"
,
[]
Token
{
DoesNotExistToken
,
IdentifierToken
,
EndOfStringToken
}},
{
"()"
,
[]
Token
{
OpenParToken
,
ClosedParToken
,
EndOfStringToken
}},
{
""
,
[]
Token
{
EndOfStringToken
}},
{
"x in (),y"
,
[]
Token
{
IdentifierToken
,
InToken
,
OpenParToken
,
ClosedParToken
,
CommaToken
,
IdentifierToken
,
EndOfStringToken
}},
...
...
@@ -285,6 +296,7 @@ func TestRequirementConstructor(t *testing.T) {
{
"x"
,
InOperator
,
sets
.
NewString
(
"foo"
),
true
},
{
"x"
,
NotInOperator
,
sets
.
NewString
(
"foo"
),
true
},
{
"x"
,
ExistsOperator
,
nil
,
true
},
{
"x"
,
DoesNotExistOperator
,
nil
,
true
},
{
"1foo"
,
InOperator
,
sets
.
NewString
(
"bar"
),
true
},
{
"1234"
,
InOperator
,
sets
.
NewString
(
"bar"
),
true
},
{
strings
.
Repeat
(
"a"
,
254
),
ExistsOperator
,
nil
,
false
},
//breaks DNS rule that len(key) <= 253
...
...
@@ -311,6 +323,11 @@ func TestToString(t *testing.T) {
getRequirement
(
"z"
,
ExistsOperator
,
nil
,
t
)},
"x in (abc,def),y notin (jkl),z"
,
true
},
{
&
LabelSelector
{
getRequirement
(
"x"
,
NotInOperator
,
sets
.
NewString
(
"abc"
,
"def"
),
t
),
getRequirement
(
"y"
,
NotEqualsOperator
,
sets
.
NewString
(
"jkl"
),
t
),
getRequirement
(
"z"
,
DoesNotExistOperator
,
nil
,
t
)},
"x notin (abc,def),y!=jkl,!z"
,
true
},
{
&
LabelSelector
{
getRequirement
(
"x"
,
InOperator
,
sets
.
NewString
(
"abc"
,
"def"
),
t
),
req
},
// adding empty req for the trailing ','
"x in (abc,def),"
,
false
},
...
...
@@ -322,8 +339,9 @@ func TestToString(t *testing.T) {
{
&
LabelSelector
{
getRequirement
(
"x"
,
EqualsOperator
,
sets
.
NewString
(
"abc"
),
t
),
getRequirement
(
"y"
,
DoubleEqualsOperator
,
sets
.
NewString
(
"jkl"
),
t
),
getRequirement
(
"z"
,
NotEqualsOperator
,
sets
.
NewString
(
"a"
),
t
)},
"x=abc,y==jkl,z!=a"
,
true
},
getRequirement
(
"z"
,
NotEqualsOperator
,
sets
.
NewString
(
"a"
),
t
),
getRequirement
(
"z"
,
ExistsOperator
,
nil
,
t
)},
"x=abc,y==jkl,z!=a,z"
,
true
},
}
for
_
,
ts
:=
range
toStringTests
{
if
out
:=
ts
.
In
.
String
();
out
==
""
&&
ts
.
Valid
{
...
...
@@ -356,6 +374,14 @@ func TestRequirementLabelSelectorMatching(t *testing.T) {
getRequirement
(
"x"
,
NotInOperator
,
sets
.
NewString
(
""
),
t
),
getRequirement
(
"y"
,
ExistsOperator
,
nil
,
t
),
},
true
},
{
Set
{
"y"
:
""
},
&
LabelSelector
{
getRequirement
(
"x"
,
DoesNotExistOperator
,
nil
,
t
),
getRequirement
(
"y"
,
ExistsOperator
,
nil
,
t
),
},
true
},
{
Set
{
"y"
:
""
},
&
LabelSelector
{
getRequirement
(
"x"
,
NotInOperator
,
sets
.
NewString
(
""
),
t
),
getRequirement
(
"y"
,
DoesNotExistOperator
,
nil
,
t
),
},
false
},
{
Set
{
"y"
:
"baz"
},
&
LabelSelector
{
getRequirement
(
"x"
,
InOperator
,
sets
.
NewString
(
""
),
t
),
},
false
},
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment