sigs.k8s.io/kueue@v0.6.2/pkg/controller/core/admissioncheck_controller.go (about) 1 /* 2 Copyright 2023 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 core 18 19 import ( 20 "context" 21 22 "github.com/go-logr/logr" 23 "k8s.io/apimachinery/pkg/types" 24 "k8s.io/client-go/util/workqueue" 25 "k8s.io/klog/v2" 26 "k8s.io/utils/ptr" 27 ctrl "sigs.k8s.io/controller-runtime" 28 "sigs.k8s.io/controller-runtime/pkg/client" 29 "sigs.k8s.io/controller-runtime/pkg/controller" 30 "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" 31 "sigs.k8s.io/controller-runtime/pkg/event" 32 "sigs.k8s.io/controller-runtime/pkg/log" 33 "sigs.k8s.io/controller-runtime/pkg/reconcile" 34 "sigs.k8s.io/controller-runtime/pkg/source" 35 36 config "sigs.k8s.io/kueue/apis/config/v1beta1" 37 kueue "sigs.k8s.io/kueue/apis/kueue/v1beta1" 38 "sigs.k8s.io/kueue/pkg/cache" 39 "sigs.k8s.io/kueue/pkg/queue" 40 "sigs.k8s.io/kueue/pkg/util/slices" 41 ) 42 43 type AdmissionCheckUpdateWatcher interface { 44 NotifyAdmissionCheckUpdate(oldAc, newAc *kueue.AdmissionCheck) 45 } 46 47 // AdmissionCheckReconciler reconciles a AdmissionCheck object 48 type AdmissionCheckReconciler struct { 49 log logr.Logger 50 qManager *queue.Manager 51 client client.Client 52 cache *cache.Cache 53 cqUpdateCh chan event.GenericEvent 54 watchers []AdmissionCheckUpdateWatcher 55 } 56 57 func NewAdmissionCheckReconciler( 58 client client.Client, 59 qMgr *queue.Manager, 60 cache *cache.Cache, 61 ) *AdmissionCheckReconciler { 62 return &AdmissionCheckReconciler{ 63 log: ctrl.Log.WithName("admissioncheck-reconciler"), 64 qManager: qMgr, 65 client: client, 66 cache: cache, 67 cqUpdateCh: make(chan event.GenericEvent, updateChBuffer), 68 } 69 } 70 71 // +kubebuilder:rbac:groups=kueue.x-k8s.io,resources=admissionchecks,verbs=get;list;watch;create;update;patch;delete 72 // +kubebuilder:rbac:groups=kueue.x-k8s.io,resources=admissionchecks/status,verbs=get;update;patch 73 // +kubebuilder:rbac:groups=kueue.x-k8s.io,resources=admissionchecks/finalizers,verbs=update 74 75 // Reconcile is part of the main kubernetes reconciliation loop which aims to 76 // move the current state of the cluster closer to the desired state. 77 func (r *AdmissionCheckReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { 78 ac := &kueue.AdmissionCheck{} 79 80 if err := r.client.Get(ctx, req.NamespacedName, ac); err != nil { 81 return ctrl.Result{}, client.IgnoreNotFound(err) 82 } 83 84 log := log.FromContext(ctx).WithValues("admissionCheck", klog.KObj(ac)) 85 if ac.DeletionTimestamp.IsZero() { 86 if controllerutil.AddFinalizer(ac, kueue.ResourceInUseFinalizerName) { 87 if err := r.client.Update(ctx, ac); err != nil { 88 return ctrl.Result{}, err 89 } 90 log.V(5).Info("Added finalizer") 91 } 92 } else { 93 if controllerutil.ContainsFinalizer(ac, kueue.ResourceInUseFinalizerName) { 94 if cqs := r.cache.ClusterQueuesUsingAdmissionCheck(ac.Name); len(cqs) != 0 { 95 log.V(3).Info("admissionCheck is still in use", "ClusterQueues", cqs) 96 // We avoid to return error here to prevent backoff requeue, which is passive and wasteful. 97 // Instead, we drive the removal of finalizer by ClusterQueue Update/Delete events 98 // when the admissionCheck is no longer in use. 99 return ctrl.Result{}, nil 100 } 101 controllerutil.RemoveFinalizer(ac, kueue.ResourceInUseFinalizerName) 102 if err := r.client.Update(ctx, ac); err != nil { 103 return ctrl.Result{}, err 104 } 105 log.V(5).Info("Removed finalizer") 106 } 107 } 108 return ctrl.Result{}, nil 109 } 110 func (r *AdmissionCheckReconciler) notifyWatchers(oldAc, newAc *kueue.AdmissionCheck) { 111 for _, w := range r.watchers { 112 w.NotifyAdmissionCheckUpdate(oldAc, newAc) 113 } 114 } 115 116 func (r *AdmissionCheckReconciler) AddUpdateWatchers(watchers ...AdmissionCheckUpdateWatcher) { 117 r.watchers = append(r.watchers, watchers...) 118 } 119 120 func (r *AdmissionCheckReconciler) Create(e event.CreateEvent) bool { 121 ac, isAc := e.Object.(*kueue.AdmissionCheck) 122 if !isAc { 123 return false 124 } 125 defer r.notifyWatchers(nil, ac) 126 r.log.WithValues("admissionCheck", klog.KObj(ac)).V(5).Info("Create event") 127 if cqNames := r.cache.AddOrUpdateAdmissionCheck(ac); len(cqNames) > 0 { 128 r.qManager.QueueInadmissibleWorkloads(context.Background(), cqNames) 129 } 130 return true 131 } 132 133 func (r *AdmissionCheckReconciler) Update(e event.UpdateEvent) bool { 134 newAc, isAc := e.ObjectNew.(*kueue.AdmissionCheck) 135 if !isAc { 136 return false 137 } 138 oldAc, isAc := e.ObjectNew.(*kueue.AdmissionCheck) 139 if !isAc { 140 return false 141 } 142 defer r.notifyWatchers(oldAc, newAc) 143 r.log.WithValues("admissionCheck", klog.KObj(newAc)).V(5).Info("Update event") 144 if !newAc.DeletionTimestamp.IsZero() { 145 return true 146 } 147 if cqNames := r.cache.AddOrUpdateAdmissionCheck(newAc); len(cqNames) > 0 { 148 r.qManager.QueueInadmissibleWorkloads(context.Background(), cqNames) 149 } 150 return false 151 } 152 153 func (r *AdmissionCheckReconciler) Delete(e event.DeleteEvent) bool { 154 ac, isAc := e.Object.(*kueue.AdmissionCheck) 155 if !isAc { 156 return false 157 } 158 defer r.notifyWatchers(ac, nil) 159 r.log.WithValues("admissionCheck", klog.KObj(ac)).V(5).Info("Delete event") 160 161 if cqNames := r.cache.DeleteAdmissionCheck(ac); len(cqNames) > 0 { 162 r.qManager.QueueInadmissibleWorkloads(context.Background(), cqNames) 163 } 164 return true 165 } 166 167 func (r *AdmissionCheckReconciler) Generic(e event.GenericEvent) bool { 168 r.log.WithValues("object", klog.KObj(e.Object), "kind", e.Object.GetObjectKind().GroupVersionKind()).V(5).Info("Generic event") 169 return true 170 } 171 172 func (r *AdmissionCheckReconciler) NotifyClusterQueueUpdate(oldCq *kueue.ClusterQueue, newCq *kueue.ClusterQueue) { 173 log := r.log.WithValues("oldClusterQueue", klog.KObj(oldCq), "newClusterQueue", klog.KObj(newCq)) 174 log.V(5).Info("Cluster queue notification") 175 noChange := newCq != nil && oldCq != nil && slices.CmpNoOrder(oldCq.Spec.AdmissionChecks, newCq.Spec.AdmissionChecks) 176 if noChange { 177 return 178 } 179 180 if oldCq != nil { 181 r.cqUpdateCh <- event.GenericEvent{Object: oldCq} 182 } 183 184 if newCq != nil { 185 r.cqUpdateCh <- event.GenericEvent{Object: newCq} 186 } 187 } 188 189 type acCqHandler struct { 190 cache *cache.Cache 191 } 192 193 func (h *acCqHandler) Create(context.Context, event.CreateEvent, workqueue.RateLimitingInterface) { 194 } 195 196 func (h *acCqHandler) Update(context.Context, event.UpdateEvent, workqueue.RateLimitingInterface) { 197 } 198 199 func (h *acCqHandler) Delete(context.Context, event.DeleteEvent, workqueue.RateLimitingInterface) { 200 } 201 202 func (h *acCqHandler) Generic(ctx context.Context, e event.GenericEvent, q workqueue.RateLimitingInterface) { 203 cq := e.Object.(*kueue.ClusterQueue) 204 log := log.FromContext(ctx).WithValues("clusterQueue", klog.KObj(cq)) 205 log.V(6).Info("Cluster queue generic event") 206 207 for _, ac := range cq.Spec.AdmissionChecks { 208 if cqs := h.cache.ClusterQueuesUsingAdmissionCheck(ac); len(cqs) == 0 { 209 req := reconcile.Request{ 210 NamespacedName: types.NamespacedName{ 211 Name: ac, 212 }, 213 } 214 q.Add(req) 215 } 216 } 217 } 218 219 // SetupWithManager sets up the controller with the Manager. 220 func (r *AdmissionCheckReconciler) SetupWithManager(mgr ctrl.Manager, cfg *config.Configuration) error { 221 handler := acCqHandler{ 222 cache: r.cache, 223 } 224 return ctrl.NewControllerManagedBy(mgr). 225 For(&kueue.AdmissionCheck{}). 226 WithOptions(controller.Options{NeedLeaderElection: ptr.To(false)}). 227 WatchesRawSource(&source.Channel{Source: r.cqUpdateCh}, &handler). 228 WithEventFilter(r). 229 Complete(WithLeadingManager(mgr, r, &kueue.AdmissionCheck{}, cfg)) 230 }