sigs.k8s.io/kueue@v0.6.2/pkg/controller/jobs/pod/event_handlers.go (about)

     1  package pod
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"slices"
     7  
     8  	corev1 "k8s.io/api/core/v1"
     9  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    10  	"k8s.io/apimachinery/pkg/runtime/schema"
    11  	"k8s.io/apimachinery/pkg/types"
    12  	"k8s.io/client-go/util/workqueue"
    13  	"k8s.io/klog/v2"
    14  	ctrl "sigs.k8s.io/controller-runtime"
    15  	"sigs.k8s.io/controller-runtime/pkg/client"
    16  	"sigs.k8s.io/controller-runtime/pkg/event"
    17  	"sigs.k8s.io/controller-runtime/pkg/reconcile"
    18  
    19  	kueue "sigs.k8s.io/kueue/apis/kueue/v1beta1"
    20  )
    21  
    22  var (
    23  	errFailedRefAPIVersionParse = fmt.Errorf("could not parse single pod OwnerReference APIVersion")
    24  )
    25  
    26  func reconcileRequestForPod(p *corev1.Pod) reconcile.Request {
    27  	groupName := p.GetLabels()[GroupNameLabel]
    28  
    29  	if groupName == "" {
    30  		return reconcile.Request{
    31  			NamespacedName: types.NamespacedName{
    32  				Namespace: p.Namespace,
    33  				Name:      p.Name,
    34  			},
    35  		}
    36  	}
    37  	return reconcile.Request{
    38  		NamespacedName: types.NamespacedName{
    39  			Name:      groupName,
    40  			Namespace: fmt.Sprintf("group/%s", p.Namespace),
    41  		},
    42  	}
    43  }
    44  
    45  // podEventHandler will convert reconcile requests for pods in group from "<namespace>/<pod-name>" to
    46  // "group/<namespace>/<group-name>".
    47  type podEventHandler struct {
    48  	cleanedUpPodsExpectations *expectationsStore
    49  }
    50  
    51  func (h *podEventHandler) Create(ctx context.Context, e event.CreateEvent, q workqueue.RateLimitingInterface) {
    52  	h.queueReconcileForPod(ctx, e.Object, q)
    53  }
    54  
    55  func (h *podEventHandler) Update(ctx context.Context, e event.UpdateEvent, q workqueue.RateLimitingInterface) {
    56  	h.queueReconcileForPod(ctx, e.ObjectNew, q)
    57  }
    58  
    59  func (h *podEventHandler) Delete(ctx context.Context, e event.DeleteEvent, q workqueue.RateLimitingInterface) {
    60  	p, ok := e.Object.(*corev1.Pod)
    61  	if !ok {
    62  		return
    63  	}
    64  
    65  	log := ctrl.LoggerFrom(ctx).WithValues("pod", klog.KObj(p))
    66  
    67  	if g, isGroup := p.Labels[GroupNameLabel]; isGroup {
    68  		// If the watch was temporarily unavailable, it is possible that the object reported in the event still
    69  		// has a finalizer, but we can consider this Pod cleaned up, as it is being deleted.
    70  		h.cleanedUpPodsExpectations.ObservedUID(log, types.NamespacedName{Namespace: p.Namespace, Name: g}, p.UID)
    71  	}
    72  
    73  	log.V(5).Info("Queueing reconcile for pod")
    74  
    75  	q.Add(reconcileRequestForPod(p))
    76  }
    77  
    78  func (h *podEventHandler) Generic(ctx context.Context, e event.GenericEvent, q workqueue.RateLimitingInterface) {
    79  }
    80  
    81  func (h *podEventHandler) queueReconcileForPod(ctx context.Context, object client.Object, q workqueue.RateLimitingInterface) {
    82  	p, ok := object.(*corev1.Pod)
    83  	if !ok {
    84  		return
    85  	}
    86  
    87  	log := ctrl.LoggerFrom(ctx).WithValues("pod", klog.KObj(p))
    88  
    89  	if g, isGroup := p.Labels[GroupNameLabel]; isGroup {
    90  		if !slices.Contains(p.Finalizers, PodFinalizer) {
    91  			h.cleanedUpPodsExpectations.ObservedUID(log, types.NamespacedName{Namespace: p.Namespace, Name: g}, p.UID)
    92  		}
    93  	}
    94  
    95  	log.V(5).Info("Queueing reconcile for pod")
    96  
    97  	q.Add(reconcileRequestForPod(p))
    98  }
    99  
   100  type workloadHandler struct{}
   101  
   102  func (h *workloadHandler) Create(ctx context.Context, e event.CreateEvent, q workqueue.RateLimitingInterface) {
   103  	h.queueReconcileForChildPod(ctx, e.Object, q)
   104  }
   105  
   106  func (h *workloadHandler) Update(ctx context.Context, e event.UpdateEvent, q workqueue.RateLimitingInterface) {
   107  	h.queueReconcileForChildPod(ctx, e.ObjectNew, q)
   108  }
   109  
   110  func (h *workloadHandler) Delete(context.Context, event.DeleteEvent, workqueue.RateLimitingInterface) {
   111  }
   112  
   113  func (h *workloadHandler) Generic(ctx context.Context, e event.GenericEvent, q workqueue.RateLimitingInterface) {
   114  }
   115  
   116  func (h *workloadHandler) queueReconcileForChildPod(ctx context.Context, object client.Object, q workqueue.RateLimitingInterface) {
   117  	w, ok := object.(*kueue.Workload)
   118  	if !ok {
   119  		return
   120  	}
   121  	log := ctrl.LoggerFrom(ctx).WithValues("workload", klog.KObj(w))
   122  
   123  	if len(w.ObjectMeta.OwnerReferences) == 0 {
   124  		return
   125  	}
   126  	log.V(5).Info("Queueing reconcile for parent pods")
   127  
   128  	// Compose request for a pod group if workload has an "is-group-workload" annotation
   129  	if w.Annotations[IsGroupWorkloadAnnotationKey] == IsGroupWorkloadAnnotationValue {
   130  		log.V(5).Info("Queueing reconcile for the pod group", "groupName", w.Name, "namespace", w.Namespace)
   131  		q.Add(reconcile.Request{
   132  			NamespacedName: types.NamespacedName{
   133  				Name:      w.Name,
   134  				Namespace: fmt.Sprintf("group/%s", w.Namespace),
   135  			},
   136  		})
   137  		return
   138  	}
   139  
   140  	// Get controller reference to a single pod object
   141  	if ref := metav1.GetControllerOf(object); ref != nil {
   142  		log.V(5).Info("Queueing reconcile for the single pod", "ControllerReference", ref)
   143  
   144  		// Parse the Group out of the OwnerReference to compare it to what was parsed out of the requested OwnerType
   145  		refGV, err := schema.ParseGroupVersion(ref.APIVersion)
   146  		if err != nil {
   147  			log.Error(errFailedRefAPIVersionParse, "failed to enqueue single pod request", "APIVersion", ref.APIVersion)
   148  			return
   149  		}
   150  
   151  		// Check if the OwnerReference is pointing to a Pod object.
   152  		if ref.Kind == "Pod" && refGV.Group == "" {
   153  			// Match found - add a Request for the object referred to in the OwnerReference
   154  			q.Add(reconcile.Request{NamespacedName: types.NamespacedName{
   155  				Name:      ref.Name,
   156  				Namespace: object.GetNamespace(),
   157  			}})
   158  			return
   159  		}
   160  	}
   161  }