179 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			179 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
   Copyright The containerd 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 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 against 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 against 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
 | 
						|
	}
 | 
						|
}
 |