mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-10-31 16:13:45 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			477 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			477 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| /*
 | |
| 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)
 | |
| }
 | 
