k8s.io/kubernetes@v1.29.3/pkg/scheduler/eventhandlers.go (about) 1 /* 2 Copyright 2019 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 scheduler 18 19 import ( 20 "context" 21 "fmt" 22 "reflect" 23 "strings" 24 "time" 25 26 v1 "k8s.io/api/core/v1" 27 storagev1 "k8s.io/api/storage/v1" 28 "k8s.io/apimachinery/pkg/runtime/schema" 29 utilruntime "k8s.io/apimachinery/pkg/util/runtime" 30 "k8s.io/apimachinery/pkg/util/wait" 31 utilfeature "k8s.io/apiserver/pkg/util/feature" 32 "k8s.io/client-go/dynamic/dynamicinformer" 33 "k8s.io/client-go/informers" 34 "k8s.io/client-go/tools/cache" 35 corev1helpers "k8s.io/component-helpers/scheduling/corev1" 36 corev1nodeaffinity "k8s.io/component-helpers/scheduling/corev1/nodeaffinity" 37 "k8s.io/klog/v2" 38 "k8s.io/kubernetes/pkg/features" 39 "k8s.io/kubernetes/pkg/scheduler/framework" 40 "k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodeaffinity" 41 "k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodename" 42 "k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodeports" 43 "k8s.io/kubernetes/pkg/scheduler/framework/plugins/noderesources" 44 "k8s.io/kubernetes/pkg/scheduler/internal/queue" 45 "k8s.io/kubernetes/pkg/scheduler/profile" 46 ) 47 48 func (sched *Scheduler) onStorageClassAdd(obj interface{}) { 49 logger := sched.logger 50 sc, ok := obj.(*storagev1.StorageClass) 51 if !ok { 52 logger.Error(nil, "Cannot convert to *storagev1.StorageClass", "obj", obj) 53 return 54 } 55 56 // CheckVolumeBindingPred fails if pod has unbound immediate PVCs. If these 57 // PVCs have specified StorageClass name, creating StorageClass objects 58 // with late binding will cause predicates to pass, so we need to move pods 59 // to active queue. 60 // We don't need to invalidate cached results because results will not be 61 // cached for pod that has unbound immediate PVCs. 62 if sc.VolumeBindingMode != nil && *sc.VolumeBindingMode == storagev1.VolumeBindingWaitForFirstConsumer { 63 sched.SchedulingQueue.MoveAllToActiveOrBackoffQueue(logger, queue.StorageClassAdd, nil, sc, nil) 64 } 65 } 66 67 func (sched *Scheduler) addNodeToCache(obj interface{}) { 68 logger := sched.logger 69 node, ok := obj.(*v1.Node) 70 if !ok { 71 logger.Error(nil, "Cannot convert to *v1.Node", "obj", obj) 72 return 73 } 74 75 nodeInfo := sched.Cache.AddNode(logger, node) 76 logger.V(3).Info("Add event for node", "node", klog.KObj(node)) 77 sched.SchedulingQueue.MoveAllToActiveOrBackoffQueue(logger, queue.NodeAdd, nil, node, preCheckForNode(nodeInfo)) 78 } 79 80 func (sched *Scheduler) updateNodeInCache(oldObj, newObj interface{}) { 81 logger := sched.logger 82 oldNode, ok := oldObj.(*v1.Node) 83 if !ok { 84 logger.Error(nil, "Cannot convert oldObj to *v1.Node", "oldObj", oldObj) 85 return 86 } 87 newNode, ok := newObj.(*v1.Node) 88 if !ok { 89 logger.Error(nil, "Cannot convert newObj to *v1.Node", "newObj", newObj) 90 return 91 } 92 93 nodeInfo := sched.Cache.UpdateNode(logger, oldNode, newNode) 94 // Only requeue unschedulable pods if the node became more schedulable. 95 if event := nodeSchedulingPropertiesChange(newNode, oldNode); event != nil { 96 sched.SchedulingQueue.MoveAllToActiveOrBackoffQueue(logger, *event, oldNode, newNode, preCheckForNode(nodeInfo)) 97 } 98 } 99 100 func (sched *Scheduler) deleteNodeFromCache(obj interface{}) { 101 logger := sched.logger 102 var node *v1.Node 103 switch t := obj.(type) { 104 case *v1.Node: 105 node = t 106 case cache.DeletedFinalStateUnknown: 107 var ok bool 108 node, ok = t.Obj.(*v1.Node) 109 if !ok { 110 logger.Error(nil, "Cannot convert to *v1.Node", "obj", t.Obj) 111 return 112 } 113 default: 114 logger.Error(nil, "Cannot convert to *v1.Node", "obj", t) 115 return 116 } 117 logger.V(3).Info("Delete event for node", "node", klog.KObj(node)) 118 if err := sched.Cache.RemoveNode(logger, node); err != nil { 119 logger.Error(err, "Scheduler cache RemoveNode failed") 120 } 121 } 122 123 func (sched *Scheduler) addPodToSchedulingQueue(obj interface{}) { 124 logger := sched.logger 125 pod := obj.(*v1.Pod) 126 logger.V(3).Info("Add event for unscheduled pod", "pod", klog.KObj(pod)) 127 if err := sched.SchedulingQueue.Add(logger, pod); err != nil { 128 utilruntime.HandleError(fmt.Errorf("unable to queue %T: %v", obj, err)) 129 } 130 } 131 132 func (sched *Scheduler) updatePodInSchedulingQueue(oldObj, newObj interface{}) { 133 logger := sched.logger 134 oldPod, newPod := oldObj.(*v1.Pod), newObj.(*v1.Pod) 135 // Bypass update event that carries identical objects; otherwise, a duplicated 136 // Pod may go through scheduling and cause unexpected behavior (see #96071). 137 if oldPod.ResourceVersion == newPod.ResourceVersion { 138 return 139 } 140 141 isAssumed, err := sched.Cache.IsAssumedPod(newPod) 142 if err != nil { 143 utilruntime.HandleError(fmt.Errorf("failed to check whether pod %s/%s is assumed: %v", newPod.Namespace, newPod.Name, err)) 144 } 145 if isAssumed { 146 return 147 } 148 149 if err := sched.SchedulingQueue.Update(logger, oldPod, newPod); err != nil { 150 utilruntime.HandleError(fmt.Errorf("unable to update %T: %v", newObj, err)) 151 } 152 } 153 154 func (sched *Scheduler) deletePodFromSchedulingQueue(obj interface{}) { 155 logger := sched.logger 156 var pod *v1.Pod 157 switch t := obj.(type) { 158 case *v1.Pod: 159 pod = obj.(*v1.Pod) 160 case cache.DeletedFinalStateUnknown: 161 var ok bool 162 pod, ok = t.Obj.(*v1.Pod) 163 if !ok { 164 utilruntime.HandleError(fmt.Errorf("unable to convert object %T to *v1.Pod in %T", obj, sched)) 165 return 166 } 167 default: 168 utilruntime.HandleError(fmt.Errorf("unable to handle object in %T: %T", sched, obj)) 169 return 170 } 171 logger.V(3).Info("Delete event for unscheduled pod", "pod", klog.KObj(pod)) 172 if err := sched.SchedulingQueue.Delete(pod); err != nil { 173 utilruntime.HandleError(fmt.Errorf("unable to dequeue %T: %v", obj, err)) 174 } 175 fwk, err := sched.frameworkForPod(pod) 176 if err != nil { 177 // This shouldn't happen, because we only accept for scheduling the pods 178 // which specify a scheduler name that matches one of the profiles. 179 logger.Error(err, "Unable to get profile", "pod", klog.KObj(pod)) 180 return 181 } 182 // If a waiting pod is rejected, it indicates it's previously assumed and we're 183 // removing it from the scheduler cache. In this case, signal a AssignedPodDelete 184 // event to immediately retry some unscheduled Pods. 185 if fwk.RejectWaitingPod(pod.UID) { 186 sched.SchedulingQueue.MoveAllToActiveOrBackoffQueue(logger, queue.AssignedPodDelete, pod, nil, nil) 187 } 188 } 189 190 func (sched *Scheduler) addPodToCache(obj interface{}) { 191 logger := sched.logger 192 pod, ok := obj.(*v1.Pod) 193 if !ok { 194 logger.Error(nil, "Cannot convert to *v1.Pod", "obj", obj) 195 return 196 } 197 logger.V(3).Info("Add event for scheduled pod", "pod", klog.KObj(pod)) 198 199 if err := sched.Cache.AddPod(logger, pod); err != nil { 200 logger.Error(err, "Scheduler cache AddPod failed", "pod", klog.KObj(pod)) 201 } 202 203 sched.SchedulingQueue.AssignedPodAdded(logger, pod) 204 } 205 206 func (sched *Scheduler) updatePodInCache(oldObj, newObj interface{}) { 207 logger := sched.logger 208 oldPod, ok := oldObj.(*v1.Pod) 209 if !ok { 210 logger.Error(nil, "Cannot convert oldObj to *v1.Pod", "oldObj", oldObj) 211 return 212 } 213 newPod, ok := newObj.(*v1.Pod) 214 if !ok { 215 logger.Error(nil, "Cannot convert newObj to *v1.Pod", "newObj", newObj) 216 return 217 } 218 logger.V(4).Info("Update event for scheduled pod", "pod", klog.KObj(oldPod)) 219 220 if err := sched.Cache.UpdatePod(logger, oldPod, newPod); err != nil { 221 logger.Error(err, "Scheduler cache UpdatePod failed", "pod", klog.KObj(oldPod)) 222 } 223 224 sched.SchedulingQueue.AssignedPodUpdated(logger, oldPod, newPod) 225 } 226 227 func (sched *Scheduler) deletePodFromCache(obj interface{}) { 228 logger := sched.logger 229 var pod *v1.Pod 230 switch t := obj.(type) { 231 case *v1.Pod: 232 pod = t 233 case cache.DeletedFinalStateUnknown: 234 var ok bool 235 pod, ok = t.Obj.(*v1.Pod) 236 if !ok { 237 logger.Error(nil, "Cannot convert to *v1.Pod", "obj", t.Obj) 238 return 239 } 240 default: 241 logger.Error(nil, "Cannot convert to *v1.Pod", "obj", t) 242 return 243 } 244 logger.V(3).Info("Delete event for scheduled pod", "pod", klog.KObj(pod)) 245 if err := sched.Cache.RemovePod(logger, pod); err != nil { 246 logger.Error(err, "Scheduler cache RemovePod failed", "pod", klog.KObj(pod)) 247 } 248 249 sched.SchedulingQueue.MoveAllToActiveOrBackoffQueue(logger, queue.AssignedPodDelete, pod, nil, nil) 250 } 251 252 // assignedPod selects pods that are assigned (scheduled and running). 253 func assignedPod(pod *v1.Pod) bool { 254 return len(pod.Spec.NodeName) != 0 255 } 256 257 // responsibleForPod returns true if the pod has asked to be scheduled by the given scheduler. 258 func responsibleForPod(pod *v1.Pod, profiles profile.Map) bool { 259 return profiles.HandlesSchedulerName(pod.Spec.SchedulerName) 260 } 261 262 const ( 263 // syncedPollPeriod controls how often you look at the status of your sync funcs 264 syncedPollPeriod = 100 * time.Millisecond 265 ) 266 267 // WaitForHandlersSync waits for EventHandlers to sync. 268 // It returns true if it was successful, false if the controller should shut down 269 func (sched *Scheduler) WaitForHandlersSync(ctx context.Context) error { 270 return wait.PollUntilContextCancel(ctx, syncedPollPeriod, true, func(ctx context.Context) (done bool, err error) { 271 for _, handler := range sched.registeredHandlers { 272 if !handler.HasSynced() { 273 return false, nil 274 } 275 } 276 return true, nil 277 }) 278 } 279 280 // addAllEventHandlers is a helper function used in tests and in Scheduler 281 // to add event handlers for various informers. 282 func addAllEventHandlers( 283 sched *Scheduler, 284 informerFactory informers.SharedInformerFactory, 285 dynInformerFactory dynamicinformer.DynamicSharedInformerFactory, 286 gvkMap map[framework.GVK]framework.ActionType, 287 ) error { 288 var ( 289 handlerRegistration cache.ResourceEventHandlerRegistration 290 err error 291 handlers []cache.ResourceEventHandlerRegistration 292 ) 293 // scheduled pod cache 294 if handlerRegistration, err = informerFactory.Core().V1().Pods().Informer().AddEventHandler( 295 cache.FilteringResourceEventHandler{ 296 FilterFunc: func(obj interface{}) bool { 297 switch t := obj.(type) { 298 case *v1.Pod: 299 return assignedPod(t) 300 case cache.DeletedFinalStateUnknown: 301 if _, ok := t.Obj.(*v1.Pod); ok { 302 // The carried object may be stale, so we don't use it to check if 303 // it's assigned or not. Attempting to cleanup anyways. 304 return true 305 } 306 utilruntime.HandleError(fmt.Errorf("unable to convert object %T to *v1.Pod in %T", obj, sched)) 307 return false 308 default: 309 utilruntime.HandleError(fmt.Errorf("unable to handle object in %T: %T", sched, obj)) 310 return false 311 } 312 }, 313 Handler: cache.ResourceEventHandlerFuncs{ 314 AddFunc: sched.addPodToCache, 315 UpdateFunc: sched.updatePodInCache, 316 DeleteFunc: sched.deletePodFromCache, 317 }, 318 }, 319 ); err != nil { 320 return err 321 } 322 handlers = append(handlers, handlerRegistration) 323 324 // unscheduled pod queue 325 if handlerRegistration, err = informerFactory.Core().V1().Pods().Informer().AddEventHandler( 326 cache.FilteringResourceEventHandler{ 327 FilterFunc: func(obj interface{}) bool { 328 switch t := obj.(type) { 329 case *v1.Pod: 330 return !assignedPod(t) && responsibleForPod(t, sched.Profiles) 331 case cache.DeletedFinalStateUnknown: 332 if pod, ok := t.Obj.(*v1.Pod); ok { 333 // The carried object may be stale, so we don't use it to check if 334 // it's assigned or not. 335 return responsibleForPod(pod, sched.Profiles) 336 } 337 utilruntime.HandleError(fmt.Errorf("unable to convert object %T to *v1.Pod in %T", obj, sched)) 338 return false 339 default: 340 utilruntime.HandleError(fmt.Errorf("unable to handle object in %T: %T", sched, obj)) 341 return false 342 } 343 }, 344 Handler: cache.ResourceEventHandlerFuncs{ 345 AddFunc: sched.addPodToSchedulingQueue, 346 UpdateFunc: sched.updatePodInSchedulingQueue, 347 DeleteFunc: sched.deletePodFromSchedulingQueue, 348 }, 349 }, 350 ); err != nil { 351 return err 352 } 353 handlers = append(handlers, handlerRegistration) 354 355 if handlerRegistration, err = informerFactory.Core().V1().Nodes().Informer().AddEventHandler( 356 cache.ResourceEventHandlerFuncs{ 357 AddFunc: sched.addNodeToCache, 358 UpdateFunc: sched.updateNodeInCache, 359 DeleteFunc: sched.deleteNodeFromCache, 360 }, 361 ); err != nil { 362 return err 363 } 364 handlers = append(handlers, handlerRegistration) 365 366 logger := sched.logger 367 buildEvtResHandler := func(at framework.ActionType, gvk framework.GVK, shortGVK string) cache.ResourceEventHandlerFuncs { 368 funcs := cache.ResourceEventHandlerFuncs{} 369 if at&framework.Add != 0 { 370 evt := framework.ClusterEvent{Resource: gvk, ActionType: framework.Add, Label: fmt.Sprintf("%vAdd", shortGVK)} 371 funcs.AddFunc = func(obj interface{}) { 372 sched.SchedulingQueue.MoveAllToActiveOrBackoffQueue(logger, evt, nil, obj, nil) 373 } 374 } 375 if at&framework.Update != 0 { 376 evt := framework.ClusterEvent{Resource: gvk, ActionType: framework.Update, Label: fmt.Sprintf("%vUpdate", shortGVK)} 377 funcs.UpdateFunc = func(old, obj interface{}) { 378 sched.SchedulingQueue.MoveAllToActiveOrBackoffQueue(logger, evt, old, obj, nil) 379 } 380 } 381 if at&framework.Delete != 0 { 382 evt := framework.ClusterEvent{Resource: gvk, ActionType: framework.Delete, Label: fmt.Sprintf("%vDelete", shortGVK)} 383 funcs.DeleteFunc = func(obj interface{}) { 384 sched.SchedulingQueue.MoveAllToActiveOrBackoffQueue(logger, evt, obj, nil, nil) 385 } 386 } 387 return funcs 388 } 389 390 for gvk, at := range gvkMap { 391 switch gvk { 392 case framework.Node, framework.Pod: 393 // Do nothing. 394 case framework.CSINode: 395 if handlerRegistration, err = informerFactory.Storage().V1().CSINodes().Informer().AddEventHandler( 396 buildEvtResHandler(at, framework.CSINode, "CSINode"), 397 ); err != nil { 398 return err 399 } 400 handlers = append(handlers, handlerRegistration) 401 case framework.CSIDriver: 402 if handlerRegistration, err = informerFactory.Storage().V1().CSIDrivers().Informer().AddEventHandler( 403 buildEvtResHandler(at, framework.CSIDriver, "CSIDriver"), 404 ); err != nil { 405 return err 406 } 407 handlers = append(handlers, handlerRegistration) 408 case framework.CSIStorageCapacity: 409 if handlerRegistration, err = informerFactory.Storage().V1().CSIStorageCapacities().Informer().AddEventHandler( 410 buildEvtResHandler(at, framework.CSIStorageCapacity, "CSIStorageCapacity"), 411 ); err != nil { 412 return err 413 } 414 handlers = append(handlers, handlerRegistration) 415 case framework.PersistentVolume: 416 // MaxPDVolumeCountPredicate: since it relies on the counts of PV. 417 // 418 // PvAdd: Pods created when there are no PVs available will be stuck in 419 // unschedulable queue. But unbound PVs created for static provisioning and 420 // delay binding storage class are skipped in PV controller dynamic 421 // provisioning and binding process, will not trigger events to schedule pod 422 // again. So we need to move pods to active queue on PV add for this 423 // scenario. 424 // 425 // PvUpdate: Scheduler.bindVolumesWorker may fail to update assumed pod volume 426 // bindings due to conflicts if PVs are updated by PV controller or other 427 // parties, then scheduler will add pod back to unschedulable queue. We 428 // need to move pods to active queue on PV update for this scenario. 429 if handlerRegistration, err = informerFactory.Core().V1().PersistentVolumes().Informer().AddEventHandler( 430 buildEvtResHandler(at, framework.PersistentVolume, "Pv"), 431 ); err != nil { 432 return err 433 } 434 handlers = append(handlers, handlerRegistration) 435 case framework.PersistentVolumeClaim: 436 // MaxPDVolumeCountPredicate: add/update PVC will affect counts of PV when it is bound. 437 if handlerRegistration, err = informerFactory.Core().V1().PersistentVolumeClaims().Informer().AddEventHandler( 438 buildEvtResHandler(at, framework.PersistentVolumeClaim, "Pvc"), 439 ); err != nil { 440 return err 441 } 442 handlers = append(handlers, handlerRegistration) 443 case framework.PodSchedulingContext: 444 if utilfeature.DefaultFeatureGate.Enabled(features.DynamicResourceAllocation) { 445 if handlerRegistration, err = informerFactory.Resource().V1alpha2().PodSchedulingContexts().Informer().AddEventHandler( 446 buildEvtResHandler(at, framework.PodSchedulingContext, "PodSchedulingContext"), 447 ); err != nil { 448 return err 449 } 450 handlers = append(handlers, handlerRegistration) 451 } 452 case framework.ResourceClaim: 453 if utilfeature.DefaultFeatureGate.Enabled(features.DynamicResourceAllocation) { 454 if handlerRegistration, err = informerFactory.Resource().V1alpha2().ResourceClaims().Informer().AddEventHandler( 455 buildEvtResHandler(at, framework.ResourceClaim, "ResourceClaim"), 456 ); err != nil { 457 return err 458 } 459 handlers = append(handlers, handlerRegistration) 460 } 461 case framework.ResourceClass: 462 if utilfeature.DefaultFeatureGate.Enabled(features.DynamicResourceAllocation) { 463 if handlerRegistration, err = informerFactory.Resource().V1alpha2().ResourceClasses().Informer().AddEventHandler( 464 buildEvtResHandler(at, framework.ResourceClass, "ResourceClass"), 465 ); err != nil { 466 return err 467 } 468 handlers = append(handlers, handlerRegistration) 469 } 470 case framework.StorageClass: 471 if at&framework.Add != 0 { 472 if handlerRegistration, err = informerFactory.Storage().V1().StorageClasses().Informer().AddEventHandler( 473 cache.ResourceEventHandlerFuncs{ 474 AddFunc: sched.onStorageClassAdd, 475 }, 476 ); err != nil { 477 return err 478 } 479 handlers = append(handlers, handlerRegistration) 480 } 481 if at&framework.Update != 0 { 482 if handlerRegistration, err = informerFactory.Storage().V1().StorageClasses().Informer().AddEventHandler( 483 cache.ResourceEventHandlerFuncs{ 484 UpdateFunc: func(old, obj interface{}) { 485 sched.SchedulingQueue.MoveAllToActiveOrBackoffQueue(logger, queue.StorageClassUpdate, old, obj, nil) 486 }, 487 }, 488 ); err != nil { 489 return err 490 } 491 handlers = append(handlers, handlerRegistration) 492 } 493 default: 494 // Tests may not instantiate dynInformerFactory. 495 if dynInformerFactory == nil { 496 continue 497 } 498 // GVK is expected to be at least 3-folded, separated by dots. 499 // <kind in plural>.<version>.<group> 500 // Valid examples: 501 // - foos.v1.example.com 502 // - bars.v1beta1.a.b.c 503 // Invalid examples: 504 // - foos.v1 (2 sections) 505 // - foo.v1.example.com (the first section should be plural) 506 if strings.Count(string(gvk), ".") < 2 { 507 logger.Error(nil, "incorrect event registration", "gvk", gvk) 508 continue 509 } 510 // Fall back to try dynamic informers. 511 gvr, _ := schema.ParseResourceArg(string(gvk)) 512 dynInformer := dynInformerFactory.ForResource(*gvr).Informer() 513 if handlerRegistration, err = dynInformer.AddEventHandler( 514 buildEvtResHandler(at, gvk, strings.Title(gvr.Resource)), 515 ); err != nil { 516 return err 517 } 518 handlers = append(handlers, handlerRegistration) 519 } 520 } 521 sched.registeredHandlers = handlers 522 return nil 523 } 524 525 func nodeSchedulingPropertiesChange(newNode *v1.Node, oldNode *v1.Node) *framework.ClusterEvent { 526 if nodeSpecUnschedulableChanged(newNode, oldNode) { 527 return &queue.NodeSpecUnschedulableChange 528 } 529 if nodeAllocatableChanged(newNode, oldNode) { 530 return &queue.NodeAllocatableChange 531 } 532 if nodeLabelsChanged(newNode, oldNode) { 533 return &queue.NodeLabelChange 534 } 535 if nodeTaintsChanged(newNode, oldNode) { 536 return &queue.NodeTaintChange 537 } 538 if nodeConditionsChanged(newNode, oldNode) { 539 return &queue.NodeConditionChange 540 } 541 542 return nil 543 } 544 545 func nodeAllocatableChanged(newNode *v1.Node, oldNode *v1.Node) bool { 546 return !reflect.DeepEqual(oldNode.Status.Allocatable, newNode.Status.Allocatable) 547 } 548 549 func nodeLabelsChanged(newNode *v1.Node, oldNode *v1.Node) bool { 550 return !reflect.DeepEqual(oldNode.GetLabels(), newNode.GetLabels()) 551 } 552 553 func nodeTaintsChanged(newNode *v1.Node, oldNode *v1.Node) bool { 554 return !reflect.DeepEqual(newNode.Spec.Taints, oldNode.Spec.Taints) 555 } 556 557 func nodeConditionsChanged(newNode *v1.Node, oldNode *v1.Node) bool { 558 strip := func(conditions []v1.NodeCondition) map[v1.NodeConditionType]v1.ConditionStatus { 559 conditionStatuses := make(map[v1.NodeConditionType]v1.ConditionStatus, len(conditions)) 560 for i := range conditions { 561 conditionStatuses[conditions[i].Type] = conditions[i].Status 562 } 563 return conditionStatuses 564 } 565 return !reflect.DeepEqual(strip(oldNode.Status.Conditions), strip(newNode.Status.Conditions)) 566 } 567 568 func nodeSpecUnschedulableChanged(newNode *v1.Node, oldNode *v1.Node) bool { 569 return newNode.Spec.Unschedulable != oldNode.Spec.Unschedulable && !newNode.Spec.Unschedulable 570 } 571 572 func preCheckForNode(nodeInfo *framework.NodeInfo) queue.PreEnqueueCheck { 573 // Note: the following checks doesn't take preemption into considerations, in very rare 574 // cases (e.g., node resizing), "pod" may still fail a check but preemption helps. We deliberately 575 // chose to ignore those cases as unschedulable pods will be re-queued eventually. 576 return func(pod *v1.Pod) bool { 577 admissionResults := AdmissionCheck(pod, nodeInfo, false) 578 if len(admissionResults) != 0 { 579 return false 580 } 581 _, isUntolerated := corev1helpers.FindMatchingUntoleratedTaint(nodeInfo.Node().Spec.Taints, pod.Spec.Tolerations, func(t *v1.Taint) bool { 582 return t.Effect == v1.TaintEffectNoSchedule 583 }) 584 return !isUntolerated 585 } 586 } 587 588 // AdmissionCheck calls the filtering logic of noderesources/nodeport/nodeAffinity/nodename 589 // and returns the failure reasons. It's used in kubelet(pkg/kubelet/lifecycle/predicate.go) and scheduler. 590 // It returns the first failure if `includeAllFailures` is set to false; otherwise 591 // returns all failures. 592 func AdmissionCheck(pod *v1.Pod, nodeInfo *framework.NodeInfo, includeAllFailures bool) []AdmissionResult { 593 var admissionResults []AdmissionResult 594 insufficientResources := noderesources.Fits(pod, nodeInfo) 595 if len(insufficientResources) != 0 { 596 for i := range insufficientResources { 597 admissionResults = append(admissionResults, AdmissionResult{InsufficientResource: &insufficientResources[i]}) 598 } 599 if !includeAllFailures { 600 return admissionResults 601 } 602 } 603 604 if matches, _ := corev1nodeaffinity.GetRequiredNodeAffinity(pod).Match(nodeInfo.Node()); !matches { 605 admissionResults = append(admissionResults, AdmissionResult{Name: nodeaffinity.Name, Reason: nodeaffinity.ErrReasonPod}) 606 if !includeAllFailures { 607 return admissionResults 608 } 609 } 610 if !nodename.Fits(pod, nodeInfo) { 611 admissionResults = append(admissionResults, AdmissionResult{Name: nodename.Name, Reason: nodename.ErrReason}) 612 if !includeAllFailures { 613 return admissionResults 614 } 615 } 616 if !nodeports.Fits(pod, nodeInfo) { 617 admissionResults = append(admissionResults, AdmissionResult{Name: nodeports.Name, Reason: nodeports.ErrReason}) 618 if !includeAllFailures { 619 return admissionResults 620 } 621 } 622 return admissionResults 623 } 624 625 // AdmissionResult describes the reason why Scheduler can't admit the pod. 626 // If the reason is a resource fit one, then AdmissionResult.InsufficientResource includes the details. 627 type AdmissionResult struct { 628 Name string 629 Reason string 630 InsufficientResource *noderesources.InsufficientResource 631 }