...

Source file src/edge-infra.dev/pkg/k8s/object/match/match.go

Documentation: edge-infra.dev/pkg/k8s/object/match

     1  // Package match provides composable K8s object matchers.
     2  package match
     3  
     4  import (
     5  	"strings"
     6  
     7  	"k8s.io/apimachinery/pkg/runtime/schema"
     8  	"sigs.k8s.io/controller-runtime/pkg/client"
     9  )
    10  
    11  // Matcher is an object evaluation that can be composed into sets for
    12  // implementing "Do X if Any/All/None matchers apply" style logic, such as batch
    13  // operations against objects that need to implement configurable behavior for
    14  // subsets of objects that meet specific requirements.
    15  type Matcher interface {
    16  	Match(o client.Object) bool
    17  }
    18  
    19  // Object executes the [Matcher] against the K8s object. If the matcher or the
    20  // object is nil, false is returned. This allows optional matchers to be
    21  // evaluated cleanly, e.g.:
    22  //
    23  //		type Options struct {
    24  //			Exclude match.Matcher
    25  //		}
    26  //
    27  //		func DoThing(obj client.Object, opts Options) error {
    28  //			// This will return false if the Exclude option is unset, which would be
    29  //	   	// the expected behavior (i.e., include all objects)
    30  //			if match.Object(obj, opts.Exclude) {
    31  //				// Ignore this object, do nothing
    32  //				return nil
    33  //			}
    34  //			// [...]
    35  //		}
    36  func Object(o client.Object, m Matcher) bool {
    37  	if m == nil || o == nil {
    38  		return false
    39  	}
    40  	return m.Match(o)
    41  }
    42  
    43  // Everything matches on everything
    44  type Everything struct{}
    45  
    46  func (e Everything) Match(_ client.Object) bool {
    47  	return true
    48  }
    49  
    50  // All returns true if all matchers return true for the object. Nil matchers are
    51  // ignored. Empty list of matchers returns false.
    52  type All []Matcher
    53  
    54  func (a All) Match(o client.Object) bool {
    55  	if len(a) == 0 {
    56  		return false
    57  	}
    58  	for _, m := range a {
    59  		if m != nil && !m.Match(o) {
    60  			return false
    61  		}
    62  	}
    63  	return true
    64  }
    65  
    66  // Any returns true if any matcher returns true for the object. Nil matchers are
    67  // ignored. Empty list of matchers returns false.
    68  type Any []Matcher
    69  
    70  func (a Any) Match(o client.Object) bool {
    71  	for _, m := range a {
    72  		if m != nil && m.Match(o) {
    73  			return true
    74  		}
    75  	}
    76  	return false
    77  }
    78  
    79  // AnyInMetadata returns true if the object contains any individual record in
    80  // either `metadata.labels` or `metadata.annotations`
    81  type AnyInMetadata map[string]string
    82  
    83  func (a AnyInMetadata) Match(o client.Object) bool {
    84  	labels, annos := o.GetLabels(), o.GetAnnotations()
    85  	for k, v := range a {
    86  		if strings.EqualFold(labels[k], v) || strings.EqualFold(annos[k], v) {
    87  			return true
    88  		}
    89  	}
    90  	return false
    91  }
    92  
    93  // Labels returns true if all key value pairs are present on the object's
    94  // `metadata.labels` field.
    95  type Labels map[string]string
    96  
    97  func (l Labels) Match(o client.Object) bool {
    98  	if len(l) == 0 {
    99  		return false
   100  	}
   101  	labels := o.GetLabels()
   102  	for k, v := range l {
   103  		if !strings.EqualFold(labels[k], v) {
   104  			return false
   105  		}
   106  	}
   107  	return true
   108  }
   109  
   110  // Annotations returns true if all key value pairs are present on the object's
   111  // `metadata.annotations` field.
   112  type Annotations map[string]string
   113  
   114  func (a Annotations) Match(o client.Object) bool {
   115  	if len(a) == 0 {
   116  		return false
   117  	}
   118  	annos := o.GetAnnotations()
   119  	for k, v := range a {
   120  		if !strings.EqualFold(annos[k], v) {
   121  			return false
   122  		}
   123  	}
   124  	return true
   125  }
   126  
   127  // Kind returns true if the object has a matching kind.
   128  type Kind struct{ Kind string }
   129  
   130  func (k Kind) Match(o client.Object) bool {
   131  	return o.GetObjectKind().GroupVersionKind().Kind == k.Kind
   132  }
   133  
   134  // GroupVersion returns true if the object has a matching GroupVersion.
   135  type GroupVersion schema.GroupVersion
   136  
   137  func (gv GroupVersion) Match(o client.Object) bool {
   138  	actual := o.GetObjectKind().GroupVersionKind().GroupVersion()
   139  	return gv.Group == actual.Group && gv.Version == actual.Version
   140  }
   141  
   142  // Name returns true if the object has a matching Name.
   143  type Name struct{ Name string }
   144  
   145  func (n Name) Match(o client.Object) bool {
   146  	return o.GetName() == n.Name
   147  }
   148  
   149  // Namespace returns true if the object has a matching Namespace.
   150  type Namespace struct{ Namespace string }
   151  
   152  func (n Namespace) Match(o client.Object) bool {
   153  	return o.GetNamespace() == n.Namespace
   154  }
   155  

View as plain text