github.com/containerd/containerd@v22.0.0-20200918172823-438c87b8e050+incompatible/filters/filter.go (about)

     1  /*
     2     Copyright The containerd Authors.
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  // Package filters defines a syntax and parser that can be used for the
    18  // filtration of items across the containerd API. The core is built on the
    19  // concept of protobuf field paths, with quoting.  Several operators allow the
    20  // user to flexibly select items based on field presence, equality, inequality
    21  // and regular expressions. Flexible adaptors support working with any type.
    22  //
    23  // The syntax is fairly familiar, if you've used container ecosystem
    24  // projects.  At the core, we base it on the concept of protobuf field
    25  // paths, augmenting with the ability to quote portions of the field path
    26  // to match arbitrary labels. These "selectors" come in the following
    27  // syntax:
    28  //
    29  // ```
    30  // <fieldpath>[<operator><value>]
    31  // ```
    32  //
    33  // A basic example is as follows:
    34  //
    35  // ```
    36  // name==foo
    37  // ```
    38  //
    39  // This would match all objects that have a field `name` with the value
    40  // `foo`. If we only want to test if the field is present, we can omit the
    41  // operator. This is most useful for matching labels in containerd. The
    42  // following will match objects that have the field "labels" and have the
    43  // label "foo" defined:
    44  //
    45  // ```
    46  // labels.foo
    47  // ```
    48  //
    49  // We also allow for quoting of parts of the field path to allow matching
    50  // of arbitrary items:
    51  //
    52  // ```
    53  // labels."very complex label"==something
    54  // ```
    55  //
    56  // We also define `!=` and `~=` as operators. The `!=` will match all
    57  // objects that don't match the value for a field and `~=` will compile the
    58  // target value as a regular expression and match the field value against that.
    59  //
    60  // Selectors can be combined using a comma, such that the resulting
    61  // selector will require all selectors are matched for the object to match.
    62  // The following example will match objects that are named `foo` and have
    63  // the label `bar`:
    64  //
    65  // ```
    66  // name==foo,labels.bar
    67  // ```
    68  //
    69  package filters
    70  
    71  import (
    72  	"regexp"
    73  
    74  	"github.com/containerd/containerd/log"
    75  )
    76  
    77  // Filter matches specific resources based the provided filter
    78  type Filter interface {
    79  	Match(adaptor Adaptor) bool
    80  }
    81  
    82  // FilterFunc is a function that handles matching with an adaptor
    83  type FilterFunc func(Adaptor) bool
    84  
    85  // Match matches the FilterFunc returning true if the object matches the filter
    86  func (fn FilterFunc) Match(adaptor Adaptor) bool {
    87  	return fn(adaptor)
    88  }
    89  
    90  // Always is a filter that always returns true for any type of object
    91  var Always FilterFunc = func(adaptor Adaptor) bool {
    92  	return true
    93  }
    94  
    95  // Any allows multiple filters to be matched against the object
    96  type Any []Filter
    97  
    98  // Match returns true if any of the provided filters are true
    99  func (m Any) Match(adaptor Adaptor) bool {
   100  	for _, m := range m {
   101  		if m.Match(adaptor) {
   102  			return true
   103  		}
   104  	}
   105  
   106  	return false
   107  }
   108  
   109  // All allows multiple filters to be matched against the object
   110  type All []Filter
   111  
   112  // Match only returns true if all filters match the object
   113  func (m All) Match(adaptor Adaptor) bool {
   114  	for _, m := range m {
   115  		if !m.Match(adaptor) {
   116  			return false
   117  		}
   118  	}
   119  
   120  	return true
   121  }
   122  
   123  type operator int
   124  
   125  const (
   126  	operatorPresent = iota
   127  	operatorEqual
   128  	operatorNotEqual
   129  	operatorMatches
   130  )
   131  
   132  func (op operator) String() string {
   133  	switch op {
   134  	case operatorPresent:
   135  		return "?"
   136  	case operatorEqual:
   137  		return "=="
   138  	case operatorNotEqual:
   139  		return "!="
   140  	case operatorMatches:
   141  		return "~="
   142  	}
   143  
   144  	return "unknown"
   145  }
   146  
   147  type selector struct {
   148  	fieldpath []string
   149  	operator  operator
   150  	value     string
   151  	re        *regexp.Regexp
   152  }
   153  
   154  func (m selector) Match(adaptor Adaptor) bool {
   155  	value, present := adaptor.Field(m.fieldpath)
   156  
   157  	switch m.operator {
   158  	case operatorPresent:
   159  		return present
   160  	case operatorEqual:
   161  		return present && value == m.value
   162  	case operatorNotEqual:
   163  		return value != m.value
   164  	case operatorMatches:
   165  		if m.re == nil {
   166  			r, err := regexp.Compile(m.value)
   167  			if err != nil {
   168  				log.L.Errorf("error compiling regexp %q", m.value)
   169  				return false
   170  			}
   171  
   172  			m.re = r
   173  		}
   174  
   175  		return m.re.MatchString(value)
   176  	default:
   177  		return false
   178  	}
   179  }