sigs.k8s.io/controller-runtime@v0.18.2/pkg/predicate/predicate.go (about) 1 /* 2 Copyright 2018 The Kubernetes 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 predicate 18 19 import ( 20 "maps" 21 "reflect" 22 23 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 "k8s.io/apimachinery/pkg/labels" 25 "sigs.k8s.io/controller-runtime/pkg/client" 26 "sigs.k8s.io/controller-runtime/pkg/event" 27 logf "sigs.k8s.io/controller-runtime/pkg/internal/log" 28 ) 29 30 var log = logf.RuntimeLog.WithName("predicate").WithName("eventFilters") 31 32 // Predicate filters events before enqueuing the keys. 33 type Predicate = TypedPredicate[client.Object] 34 35 // TypedPredicate filters events before enqueuing the keys. 36 type TypedPredicate[T any] interface { 37 // Create returns true if the Create event should be processed 38 Create(event.TypedCreateEvent[T]) bool 39 40 // Delete returns true if the Delete event should be processed 41 Delete(event.TypedDeleteEvent[T]) bool 42 43 // Update returns true if the Update event should be processed 44 Update(event.TypedUpdateEvent[T]) bool 45 46 // Generic returns true if the Generic event should be processed 47 Generic(event.TypedGenericEvent[T]) bool 48 } 49 50 var _ Predicate = Funcs{} 51 var _ Predicate = ResourceVersionChangedPredicate{} 52 var _ Predicate = GenerationChangedPredicate{} 53 var _ Predicate = AnnotationChangedPredicate{} 54 var _ Predicate = or[client.Object]{} 55 var _ Predicate = and[client.Object]{} 56 var _ Predicate = not[client.Object]{} 57 58 // Funcs is a function that implements Predicate. 59 type Funcs = TypedFuncs[client.Object] 60 61 // TypedFuncs is a function that implements TypedPredicate. 62 type TypedFuncs[T any] struct { 63 // Create returns true if the Create event should be processed 64 CreateFunc func(event.TypedCreateEvent[T]) bool 65 66 // Delete returns true if the Delete event should be processed 67 DeleteFunc func(event.TypedDeleteEvent[T]) bool 68 69 // Update returns true if the Update event should be processed 70 UpdateFunc func(event.TypedUpdateEvent[T]) bool 71 72 // Generic returns true if the Generic event should be processed 73 GenericFunc func(event.TypedGenericEvent[T]) bool 74 } 75 76 // Create implements Predicate. 77 func (p TypedFuncs[T]) Create(e event.TypedCreateEvent[T]) bool { 78 if p.CreateFunc != nil { 79 return p.CreateFunc(e) 80 } 81 return true 82 } 83 84 // Delete implements Predicate. 85 func (p TypedFuncs[T]) Delete(e event.TypedDeleteEvent[T]) bool { 86 if p.DeleteFunc != nil { 87 return p.DeleteFunc(e) 88 } 89 return true 90 } 91 92 // Update implements Predicate. 93 func (p TypedFuncs[T]) Update(e event.TypedUpdateEvent[T]) bool { 94 if p.UpdateFunc != nil { 95 return p.UpdateFunc(e) 96 } 97 return true 98 } 99 100 // Generic implements Predicate. 101 func (p TypedFuncs[T]) Generic(e event.TypedGenericEvent[T]) bool { 102 if p.GenericFunc != nil { 103 return p.GenericFunc(e) 104 } 105 return true 106 } 107 108 // NewPredicateFuncs returns a predicate funcs that applies the given filter function 109 // on CREATE, UPDATE, DELETE and GENERIC events. For UPDATE events, the filter is applied 110 // to the new object. 111 func NewPredicateFuncs(filter func(object client.Object) bool) Funcs { 112 return Funcs{ 113 CreateFunc: func(e event.CreateEvent) bool { 114 return filter(e.Object) 115 }, 116 UpdateFunc: func(e event.UpdateEvent) bool { 117 return filter(e.ObjectNew) 118 }, 119 DeleteFunc: func(e event.DeleteEvent) bool { 120 return filter(e.Object) 121 }, 122 GenericFunc: func(e event.GenericEvent) bool { 123 return filter(e.Object) 124 }, 125 } 126 } 127 128 // NewTypedPredicateFuncs returns a predicate funcs that applies the given filter function 129 // on CREATE, UPDATE, DELETE and GENERIC events. For UPDATE events, the filter is applied 130 // to the new object. 131 func NewTypedPredicateFuncs[T any](filter func(object T) bool) TypedFuncs[T] { 132 return TypedFuncs[T]{ 133 CreateFunc: func(e event.TypedCreateEvent[T]) bool { 134 return filter(e.Object) 135 }, 136 UpdateFunc: func(e event.TypedUpdateEvent[T]) bool { 137 return filter(e.ObjectNew) 138 }, 139 DeleteFunc: func(e event.TypedDeleteEvent[T]) bool { 140 return filter(e.Object) 141 }, 142 GenericFunc: func(e event.TypedGenericEvent[T]) bool { 143 return filter(e.Object) 144 }, 145 } 146 } 147 148 // ResourceVersionChangedPredicate implements a default update predicate function on resource version change. 149 type ResourceVersionChangedPredicate struct { 150 Funcs 151 } 152 153 // Update implements default UpdateEvent filter for validating resource version change. 154 func (ResourceVersionChangedPredicate) Update(e event.UpdateEvent) bool { 155 if e.ObjectOld == nil { 156 log.Error(nil, "Update event has no old object to update", "event", e) 157 return false 158 } 159 if e.ObjectNew == nil { 160 log.Error(nil, "Update event has no new object to update", "event", e) 161 return false 162 } 163 164 return e.ObjectNew.GetResourceVersion() != e.ObjectOld.GetResourceVersion() 165 } 166 167 // GenerationChangedPredicate implements a default update predicate function on Generation change. 168 // 169 // This predicate will skip update events that have no change in the object's metadata.generation field. 170 // The metadata.generation field of an object is incremented by the API server when writes are made to the spec field of an object. 171 // This allows a controller to ignore update events where the spec is unchanged, and only the metadata and/or status fields are changed. 172 // 173 // For CustomResource objects the Generation is only incremented when the status subresource is enabled. 174 // 175 // Caveats: 176 // 177 // * The assumption that the Generation is incremented only on writing to the spec does not hold for all APIs. 178 // E.g For Deployment objects the Generation is also incremented on writes to the metadata.annotations field. 179 // For object types other than CustomResources be sure to verify which fields will trigger a Generation increment when they are written to. 180 // 181 // * With this predicate, any update events with writes only to the status field will not be reconciled. 182 // So in the event that the status block is overwritten or wiped by someone else the controller will not self-correct to restore the correct status. 183 type GenerationChangedPredicate = TypedGenerationChangedPredicate[client.Object] 184 185 // TypedGenerationChangedPredicate implements a default update predicate function on Generation change. 186 // 187 // This predicate will skip update events that have no change in the object's metadata.generation field. 188 // The metadata.generation field of an object is incremented by the API server when writes are made to the spec field of an object. 189 // This allows a controller to ignore update events where the spec is unchanged, and only the metadata and/or status fields are changed. 190 // 191 // For CustomResource objects the Generation is only incremented when the status subresource is enabled. 192 // 193 // Caveats: 194 // 195 // * The assumption that the Generation is incremented only on writing to the spec does not hold for all APIs. 196 // E.g For Deployment objects the Generation is also incremented on writes to the metadata.annotations field. 197 // For object types other than CustomResources be sure to verify which fields will trigger a Generation increment when they are written to. 198 // 199 // * With this predicate, any update events with writes only to the status field will not be reconciled. 200 // So in the event that the status block is overwritten or wiped by someone else the controller will not self-correct to restore the correct status. 201 type TypedGenerationChangedPredicate[T metav1.Object] struct { 202 TypedFuncs[T] 203 } 204 205 // Update implements default UpdateEvent filter for validating generation change. 206 func (TypedGenerationChangedPredicate[T]) Update(e event.TypedUpdateEvent[T]) bool { 207 if isNil(e.ObjectOld) { 208 log.Error(nil, "Update event has no old object to update", "event", e) 209 return false 210 } 211 if isNil(e.ObjectNew) { 212 log.Error(nil, "Update event has no new object for update", "event", e) 213 return false 214 } 215 216 return e.ObjectNew.GetGeneration() != e.ObjectOld.GetGeneration() 217 } 218 219 // AnnotationChangedPredicate implements a default update predicate function on annotation change. 220 // 221 // This predicate will skip update events that have no change in the object's annotation. 222 // It is intended to be used in conjunction with the GenerationChangedPredicate, as in the following example: 223 // 224 // Controller.Watch( 225 // &source.Kind{Type: v1.MyCustomKind}, 226 // &handler.EnqueueRequestForObject{}, 227 // predicate.Or(predicate.GenerationChangedPredicate{}, predicate.AnnotationChangedPredicate{})) 228 // 229 // This is mostly useful for controllers that needs to trigger both when the resource's generation is incremented 230 // (i.e., when the resource' .spec changes), or an annotation changes (e.g., for a staging/alpha API). 231 type AnnotationChangedPredicate = TypedAnnotationChangedPredicate[client.Object] 232 233 // TypedAnnotationChangedPredicate implements a default update predicate function on annotation change. 234 type TypedAnnotationChangedPredicate[T metav1.Object] struct { 235 TypedFuncs[T] 236 } 237 238 // Update implements default UpdateEvent filter for validating annotation change. 239 func (TypedAnnotationChangedPredicate[T]) Update(e event.TypedUpdateEvent[T]) bool { 240 if isNil(e.ObjectOld) { 241 log.Error(nil, "Update event has no old object to update", "event", e) 242 return false 243 } 244 if isNil(e.ObjectNew) { 245 log.Error(nil, "Update event has no new object for update", "event", e) 246 return false 247 } 248 249 return !maps.Equal(e.ObjectNew.GetAnnotations(), e.ObjectOld.GetAnnotations()) 250 } 251 252 // LabelChangedPredicate implements a default update predicate function on label change. 253 // 254 // This predicate will skip update events that have no change in the object's label. 255 // It is intended to be used in conjunction with the GenerationChangedPredicate, as in the following example: 256 // 257 // Controller.Watch( 258 // 259 // &source.Kind{Type: v1.MyCustomKind}, 260 // &handler.EnqueueRequestForObject{}, 261 // predicate.Or(predicate.GenerationChangedPredicate{}, predicate.LabelChangedPredicate{})) 262 // 263 // This will be helpful when object's labels is carrying some extra specification information beyond object's spec, 264 // and the controller will be triggered if any valid spec change (not only in spec, but also in labels) happens. 265 type LabelChangedPredicate = TypedLabelChangedPredicate[client.Object] 266 267 // TypedLabelChangedPredicate implements a default update predicate function on label change. 268 type TypedLabelChangedPredicate[T metav1.Object] struct { 269 TypedFuncs[T] 270 } 271 272 // Update implements default UpdateEvent filter for checking label change. 273 func (TypedLabelChangedPredicate[T]) Update(e event.TypedUpdateEvent[T]) bool { 274 if isNil(e.ObjectOld) { 275 log.Error(nil, "Update event has no old object to update", "event", e) 276 return false 277 } 278 if isNil(e.ObjectNew) { 279 log.Error(nil, "Update event has no new object for update", "event", e) 280 return false 281 } 282 283 return !maps.Equal(e.ObjectNew.GetLabels(), e.ObjectOld.GetLabels()) 284 } 285 286 // And returns a composite predicate that implements a logical AND of the predicates passed to it. 287 func And[T any](predicates ...TypedPredicate[T]) TypedPredicate[T] { 288 return and[T]{predicates} 289 } 290 291 type and[T any] struct { 292 predicates []TypedPredicate[T] 293 } 294 295 func (a and[T]) Create(e event.TypedCreateEvent[T]) bool { 296 for _, p := range a.predicates { 297 if !p.Create(e) { 298 return false 299 } 300 } 301 return true 302 } 303 304 func (a and[T]) Update(e event.TypedUpdateEvent[T]) bool { 305 for _, p := range a.predicates { 306 if !p.Update(e) { 307 return false 308 } 309 } 310 return true 311 } 312 313 func (a and[T]) Delete(e event.TypedDeleteEvent[T]) bool { 314 for _, p := range a.predicates { 315 if !p.Delete(e) { 316 return false 317 } 318 } 319 return true 320 } 321 322 func (a and[T]) Generic(e event.TypedGenericEvent[T]) bool { 323 for _, p := range a.predicates { 324 if !p.Generic(e) { 325 return false 326 } 327 } 328 return true 329 } 330 331 // Or returns a composite predicate that implements a logical OR of the predicates passed to it. 332 func Or[T any](predicates ...TypedPredicate[T]) TypedPredicate[T] { 333 return or[T]{predicates} 334 } 335 336 type or[T any] struct { 337 predicates []TypedPredicate[T] 338 } 339 340 func (o or[T]) Create(e event.TypedCreateEvent[T]) bool { 341 for _, p := range o.predicates { 342 if p.Create(e) { 343 return true 344 } 345 } 346 return false 347 } 348 349 func (o or[T]) Update(e event.TypedUpdateEvent[T]) bool { 350 for _, p := range o.predicates { 351 if p.Update(e) { 352 return true 353 } 354 } 355 return false 356 } 357 358 func (o or[T]) Delete(e event.TypedDeleteEvent[T]) bool { 359 for _, p := range o.predicates { 360 if p.Delete(e) { 361 return true 362 } 363 } 364 return false 365 } 366 367 func (o or[T]) Generic(e event.TypedGenericEvent[T]) bool { 368 for _, p := range o.predicates { 369 if p.Generic(e) { 370 return true 371 } 372 } 373 return false 374 } 375 376 // Not returns a predicate that implements a logical NOT of the predicate passed to it. 377 func Not[T any](predicate TypedPredicate[T]) TypedPredicate[T] { 378 return not[T]{predicate} 379 } 380 381 type not[T any] struct { 382 predicate TypedPredicate[T] 383 } 384 385 func (n not[T]) Create(e event.TypedCreateEvent[T]) bool { 386 return !n.predicate.Create(e) 387 } 388 389 func (n not[T]) Update(e event.TypedUpdateEvent[T]) bool { 390 return !n.predicate.Update(e) 391 } 392 393 func (n not[T]) Delete(e event.TypedDeleteEvent[T]) bool { 394 return !n.predicate.Delete(e) 395 } 396 397 func (n not[T]) Generic(e event.TypedGenericEvent[T]) bool { 398 return !n.predicate.Generic(e) 399 } 400 401 // LabelSelectorPredicate constructs a Predicate from a LabelSelector. 402 // Only objects matching the LabelSelector will be admitted. 403 func LabelSelectorPredicate(s metav1.LabelSelector) (Predicate, error) { 404 selector, err := metav1.LabelSelectorAsSelector(&s) 405 if err != nil { 406 return Funcs{}, err 407 } 408 return NewPredicateFuncs(func(o client.Object) bool { 409 return selector.Matches(labels.Set(o.GetLabels())) 410 }), nil 411 } 412 413 func isNil(arg any) bool { 414 if v := reflect.ValueOf(arg); !v.IsValid() || ((v.Kind() == reflect.Ptr || 415 v.Kind() == reflect.Interface || 416 v.Kind() == reflect.Slice || 417 v.Kind() == reflect.Map || 418 v.Kind() == reflect.Chan || 419 v.Kind() == reflect.Func) && v.IsNil()) { 420 return true 421 } 422 return false 423 }