mirror of https://github.com/goharbor/harbor.git
180 lines
4.4 KiB
Go
180 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
|
|
}
|
|
}
|