...

Source file src/sigs.k8s.io/controller-runtime/pkg/handler/enqueue_owner.go

Documentation: sigs.k8s.io/controller-runtime/pkg/handler

     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 handler
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  
    23  	"k8s.io/apimachinery/pkg/api/meta"
    24  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    25  	"k8s.io/apimachinery/pkg/runtime"
    26  	"k8s.io/apimachinery/pkg/runtime/schema"
    27  	"k8s.io/apimachinery/pkg/types"
    28  	"k8s.io/client-go/util/workqueue"
    29  	"sigs.k8s.io/controller-runtime/pkg/client"
    30  	"sigs.k8s.io/controller-runtime/pkg/event"
    31  	logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
    32  	"sigs.k8s.io/controller-runtime/pkg/reconcile"
    33  )
    34  
    35  var _ EventHandler = &enqueueRequestForOwner[client.Object]{}
    36  
    37  var log = logf.RuntimeLog.WithName("eventhandler").WithName("enqueueRequestForOwner")
    38  
    39  // OwnerOption modifies an EnqueueRequestForOwner EventHandler.
    40  type OwnerOption func(e enqueueRequestForOwnerInterface)
    41  
    42  // EnqueueRequestForOwner enqueues Requests for the Owners of an object.  E.g. the object that created
    43  // the object that was the source of the Event.
    44  //
    45  // If a ReplicaSet creates Pods, users may reconcile the ReplicaSet in response to Pod Events using:
    46  //
    47  // - a source.Kind Source with Type of Pod.
    48  //
    49  // - a handler.enqueueRequestForOwner EventHandler with an OwnerType of ReplicaSet and OnlyControllerOwner set to true.
    50  func EnqueueRequestForOwner(scheme *runtime.Scheme, mapper meta.RESTMapper, ownerType client.Object, opts ...OwnerOption) EventHandler {
    51  	return TypedEnqueueRequestForOwner[client.Object](scheme, mapper, ownerType, opts...)
    52  }
    53  
    54  // TypedEnqueueRequestForOwner enqueues Requests for the Owners of an object.  E.g. the object that created
    55  // the object that was the source of the Event.
    56  //
    57  // If a ReplicaSet creates Pods, users may reconcile the ReplicaSet in response to Pod Events using:
    58  //
    59  // - a source.Kind Source with Type of Pod.
    60  //
    61  // - a handler.typedEnqueueRequestForOwner EventHandler with an OwnerType of ReplicaSet and OnlyControllerOwner set to true.
    62  //
    63  // TypedEnqueueRequestForOwner is experimental and subject to future change.
    64  func TypedEnqueueRequestForOwner[T client.Object](scheme *runtime.Scheme, mapper meta.RESTMapper, ownerType client.Object, opts ...OwnerOption) TypedEventHandler[T] {
    65  	e := &enqueueRequestForOwner[T]{
    66  		ownerType: ownerType,
    67  		mapper:    mapper,
    68  	}
    69  	if err := e.parseOwnerTypeGroupKind(scheme); err != nil {
    70  		panic(err)
    71  	}
    72  	for _, opt := range opts {
    73  		opt(e)
    74  	}
    75  	return e
    76  }
    77  
    78  // OnlyControllerOwner if provided will only look at the first OwnerReference with Controller: true.
    79  func OnlyControllerOwner() OwnerOption {
    80  	return func(e enqueueRequestForOwnerInterface) {
    81  		e.setIsController(true)
    82  	}
    83  }
    84  
    85  type enqueueRequestForOwnerInterface interface {
    86  	setIsController(bool)
    87  }
    88  
    89  type enqueueRequestForOwner[T client.Object] struct {
    90  	// ownerType is the type of the Owner object to look for in OwnerReferences.  Only Group and Kind are compared.
    91  	ownerType runtime.Object
    92  
    93  	// isController if set will only look at the first OwnerReference with Controller: true.
    94  	isController bool
    95  
    96  	// groupKind is the cached Group and Kind from OwnerType
    97  	groupKind schema.GroupKind
    98  
    99  	// mapper maps GroupVersionKinds to Resources
   100  	mapper meta.RESTMapper
   101  }
   102  
   103  func (e *enqueueRequestForOwner[T]) setIsController(isController bool) {
   104  	e.isController = isController
   105  }
   106  
   107  // Create implements EventHandler.
   108  func (e *enqueueRequestForOwner[T]) Create(ctx context.Context, evt event.TypedCreateEvent[T], q workqueue.RateLimitingInterface) {
   109  	reqs := map[reconcile.Request]empty{}
   110  	e.getOwnerReconcileRequest(evt.Object, reqs)
   111  	for req := range reqs {
   112  		q.Add(req)
   113  	}
   114  }
   115  
   116  // Update implements EventHandler.
   117  func (e *enqueueRequestForOwner[T]) Update(ctx context.Context, evt event.TypedUpdateEvent[T], q workqueue.RateLimitingInterface) {
   118  	reqs := map[reconcile.Request]empty{}
   119  	e.getOwnerReconcileRequest(evt.ObjectOld, reqs)
   120  	e.getOwnerReconcileRequest(evt.ObjectNew, reqs)
   121  	for req := range reqs {
   122  		q.Add(req)
   123  	}
   124  }
   125  
   126  // Delete implements EventHandler.
   127  func (e *enqueueRequestForOwner[T]) Delete(ctx context.Context, evt event.TypedDeleteEvent[T], q workqueue.RateLimitingInterface) {
   128  	reqs := map[reconcile.Request]empty{}
   129  	e.getOwnerReconcileRequest(evt.Object, reqs)
   130  	for req := range reqs {
   131  		q.Add(req)
   132  	}
   133  }
   134  
   135  // Generic implements EventHandler.
   136  func (e *enqueueRequestForOwner[T]) Generic(ctx context.Context, evt event.TypedGenericEvent[T], q workqueue.RateLimitingInterface) {
   137  	reqs := map[reconcile.Request]empty{}
   138  	e.getOwnerReconcileRequest(evt.Object, reqs)
   139  	for req := range reqs {
   140  		q.Add(req)
   141  	}
   142  }
   143  
   144  // parseOwnerTypeGroupKind parses the OwnerType into a Group and Kind and caches the result.  Returns false
   145  // if the OwnerType could not be parsed using the scheme.
   146  func (e *enqueueRequestForOwner[T]) parseOwnerTypeGroupKind(scheme *runtime.Scheme) error {
   147  	// Get the kinds of the type
   148  	kinds, _, err := scheme.ObjectKinds(e.ownerType)
   149  	if err != nil {
   150  		log.Error(err, "Could not get ObjectKinds for OwnerType", "owner type", fmt.Sprintf("%T", e.ownerType))
   151  		return err
   152  	}
   153  	// Expect only 1 kind.  If there is more than one kind this is probably an edge case such as ListOptions.
   154  	if len(kinds) != 1 {
   155  		err := fmt.Errorf("expected exactly 1 kind for OwnerType %T, but found %s kinds", e.ownerType, kinds)
   156  		log.Error(nil, "expected exactly 1 kind for OwnerType", "owner type", fmt.Sprintf("%T", e.ownerType), "kinds", kinds)
   157  		return err
   158  	}
   159  	// Cache the Group and Kind for the OwnerType
   160  	e.groupKind = schema.GroupKind{Group: kinds[0].Group, Kind: kinds[0].Kind}
   161  	return nil
   162  }
   163  
   164  // getOwnerReconcileRequest looks at object and builds a map of reconcile.Request to reconcile
   165  // owners of object that match e.OwnerType.
   166  func (e *enqueueRequestForOwner[T]) getOwnerReconcileRequest(object metav1.Object, result map[reconcile.Request]empty) {
   167  	// Iterate through the OwnerReferences looking for a match on Group and Kind against what was requested
   168  	// by the user
   169  	for _, ref := range e.getOwnersReferences(object) {
   170  		// Parse the Group out of the OwnerReference to compare it to what was parsed out of the requested OwnerType
   171  		refGV, err := schema.ParseGroupVersion(ref.APIVersion)
   172  		if err != nil {
   173  			log.Error(err, "Could not parse OwnerReference APIVersion",
   174  				"api version", ref.APIVersion)
   175  			return
   176  		}
   177  
   178  		// Compare the OwnerReference Group and Kind against the OwnerType Group and Kind specified by the user.
   179  		// If the two match, create a Request for the objected referred to by
   180  		// the OwnerReference.  Use the Name from the OwnerReference and the Namespace from the
   181  		// object in the event.
   182  		if ref.Kind == e.groupKind.Kind && refGV.Group == e.groupKind.Group {
   183  			// Match found - add a Request for the object referred to in the OwnerReference
   184  			request := reconcile.Request{NamespacedName: types.NamespacedName{
   185  				Name: ref.Name,
   186  			}}
   187  
   188  			// if owner is not namespaced then we should not set the namespace
   189  			mapping, err := e.mapper.RESTMapping(e.groupKind, refGV.Version)
   190  			if err != nil {
   191  				log.Error(err, "Could not retrieve rest mapping", "kind", e.groupKind)
   192  				return
   193  			}
   194  			if mapping.Scope.Name() != meta.RESTScopeNameRoot {
   195  				request.Namespace = object.GetNamespace()
   196  			}
   197  
   198  			result[request] = empty{}
   199  		}
   200  	}
   201  }
   202  
   203  // getOwnersReferences returns the OwnerReferences for an object as specified by the enqueueRequestForOwner
   204  // - if IsController is true: only take the Controller OwnerReference (if found)
   205  // - if IsController is false: take all OwnerReferences.
   206  func (e *enqueueRequestForOwner[T]) getOwnersReferences(object metav1.Object) []metav1.OwnerReference {
   207  	if object == nil {
   208  		return nil
   209  	}
   210  
   211  	// If not filtered as Controller only, then use all the OwnerReferences
   212  	if !e.isController {
   213  		return object.GetOwnerReferences()
   214  	}
   215  	// If filtered to a Controller, only take the Controller OwnerReference
   216  	if ownerRef := metav1.GetControllerOf(object); ownerRef != nil {
   217  		return []metav1.OwnerReference{*ownerRef}
   218  	}
   219  	// No Controller OwnerReference found
   220  	return nil
   221  }
   222  

View as plain text