sigs.k8s.io/controller-runtime@v0.18.2/pkg/internal/source/event_handler.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 internal
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  
    23  	"k8s.io/client-go/tools/cache"
    24  	"k8s.io/client-go/util/workqueue"
    25  	"sigs.k8s.io/controller-runtime/pkg/client"
    26  	"sigs.k8s.io/controller-runtime/pkg/event"
    27  	"sigs.k8s.io/controller-runtime/pkg/handler"
    28  	logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
    29  
    30  	"sigs.k8s.io/controller-runtime/pkg/predicate"
    31  )
    32  
    33  var log = logf.RuntimeLog.WithName("source").WithName("EventHandler")
    34  
    35  // NewEventHandler creates a new EventHandler.
    36  func NewEventHandler[T client.Object](ctx context.Context, queue workqueue.RateLimitingInterface, handler handler.TypedEventHandler[T], predicates []predicate.TypedPredicate[T]) *EventHandler[T] {
    37  	return &EventHandler[T]{
    38  		ctx:        ctx,
    39  		handler:    handler,
    40  		queue:      queue,
    41  		predicates: predicates,
    42  	}
    43  }
    44  
    45  // EventHandler adapts a handler.EventHandler interface to a cache.ResourceEventHandler interface.
    46  type EventHandler[T client.Object] struct {
    47  	// ctx stores the context that created the event handler
    48  	// that is used to propagate cancellation signals to each handler function.
    49  	ctx context.Context
    50  
    51  	handler    handler.TypedEventHandler[T]
    52  	queue      workqueue.RateLimitingInterface
    53  	predicates []predicate.TypedPredicate[T]
    54  }
    55  
    56  // HandlerFuncs converts EventHandler to a ResourceEventHandlerFuncs
    57  // TODO: switch to ResourceEventHandlerDetailedFuncs with client-go 1.27
    58  func (e *EventHandler[T]) HandlerFuncs() cache.ResourceEventHandlerFuncs {
    59  	return cache.ResourceEventHandlerFuncs{
    60  		AddFunc:    e.OnAdd,
    61  		UpdateFunc: e.OnUpdate,
    62  		DeleteFunc: e.OnDelete,
    63  	}
    64  }
    65  
    66  // OnAdd creates CreateEvent and calls Create on EventHandler.
    67  func (e *EventHandler[T]) OnAdd(obj interface{}) {
    68  	c := event.TypedCreateEvent[T]{}
    69  
    70  	// Pull Object out of the object
    71  	if o, ok := obj.(T); ok {
    72  		c.Object = o
    73  	} else {
    74  		log.Error(nil, "OnAdd missing Object",
    75  			"object", obj, "type", fmt.Sprintf("%T", obj))
    76  		return
    77  	}
    78  
    79  	for _, p := range e.predicates {
    80  		if !p.Create(c) {
    81  			return
    82  		}
    83  	}
    84  
    85  	// Invoke create handler
    86  	ctx, cancel := context.WithCancel(e.ctx)
    87  	defer cancel()
    88  	e.handler.Create(ctx, c, e.queue)
    89  }
    90  
    91  // OnUpdate creates UpdateEvent and calls Update on EventHandler.
    92  func (e *EventHandler[T]) OnUpdate(oldObj, newObj interface{}) {
    93  	u := event.TypedUpdateEvent[T]{}
    94  
    95  	if o, ok := oldObj.(T); ok {
    96  		u.ObjectOld = o
    97  	} else {
    98  		log.Error(nil, "OnUpdate missing ObjectOld",
    99  			"object", oldObj, "type", fmt.Sprintf("%T", oldObj))
   100  		return
   101  	}
   102  
   103  	// Pull Object out of the object
   104  	if o, ok := newObj.(T); ok {
   105  		u.ObjectNew = o
   106  	} else {
   107  		log.Error(nil, "OnUpdate missing ObjectNew",
   108  			"object", newObj, "type", fmt.Sprintf("%T", newObj))
   109  		return
   110  	}
   111  
   112  	for _, p := range e.predicates {
   113  		if !p.Update(u) {
   114  			return
   115  		}
   116  	}
   117  
   118  	// Invoke update handler
   119  	ctx, cancel := context.WithCancel(e.ctx)
   120  	defer cancel()
   121  	e.handler.Update(ctx, u, e.queue)
   122  }
   123  
   124  // OnDelete creates DeleteEvent and calls Delete on EventHandler.
   125  func (e *EventHandler[T]) OnDelete(obj interface{}) {
   126  	d := event.TypedDeleteEvent[T]{}
   127  
   128  	// Deal with tombstone events by pulling the object out.  Tombstone events wrap the object in a
   129  	// DeleteFinalStateUnknown struct, so the object needs to be pulled out.
   130  	// Copied from sample-controller
   131  	// This should never happen if we aren't missing events, which we have concluded that we are not
   132  	// and made decisions off of this belief.  Maybe this shouldn't be here?
   133  	var ok bool
   134  	if _, ok = obj.(client.Object); !ok {
   135  		// If the object doesn't have Metadata, assume it is a tombstone object of type DeletedFinalStateUnknown
   136  		tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
   137  		if !ok {
   138  			log.Error(nil, "Error decoding objects.  Expected cache.DeletedFinalStateUnknown",
   139  				"type", fmt.Sprintf("%T", obj),
   140  				"object", obj)
   141  			return
   142  		}
   143  
   144  		// Set DeleteStateUnknown to true
   145  		d.DeleteStateUnknown = true
   146  
   147  		// Set obj to the tombstone obj
   148  		obj = tombstone.Obj
   149  	}
   150  
   151  	// Pull Object out of the object
   152  	if o, ok := obj.(T); ok {
   153  		d.Object = o
   154  	} else {
   155  		log.Error(nil, "OnDelete missing Object",
   156  			"object", obj, "type", fmt.Sprintf("%T", obj))
   157  		return
   158  	}
   159  
   160  	for _, p := range e.predicates {
   161  		if !p.Delete(d) {
   162  			return
   163  		}
   164  	}
   165  
   166  	// Invoke delete handler
   167  	ctx, cancel := context.WithCancel(e.ctx)
   168  	defer cancel()
   169  	e.handler.Delete(ctx, d, e.queue)
   170  }