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 }