123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478 |
- /*
- Copyright 2015 The Kubernetes Authors.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- package fields
- import (
- "bytes"
- "fmt"
- "sort"
- "strings"
- "k8s.io/apimachinery/pkg/selection"
- )
- // Selector represents a field selector.
- type Selector interface {
- // Matches returns true if this selector matches the given set of fields.
- Matches(Fields) bool
- // Empty returns true if this selector does not restrict the selection space.
- Empty() bool
- // RequiresExactMatch allows a caller to introspect whether a given selector
- // requires a single specific field to be set, and if so returns the value it
- // requires.
- RequiresExactMatch(field string) (value string, found bool)
- // Transform returns a new copy of the selector after TransformFunc has been
- // applied to the entire selector, or an error if fn returns an error.
- // If for a given requirement both field and value are transformed to empty
- // string, the requirement is skipped.
- Transform(fn TransformFunc) (Selector, error)
- // Requirements converts this interface to Requirements to expose
- // more detailed selection information.
- Requirements() Requirements
- // String returns a human readable string that represents this selector.
- String() string
- // Make a deep copy of the selector.
- DeepCopySelector() Selector
- }
- type nothingSelector struct{}
- func (n nothingSelector) Matches(_ Fields) bool { return false }
- func (n nothingSelector) Empty() bool { return false }
- func (n nothingSelector) String() string { return "" }
- func (n nothingSelector) Requirements() Requirements { return nil }
- func (n nothingSelector) DeepCopySelector() Selector { return n }
- func (n nothingSelector) RequiresExactMatch(field string) (value string, found bool) {
- return "", false
- }
- func (n nothingSelector) Transform(fn TransformFunc) (Selector, error) { return n, nil }
- // Nothing returns a selector that matches no fields
- func Nothing() Selector {
- return nothingSelector{}
- }
- // Everything returns a selector that matches all fields.
- func Everything() Selector {
- return andTerm{}
- }
- type hasTerm struct {
- field, value string
- }
- func (t *hasTerm) Matches(ls Fields) bool {
- return ls.Get(t.field) == t.value
- }
- func (t *hasTerm) Empty() bool {
- return false
- }
- func (t *hasTerm) RequiresExactMatch(field string) (value string, found bool) {
- if t.field == field {
- return t.value, true
- }
- return "", false
- }
- func (t *hasTerm) Transform(fn TransformFunc) (Selector, error) {
- field, value, err := fn(t.field, t.value)
- if err != nil {
- return nil, err
- }
- if len(field) == 0 && len(value) == 0 {
- return Everything(), nil
- }
- return &hasTerm{field, value}, nil
- }
- func (t *hasTerm) Requirements() Requirements {
- return []Requirement{{
- Field: t.field,
- Operator: selection.Equals,
- Value: t.value,
- }}
- }
- func (t *hasTerm) String() string {
- return fmt.Sprintf("%v=%v", t.field, EscapeValue(t.value))
- }
- func (t *hasTerm) DeepCopySelector() Selector {
- if t == nil {
- return nil
- }
- out := new(hasTerm)
- *out = *t
- return out
- }
- type notHasTerm struct {
- field, value string
- }
- func (t *notHasTerm) Matches(ls Fields) bool {
- return ls.Get(t.field) != t.value
- }
- func (t *notHasTerm) Empty() bool {
- return false
- }
- func (t *notHasTerm) RequiresExactMatch(field string) (value string, found bool) {
- return "", false
- }
- func (t *notHasTerm) Transform(fn TransformFunc) (Selector, error) {
- field, value, err := fn(t.field, t.value)
- if err != nil {
- return nil, err
- }
- if len(field) == 0 && len(value) == 0 {
- return Everything(), nil
- }
- return ¬HasTerm{field, value}, nil
- }
- func (t *notHasTerm) Requirements() Requirements {
- return []Requirement{{
- Field: t.field,
- Operator: selection.NotEquals,
- Value: t.value,
- }}
- }
- func (t *notHasTerm) String() string {
- return fmt.Sprintf("%v!=%v", t.field, EscapeValue(t.value))
- }
- func (t *notHasTerm) DeepCopySelector() Selector {
- if t == nil {
- return nil
- }
- out := new(notHasTerm)
- *out = *t
- return out
- }
- type andTerm []Selector
- func (t andTerm) Matches(ls Fields) bool {
- for _, q := range t {
- if !q.Matches(ls) {
- return false
- }
- }
- return true
- }
- func (t andTerm) Empty() bool {
- if t == nil {
- return true
- }
- if len([]Selector(t)) == 0 {
- return true
- }
- for i := range t {
- if !t[i].Empty() {
- return false
- }
- }
- return true
- }
- func (t andTerm) RequiresExactMatch(field string) (string, bool) {
- if t == nil || len([]Selector(t)) == 0 {
- return "", false
- }
- for i := range t {
- if value, found := t[i].RequiresExactMatch(field); found {
- return value, found
- }
- }
- return "", false
- }
- func (t andTerm) Transform(fn TransformFunc) (Selector, error) {
- next := make([]Selector, 0, len([]Selector(t)))
- for _, s := range []Selector(t) {
- n, err := s.Transform(fn)
- if err != nil {
- return nil, err
- }
- if !n.Empty() {
- next = append(next, n)
- }
- }
- return andTerm(next), nil
- }
- func (t andTerm) Requirements() Requirements {
- reqs := make([]Requirement, 0, len(t))
- for _, s := range []Selector(t) {
- rs := s.Requirements()
- reqs = append(reqs, rs...)
- }
- return reqs
- }
- func (t andTerm) String() string {
- var terms []string
- for _, q := range t {
- terms = append(terms, q.String())
- }
- return strings.Join(terms, ",")
- }
- func (t andTerm) DeepCopySelector() Selector {
- if t == nil {
- return nil
- }
- out := make([]Selector, len(t))
- for i := range t {
- out[i] = t[i].DeepCopySelector()
- }
- return andTerm(out)
- }
- // SelectorFromSet returns a Selector which will match exactly the given Set. A
- // nil Set is considered equivalent to Everything().
- func SelectorFromSet(ls Set) Selector {
- if ls == nil {
- return Everything()
- }
- items := make([]Selector, 0, len(ls))
- for field, value := range ls {
- items = append(items, &hasTerm{field: field, value: value})
- }
- if len(items) == 1 {
- return items[0]
- }
- return andTerm(items)
- }
- // valueEscaper prefixes \,= characters with a backslash
- var valueEscaper = strings.NewReplacer(
- // escape \ characters
- `\`, `\\`,
- // then escape , and = characters to allow unambiguous parsing of the value in a fieldSelector
- `,`, `\,`,
- `=`, `\=`,
- )
- // EscapeValue escapes an arbitrary literal string for use as a fieldSelector value
- func EscapeValue(s string) string {
- return valueEscaper.Replace(s)
- }
- // InvalidEscapeSequence indicates an error occurred unescaping a field selector
- type InvalidEscapeSequence struct {
- sequence string
- }
- func (i InvalidEscapeSequence) Error() string {
- return fmt.Sprintf("invalid field selector: invalid escape sequence: %s", i.sequence)
- }
- // UnescapedRune indicates an error occurred unescaping a field selector
- type UnescapedRune struct {
- r rune
- }
- func (i UnescapedRune) Error() string {
- return fmt.Sprintf("invalid field selector: unescaped character in value: %v", i.r)
- }
- // UnescapeValue unescapes a fieldSelector value and returns the original literal value.
- // May return the original string if it contains no escaped or special characters.
- func UnescapeValue(s string) (string, error) {
- // if there's no escaping or special characters, just return to avoid allocation
- if !strings.ContainsAny(s, `\,=`) {
- return s, nil
- }
- v := bytes.NewBuffer(make([]byte, 0, len(s)))
- inSlash := false
- for _, c := range s {
- if inSlash {
- switch c {
- case '\\', ',', '=':
- // omit the \ for recognized escape sequences
- v.WriteRune(c)
- default:
- // error on unrecognized escape sequences
- return "", InvalidEscapeSequence{sequence: string([]rune{'\\', c})}
- }
- inSlash = false
- continue
- }
- switch c {
- case '\\':
- inSlash = true
- case ',', '=':
- // unescaped , and = characters are not allowed in field selector values
- return "", UnescapedRune{r: c}
- default:
- v.WriteRune(c)
- }
- }
- // Ending with a single backslash is an invalid sequence
- if inSlash {
- return "", InvalidEscapeSequence{sequence: "\\"}
- }
- return v.String(), nil
- }
- // ParseSelectorOrDie takes a string representing a selector and returns an
- // object suitable for matching, or panic when an error occur.
- func ParseSelectorOrDie(s string) Selector {
- selector, err := ParseSelector(s)
- if err != nil {
- panic(err)
- }
- return selector
- }
- // ParseSelector takes a string representing a selector and returns an
- // object suitable for matching, or an error.
- func ParseSelector(selector string) (Selector, error) {
- return parseSelector(selector,
- func(lhs, rhs string) (newLhs, newRhs string, err error) {
- return lhs, rhs, nil
- })
- }
- // ParseAndTransformSelector parses the selector and runs them through the given TransformFunc.
- func ParseAndTransformSelector(selector string, fn TransformFunc) (Selector, error) {
- return parseSelector(selector, fn)
- }
- // TransformFunc transforms selectors.
- type TransformFunc func(field, value string) (newField, newValue string, err error)
- // splitTerms returns the comma-separated terms contained in the given fieldSelector.
- // Backslash-escaped commas are treated as data instead of delimiters, and are included in the returned terms, with the leading backslash preserved.
- func splitTerms(fieldSelector string) []string {
- if len(fieldSelector) == 0 {
- return nil
- }
- terms := make([]string, 0, 1)
- startIndex := 0
- inSlash := false
- for i, c := range fieldSelector {
- switch {
- case inSlash:
- inSlash = false
- case c == '\\':
- inSlash = true
- case c == ',':
- terms = append(terms, fieldSelector[startIndex:i])
- startIndex = i + 1
- }
- }
- terms = append(terms, fieldSelector[startIndex:])
- return terms
- }
- const (
- notEqualOperator = "!="
- doubleEqualOperator = "=="
- equalOperator = "="
- )
- // termOperators holds the recognized operators supported in fieldSelectors.
- // doubleEqualOperator and equal are equivalent, but doubleEqualOperator is checked first
- // to avoid leaving a leading = character on the rhs value.
- var termOperators = []string{notEqualOperator, doubleEqualOperator, equalOperator}
- // splitTerm returns the lhs, operator, and rhs parsed from the given term, along with an indicator of whether the parse was successful.
- // no escaping of special characters is supported in the lhs value, so the first occurrence of a recognized operator is used as the split point.
- // the literal rhs is returned, and the caller is responsible for applying any desired unescaping.
- func splitTerm(term string) (lhs, op, rhs string, ok bool) {
- for i := range term {
- remaining := term[i:]
- for _, op := range termOperators {
- if strings.HasPrefix(remaining, op) {
- return term[0:i], op, term[i+len(op):], true
- }
- }
- }
- return "", "", "", false
- }
- func parseSelector(selector string, fn TransformFunc) (Selector, error) {
- parts := splitTerms(selector)
- sort.StringSlice(parts).Sort()
- var items []Selector
- for _, part := range parts {
- if part == "" {
- continue
- }
- lhs, op, rhs, ok := splitTerm(part)
- if !ok {
- return nil, fmt.Errorf("invalid selector: '%s'; can't understand '%s'", selector, part)
- }
- unescapedRHS, err := UnescapeValue(rhs)
- if err != nil {
- return nil, err
- }
- switch op {
- case notEqualOperator:
- items = append(items, ¬HasTerm{field: lhs, value: unescapedRHS})
- case doubleEqualOperator:
- items = append(items, &hasTerm{field: lhs, value: unescapedRHS})
- case equalOperator:
- items = append(items, &hasTerm{field: lhs, value: unescapedRHS})
- default:
- return nil, fmt.Errorf("invalid selector: '%s'; can't understand '%s'", selector, part)
- }
- }
- if len(items) == 1 {
- return items[0].Transform(fn)
- }
- return andTerm(items).Transform(fn)
- }
- // OneTermEqualSelector returns an object that matches objects where one field/field equals one value.
- // Cannot return an error.
- func OneTermEqualSelector(k, v string) Selector {
- return &hasTerm{field: k, value: v}
- }
- // OneTermNotEqualSelector returns an object that matches objects where one field/field does not equal one value.
- // Cannot return an error.
- func OneTermNotEqualSelector(k, v string) Selector {
- return ¬HasTerm{field: k, value: v}
- }
- // AndSelectors creates a selector that is the logical AND of all the given selectors
- func AndSelectors(selectors ...Selector) Selector {
- return andTerm(selectors)
- }
|