164 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			164 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Package filters defines a syntax and parser that can be used for the
 | |
| // filtration of items across the containerd API. The core is built on the
 | |
| // concept of protobuf field paths, with quoting.  Several operators allow the
 | |
| // user to flexibly select items based on field presence, equality, inequality
 | |
| // and regular expressions. Flexible adaptors support working with any type.
 | |
| //
 | |
| // The syntax is fairly familiar, if you've used container ecosystem
 | |
| // projects.  At the core, we base it on the concept of protobuf field
 | |
| // paths, augmenting with the ability to quote portions of the field path
 | |
| // to match arbitrary labels. These "selectors" come in the following
 | |
| // syntax:
 | |
| //
 | |
| // ```
 | |
| // <fieldpath>[<operator><value>]
 | |
| // ```
 | |
| //
 | |
| // A basic example is as follows:
 | |
| //
 | |
| // ```
 | |
| // name==foo
 | |
| // ```
 | |
| //
 | |
| // This would match all objects that have a field `name` with the value
 | |
| // `foo`. If we only want to test if the field is present, we can omit the
 | |
| // operator. This is most useful for matching labels in containerd. The
 | |
| // following will match objects that have the field "labels" and have the
 | |
| // label "foo" defined:
 | |
| //
 | |
| // ```
 | |
| // labels.foo
 | |
| // ```
 | |
| //
 | |
| // We also allow for quoting of parts of the field path to allow matching
 | |
| // of arbitrary items:
 | |
| //
 | |
| // ```
 | |
| // labels."very complex label"==something
 | |
| // ```
 | |
| //
 | |
| // We also define `!=` and `~=` as operators. The `!=` will match all
 | |
| // objects that don't match the value for a field and `~=` will compile the
 | |
| // target value as a regular expression and match the field value against that.
 | |
| //
 | |
| // Selectors can be combined using a comma, such that the resulting
 | |
| // selector will require all selectors are matched for the object to match.
 | |
| // The following example will match objects that are named `foo` and have
 | |
| // the label `bar`:
 | |
| //
 | |
| // ```
 | |
| // name==foo,labels.bar
 | |
| // ```
 | |
| //
 | |
| package filters
 | |
| 
 | |
| import (
 | |
| 	"regexp"
 | |
| 
 | |
| 	"github.com/containerd/containerd/log"
 | |
| )
 | |
| 
 | |
| // Filter matches specific resources based the provided filter
 | |
| type Filter interface {
 | |
| 	Match(adaptor Adaptor) bool
 | |
| }
 | |
| 
 | |
| // FilterFunc is a function that handles matching with an adaptor
 | |
| type FilterFunc func(Adaptor) bool
 | |
| 
 | |
| // Match matches the FilterFunc returning true if the object matches the filter
 | |
| func (fn FilterFunc) Match(adaptor Adaptor) bool {
 | |
| 	return fn(adaptor)
 | |
| }
 | |
| 
 | |
| // Always is a filter that always returns true for any type of object
 | |
| var Always FilterFunc = func(adaptor Adaptor) bool {
 | |
| 	return true
 | |
| }
 | |
| 
 | |
| // Any allows multiple filters to be matched aginst the object
 | |
| type Any []Filter
 | |
| 
 | |
| // Match returns true if any of the provided filters are true
 | |
| func (m Any) Match(adaptor Adaptor) bool {
 | |
| 	for _, m := range m {
 | |
| 		if m.Match(adaptor) {
 | |
| 			return true
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| // All allows multiple filters to be matched aginst the object
 | |
| type All []Filter
 | |
| 
 | |
| // Match only returns true if all filters match the object
 | |
| func (m All) Match(adaptor Adaptor) bool {
 | |
| 	for _, m := range m {
 | |
| 		if !m.Match(adaptor) {
 | |
| 			return false
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return true
 | |
| }
 | |
| 
 | |
| type operator int
 | |
| 
 | |
| const (
 | |
| 	operatorPresent = iota
 | |
| 	operatorEqual
 | |
| 	operatorNotEqual
 | |
| 	operatorMatches
 | |
| )
 | |
| 
 | |
| func (op operator) String() string {
 | |
| 	switch op {
 | |
| 	case operatorPresent:
 | |
| 		return "?"
 | |
| 	case operatorEqual:
 | |
| 		return "=="
 | |
| 	case operatorNotEqual:
 | |
| 		return "!="
 | |
| 	case operatorMatches:
 | |
| 		return "~="
 | |
| 	}
 | |
| 
 | |
| 	return "unknown"
 | |
| }
 | |
| 
 | |
| type selector struct {
 | |
| 	fieldpath []string
 | |
| 	operator  operator
 | |
| 	value     string
 | |
| 	re        *regexp.Regexp
 | |
| }
 | |
| 
 | |
| func (m selector) Match(adaptor Adaptor) bool {
 | |
| 	value, present := adaptor.Field(m.fieldpath)
 | |
| 
 | |
| 	switch m.operator {
 | |
| 	case operatorPresent:
 | |
| 		return present
 | |
| 	case operatorEqual:
 | |
| 		return present && value == m.value
 | |
| 	case operatorNotEqual:
 | |
| 		return value != m.value
 | |
| 	case operatorMatches:
 | |
| 		if m.re == nil {
 | |
| 			r, err := regexp.Compile(m.value)
 | |
| 			if err != nil {
 | |
| 				log.L.Errorf("error compiling regexp %q", m.value)
 | |
| 				return false
 | |
| 			}
 | |
| 
 | |
| 			m.re = r
 | |
| 		}
 | |
| 
 | |
| 		return m.re.MatchString(value)
 | |
| 	default:
 | |
| 		return false
 | |
| 	}
 | |
| }
 | 
