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 }