// 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: // // ``` // [] // ``` // // 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 } }