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  }