mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-04 04:08:16 +00:00 
			
		
		
		
	kube-controller-manager: Decouple TaintManager from NodeLifeCycleController (KEP-3902)
This commit is contained in:
		@@ -57,7 +57,7 @@ import (
 | 
				
			|||||||
	"k8s.io/component-base/featuregate"
 | 
						"k8s.io/component-base/featuregate"
 | 
				
			||||||
	"k8s.io/component-base/logs"
 | 
						"k8s.io/component-base/logs"
 | 
				
			||||||
	logsapi "k8s.io/component-base/logs/api/v1"
 | 
						logsapi "k8s.io/component-base/logs/api/v1"
 | 
				
			||||||
	"k8s.io/component-base/metrics/features"
 | 
						metricsfeatures "k8s.io/component-base/metrics/features"
 | 
				
			||||||
	controllersmetrics "k8s.io/component-base/metrics/prometheus/controllers"
 | 
						controllersmetrics "k8s.io/component-base/metrics/prometheus/controllers"
 | 
				
			||||||
	"k8s.io/component-base/metrics/prometheus/slis"
 | 
						"k8s.io/component-base/metrics/prometheus/slis"
 | 
				
			||||||
	"k8s.io/component-base/term"
 | 
						"k8s.io/component-base/term"
 | 
				
			||||||
@@ -75,12 +75,13 @@ import (
 | 
				
			|||||||
	"k8s.io/kubernetes/cmd/kube-controller-manager/names"
 | 
						"k8s.io/kubernetes/cmd/kube-controller-manager/names"
 | 
				
			||||||
	kubectrlmgrconfig "k8s.io/kubernetes/pkg/controller/apis/config"
 | 
						kubectrlmgrconfig "k8s.io/kubernetes/pkg/controller/apis/config"
 | 
				
			||||||
	serviceaccountcontroller "k8s.io/kubernetes/pkg/controller/serviceaccount"
 | 
						serviceaccountcontroller "k8s.io/kubernetes/pkg/controller/serviceaccount"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/features"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/serviceaccount"
 | 
						"k8s.io/kubernetes/pkg/serviceaccount"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func init() {
 | 
					func init() {
 | 
				
			||||||
	utilruntime.Must(logsapi.AddFeatureGates(utilfeature.DefaultMutableFeatureGate))
 | 
						utilruntime.Must(logsapi.AddFeatureGates(utilfeature.DefaultMutableFeatureGate))
 | 
				
			||||||
	utilruntime.Must(features.AddFeatureGates(utilfeature.DefaultMutableFeatureGate))
 | 
						utilruntime.Must(metricsfeatures.AddFeatureGates(utilfeature.DefaultMutableFeatureGate))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
@@ -556,6 +557,10 @@ func NewControllerDescriptors() map[string]*ControllerDescriptor {
 | 
				
			|||||||
	register(newResourceClaimControllerDescriptor())
 | 
						register(newResourceClaimControllerDescriptor())
 | 
				
			||||||
	register(newLegacyServiceAccountTokenCleanerControllerDescriptor())
 | 
						register(newLegacyServiceAccountTokenCleanerControllerDescriptor())
 | 
				
			||||||
	register(newValidatingAdmissionPolicyStatusControllerDescriptor())
 | 
						register(newValidatingAdmissionPolicyStatusControllerDescriptor())
 | 
				
			||||||
 | 
						if utilfeature.DefaultFeatureGate.Enabled(features.SeparateTaintEvictionController) {
 | 
				
			||||||
 | 
							// register the flag only if the SeparateTaintEvictionController flag is enabled
 | 
				
			||||||
 | 
							register(newTaintEvictionControllerDescriptor())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, alias := range aliases.UnsortedList() {
 | 
						for _, alias := range aliases.UnsortedList() {
 | 
				
			||||||
		if _, ok := controllers[alias]; ok {
 | 
							if _, ok := controllers[alias]; ok {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,8 +28,9 @@ import (
 | 
				
			|||||||
	cpnames "k8s.io/cloud-provider/names"
 | 
						cpnames "k8s.io/cloud-provider/names"
 | 
				
			||||||
	"k8s.io/component-base/featuregate"
 | 
						"k8s.io/component-base/featuregate"
 | 
				
			||||||
	featuregatetesting "k8s.io/component-base/featuregate/testing"
 | 
						featuregatetesting "k8s.io/component-base/featuregate/testing"
 | 
				
			||||||
 | 
					 | 
				
			||||||
	"k8s.io/kubernetes/cmd/kube-controller-manager/names"
 | 
						"k8s.io/kubernetes/cmd/kube-controller-manager/names"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/features"
 | 
				
			||||||
 | 
						"k8s.io/utils/strings/slices"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestControllerNamesConsistency(t *testing.T) {
 | 
					func TestControllerNamesConsistency(t *testing.T) {
 | 
				
			||||||
@@ -73,6 +74,7 @@ func TestControllerNamesDeclaration(t *testing.T) {
 | 
				
			|||||||
		names.TokenCleanerController,
 | 
							names.TokenCleanerController,
 | 
				
			||||||
		names.NodeIpamController,
 | 
							names.NodeIpamController,
 | 
				
			||||||
		names.NodeLifecycleController,
 | 
							names.NodeLifecycleController,
 | 
				
			||||||
 | 
							names.TaintEvictionController,
 | 
				
			||||||
		cpnames.ServiceLBController,
 | 
							cpnames.ServiceLBController,
 | 
				
			||||||
		cpnames.NodeRouteController,
 | 
							cpnames.NodeRouteController,
 | 
				
			||||||
		cpnames.CloudNodeLifecycleController,
 | 
							cpnames.CloudNodeLifecycleController,
 | 
				
			||||||
@@ -156,3 +158,17 @@ func TestFeatureGatedControllersShouldNotDefineAliases(t *testing.T) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TestTaintEvictionControllerDeclaration ensures that it is possible to run taint-manager as a separated controller
 | 
				
			||||||
 | 
					// only when the SeparateTaintEvictionController feature is enabled
 | 
				
			||||||
 | 
					func TestTaintEvictionControllerDeclaration(t *testing.T) {
 | 
				
			||||||
 | 
						defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SeparateTaintEvictionController, true)()
 | 
				
			||||||
 | 
						if !slices.Contains(KnownControllers(), names.TaintEvictionController) {
 | 
				
			||||||
 | 
							t.Errorf("TaintEvictionController should be a registered controller when the SeparateTaintEvictionController feature is enabled")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SeparateTaintEvictionController, false)()
 | 
				
			||||||
 | 
						if slices.Contains(KnownControllers(), names.TaintEvictionController) {
 | 
				
			||||||
 | 
							t.Errorf("TaintEvictionController should not be a registered controller when the SeparateTaintEvictionController feature is disabled")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -59,6 +59,7 @@ import (
 | 
				
			|||||||
	resourcequotacontroller "k8s.io/kubernetes/pkg/controller/resourcequota"
 | 
						resourcequotacontroller "k8s.io/kubernetes/pkg/controller/resourcequota"
 | 
				
			||||||
	serviceaccountcontroller "k8s.io/kubernetes/pkg/controller/serviceaccount"
 | 
						serviceaccountcontroller "k8s.io/kubernetes/pkg/controller/serviceaccount"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/controller/storageversiongc"
 | 
						"k8s.io/kubernetes/pkg/controller/storageversiongc"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/controller/tainteviction"
 | 
				
			||||||
	ttlcontroller "k8s.io/kubernetes/pkg/controller/ttl"
 | 
						ttlcontroller "k8s.io/kubernetes/pkg/controller/ttl"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/controller/ttlafterfinished"
 | 
						"k8s.io/kubernetes/pkg/controller/ttlafterfinished"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/controller/volume/attachdetach"
 | 
						"k8s.io/kubernetes/pkg/controller/volume/attachdetach"
 | 
				
			||||||
@@ -219,6 +220,32 @@ func startNodeLifecycleController(ctx context.Context, controllerContext Control
 | 
				
			|||||||
	return nil, true, nil
 | 
						return nil, true, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func newTaintEvictionControllerDescriptor() *ControllerDescriptor {
 | 
				
			||||||
 | 
						return &ControllerDescriptor{
 | 
				
			||||||
 | 
							name:     names.TaintEvictionController,
 | 
				
			||||||
 | 
							initFunc: startTaintEvictionController,
 | 
				
			||||||
 | 
							requiredFeatureGates: []featuregate.Feature{
 | 
				
			||||||
 | 
								features.SeparateTaintEvictionController,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func startTaintEvictionController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
 | 
				
			||||||
 | 
						taintEvictionController, err := tainteviction.New(
 | 
				
			||||||
 | 
							ctx,
 | 
				
			||||||
 | 
							// taint-manager uses existing cluster role from node-controller
 | 
				
			||||||
 | 
							controllerContext.ClientBuilder.ClientOrDie("node-controller"),
 | 
				
			||||||
 | 
							controllerContext.InformerFactory.Core().V1().Pods(),
 | 
				
			||||||
 | 
							controllerContext.InformerFactory.Core().V1().Nodes(),
 | 
				
			||||||
 | 
							controllerName,
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, false, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						go taintEvictionController.Run(ctx)
 | 
				
			||||||
 | 
						return nil, true, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func newCloudNodeLifecycleControllerDescriptor() *ControllerDescriptor {
 | 
					func newCloudNodeLifecycleControllerDescriptor() *ControllerDescriptor {
 | 
				
			||||||
	return &ControllerDescriptor{
 | 
						return &ControllerDescriptor{
 | 
				
			||||||
		name:                      cpnames.CloudNodeLifecycleController,
 | 
							name:                      cpnames.CloudNodeLifecycleController,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -68,6 +68,7 @@ const (
 | 
				
			|||||||
	TokenCleanerController                       = "token-cleaner-controller"
 | 
						TokenCleanerController                       = "token-cleaner-controller"
 | 
				
			||||||
	NodeIpamController                           = "node-ipam-controller"
 | 
						NodeIpamController                           = "node-ipam-controller"
 | 
				
			||||||
	NodeLifecycleController                      = "node-lifecycle-controller"
 | 
						NodeLifecycleController                      = "node-lifecycle-controller"
 | 
				
			||||||
 | 
						TaintEvictionController                      = "taint-eviction-controller"
 | 
				
			||||||
	PersistentVolumeBinderController             = "persistentvolume-binder-controller"
 | 
						PersistentVolumeBinderController             = "persistentvolume-binder-controller"
 | 
				
			||||||
	PersistentVolumeAttachDetachController       = "persistentvolume-attach-detach-controller"
 | 
						PersistentVolumeAttachDetachController       = "persistentvolume-attach-detach-controller"
 | 
				
			||||||
	PersistentVolumeExpanderController           = "persistentvolume-expander-controller"
 | 
						PersistentVolumeExpanderController           = "persistentvolume-expander-controller"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -37,6 +37,7 @@ import (
 | 
				
			|||||||
	"k8s.io/apimachinery/pkg/labels"
 | 
						"k8s.io/apimachinery/pkg/labels"
 | 
				
			||||||
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
 | 
						utilruntime "k8s.io/apimachinery/pkg/util/runtime"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/util/wait"
 | 
						"k8s.io/apimachinery/pkg/util/wait"
 | 
				
			||||||
 | 
						utilfeature "k8s.io/apiserver/pkg/util/feature"
 | 
				
			||||||
	appsv1informers "k8s.io/client-go/informers/apps/v1"
 | 
						appsv1informers "k8s.io/client-go/informers/apps/v1"
 | 
				
			||||||
	coordinformers "k8s.io/client-go/informers/coordination/v1"
 | 
						coordinformers "k8s.io/client-go/informers/coordination/v1"
 | 
				
			||||||
	coreinformers "k8s.io/client-go/informers/core/v1"
 | 
						coreinformers "k8s.io/client-go/informers/core/v1"
 | 
				
			||||||
@@ -54,7 +55,9 @@ import (
 | 
				
			|||||||
	kubeletapis "k8s.io/kubelet/pkg/apis"
 | 
						kubeletapis "k8s.io/kubelet/pkg/apis"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/controller"
 | 
						"k8s.io/kubernetes/pkg/controller"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/controller/nodelifecycle/scheduler"
 | 
						"k8s.io/kubernetes/pkg/controller/nodelifecycle/scheduler"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/controller/tainteviction"
 | 
				
			||||||
	controllerutil "k8s.io/kubernetes/pkg/controller/util/node"
 | 
						controllerutil "k8s.io/kubernetes/pkg/controller/util/node"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/features"
 | 
				
			||||||
	taintutils "k8s.io/kubernetes/pkg/util/taints"
 | 
						taintutils "k8s.io/kubernetes/pkg/util/taints"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -127,6 +130,13 @@ const (
 | 
				
			|||||||
	// podUpdateWorkerSizes assumes that in most cases pod will be handled by monitorNodeHealth pass.
 | 
						// podUpdateWorkerSizes assumes that in most cases pod will be handled by monitorNodeHealth pass.
 | 
				
			||||||
	// Pod update workers will only handle lagging cache pods. 4 workers should be enough.
 | 
						// Pod update workers will only handle lagging cache pods. 4 workers should be enough.
 | 
				
			||||||
	podUpdateWorkerSize = 4
 | 
						podUpdateWorkerSize = 4
 | 
				
			||||||
 | 
						// nodeUpdateWorkerSize defines the size of workers for node update or/and pod update.
 | 
				
			||||||
 | 
						nodeUpdateWorkerSize = 8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// taintEvictionController is defined here in order to prevent imports of
 | 
				
			||||||
 | 
						// k8s.io/kubernetes/cmd/kube-controller-manager/names which would result in validation errors.
 | 
				
			||||||
 | 
						// This constant will be removed upon graduation of the SeparateTaintEvictionController feature.
 | 
				
			||||||
 | 
						taintEvictionController = "taint-eviction-controller"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// labelReconcileInfo lists Node labels to reconcile, and how to reconcile them.
 | 
					// labelReconcileInfo lists Node labels to reconcile, and how to reconcile them.
 | 
				
			||||||
@@ -207,7 +217,7 @@ type podUpdateItem struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Controller is the controller that manages node's life cycle.
 | 
					// Controller is the controller that manages node's life cycle.
 | 
				
			||||||
type Controller struct {
 | 
					type Controller struct {
 | 
				
			||||||
	taintManager *scheduler.NoExecuteTaintManager
 | 
						taintManager *tainteviction.Controller
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	podLister         corelisters.PodLister
 | 
						podLister         corelisters.PodLister
 | 
				
			||||||
	podInformerSynced cache.InformerSynced
 | 
						podInformerSynced cache.InformerSynced
 | 
				
			||||||
@@ -326,7 +336,7 @@ func NewNodeLifecycleController(
 | 
				
			|||||||
		nodeMonitorPeriod:           nodeMonitorPeriod,
 | 
							nodeMonitorPeriod:           nodeMonitorPeriod,
 | 
				
			||||||
		nodeStartupGracePeriod:      nodeStartupGracePeriod,
 | 
							nodeStartupGracePeriod:      nodeStartupGracePeriod,
 | 
				
			||||||
		nodeMonitorGracePeriod:      nodeMonitorGracePeriod,
 | 
							nodeMonitorGracePeriod:      nodeMonitorGracePeriod,
 | 
				
			||||||
		nodeUpdateWorkerSize:        scheduler.UpdateWorkerSize,
 | 
							nodeUpdateWorkerSize:        nodeUpdateWorkerSize,
 | 
				
			||||||
		zoneNoExecuteTainter:        make(map[string]*scheduler.RateLimitedTimedQueue),
 | 
							zoneNoExecuteTainter:        make(map[string]*scheduler.RateLimitedTimedQueue),
 | 
				
			||||||
		nodesToRetry:                sync.Map{},
 | 
							nodesToRetry:                sync.Map{},
 | 
				
			||||||
		zoneStates:                  make(map[string]ZoneState),
 | 
							zoneStates:                  make(map[string]ZoneState),
 | 
				
			||||||
@@ -346,17 +356,11 @@ func NewNodeLifecycleController(
 | 
				
			|||||||
		AddFunc: func(obj interface{}) {
 | 
							AddFunc: func(obj interface{}) {
 | 
				
			||||||
			pod := obj.(*v1.Pod)
 | 
								pod := obj.(*v1.Pod)
 | 
				
			||||||
			nc.podUpdated(nil, pod)
 | 
								nc.podUpdated(nil, pod)
 | 
				
			||||||
			if nc.taintManager != nil {
 | 
					 | 
				
			||||||
				nc.taintManager.PodUpdated(nil, pod)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		UpdateFunc: func(prev, obj interface{}) {
 | 
							UpdateFunc: func(prev, obj interface{}) {
 | 
				
			||||||
			prevPod := prev.(*v1.Pod)
 | 
								prevPod := prev.(*v1.Pod)
 | 
				
			||||||
			newPod := obj.(*v1.Pod)
 | 
								newPod := obj.(*v1.Pod)
 | 
				
			||||||
			nc.podUpdated(prevPod, newPod)
 | 
								nc.podUpdated(prevPod, newPod)
 | 
				
			||||||
			if nc.taintManager != nil {
 | 
					 | 
				
			||||||
				nc.taintManager.PodUpdated(prevPod, newPod)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		DeleteFunc: func(obj interface{}) {
 | 
							DeleteFunc: func(obj interface{}) {
 | 
				
			||||||
			pod, isPod := obj.(*v1.Pod)
 | 
								pod, isPod := obj.(*v1.Pod)
 | 
				
			||||||
@@ -374,9 +378,6 @@ func NewNodeLifecycleController(
 | 
				
			|||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			nc.podUpdated(pod, nil)
 | 
								nc.podUpdated(pod, nil)
 | 
				
			||||||
			if nc.taintManager != nil {
 | 
					 | 
				
			||||||
				nc.taintManager.PodUpdated(pod, nil)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	nc.podInformerSynced = podInformer.Informer().HasSynced
 | 
						nc.podInformerSynced = podInformer.Informer().HasSynced
 | 
				
			||||||
@@ -412,21 +413,14 @@ func NewNodeLifecycleController(
 | 
				
			|||||||
	nc.podLister = podInformer.Lister()
 | 
						nc.podLister = podInformer.Lister()
 | 
				
			||||||
	nc.nodeLister = nodeInformer.Lister()
 | 
						nc.nodeLister = nodeInformer.Lister()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	nc.taintManager = scheduler.NewNoExecuteTaintManager(ctx, kubeClient, nc.podLister, nc.nodeLister, nc.getPodsAssignedToNode)
 | 
						if !utilfeature.DefaultFeatureGate.Enabled(features.SeparateTaintEvictionController) {
 | 
				
			||||||
	nodeInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
 | 
							logger.Info("Running TaintEvictionController as part of NodeLifecyleController")
 | 
				
			||||||
		AddFunc: controllerutil.CreateAddNodeHandler(func(node *v1.Node) error {
 | 
							tm, err := tainteviction.New(ctx, kubeClient, podInformer, nodeInformer, taintEvictionController)
 | 
				
			||||||
			nc.taintManager.NodeUpdated(nil, node)
 | 
							if err != nil {
 | 
				
			||||||
			return nil
 | 
								return nil, err
 | 
				
			||||||
		}),
 | 
							}
 | 
				
			||||||
		UpdateFunc: controllerutil.CreateUpdateNodeHandler(func(oldNode, newNode *v1.Node) error {
 | 
							nc.taintManager = tm
 | 
				
			||||||
			nc.taintManager.NodeUpdated(oldNode, newNode)
 | 
						}
 | 
				
			||||||
			return nil
 | 
					 | 
				
			||||||
		}),
 | 
					 | 
				
			||||||
		DeleteFunc: controllerutil.CreateDeleteNodeHandler(logger, func(node *v1.Node) error {
 | 
					 | 
				
			||||||
			nc.taintManager.NodeUpdated(node, nil)
 | 
					 | 
				
			||||||
			return nil
 | 
					 | 
				
			||||||
		}),
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	logger.Info("Controller will reconcile labels")
 | 
						logger.Info("Controller will reconcile labels")
 | 
				
			||||||
	nodeInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
 | 
						nodeInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
 | 
				
			||||||
@@ -480,10 +474,13 @@ func (nc *Controller) Run(ctx context.Context) {
 | 
				
			|||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	go nc.taintManager.Run(ctx)
 | 
						if !utilfeature.DefaultFeatureGate.Enabled(features.SeparateTaintEvictionController) {
 | 
				
			||||||
 | 
							logger.Info("Starting", "controller", taintEvictionController)
 | 
				
			||||||
 | 
							go nc.taintManager.Run(ctx)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Start workers to reconcile labels and/or update NoSchedule taint for nodes.
 | 
						// Start workers to reconcile labels and/or update NoSchedule taint for nodes.
 | 
				
			||||||
	for i := 0; i < scheduler.UpdateWorkerSize; i++ {
 | 
						for i := 0; i < nodeUpdateWorkerSize; i++ {
 | 
				
			||||||
		// Thanks to "workqueue", each worker just need to get item from queue, because
 | 
							// Thanks to "workqueue", each worker just need to get item from queue, because
 | 
				
			||||||
		// the item is flagged when got from queue: if new event come, the new item will
 | 
							// the item is flagged when got from queue: if new event come, the new item will
 | 
				
			||||||
		// be re-queued until "Done", so no more than one worker handle the same item and
 | 
							// be re-queued until "Done", so no more than one worker handle the same item and
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										8
									
								
								pkg/controller/tainteviction/OWNERS
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								pkg/controller/tainteviction/OWNERS
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					# See the OWNERS docs at https://go.k8s.io/owners
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					approvers:
 | 
				
			||||||
 | 
					  - sig-scheduling-maintainers
 | 
				
			||||||
 | 
					reviewers:
 | 
				
			||||||
 | 
					  - sig-scheduling
 | 
				
			||||||
 | 
					labels:
 | 
				
			||||||
 | 
					  - sig/scheduling
 | 
				
			||||||
							
								
								
									
										19
									
								
								pkg/controller/tainteviction/doc.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								pkg/controller/tainteviction/doc.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2023 The Kubernetes Authors.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					You may obtain a copy of the License at
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					limitations under the License.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Package tainteviction contains the logic implementing taint-based eviction
 | 
				
			||||||
 | 
					// for Pods running on Nodes with NoExecute taints.
 | 
				
			||||||
 | 
					package tainteviction
 | 
				
			||||||
							
								
								
									
										60
									
								
								pkg/controller/tainteviction/metrics/metrics.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								pkg/controller/tainteviction/metrics/metrics.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,60 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2023 The Kubernetes Authors.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					You may obtain a copy of the License at
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					limitations under the License.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package metrics
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"k8s.io/component-base/metrics"
 | 
				
			||||||
 | 
						"k8s.io/component-base/metrics/legacyregistry"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const taintEvictionControllerSubsystem = "taint_eviction_controller"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						// PodDeletionsTotal counts the number of Pods deleted by TaintEvictionController since its start.
 | 
				
			||||||
 | 
						PodDeletionsTotal = metrics.NewCounter(
 | 
				
			||||||
 | 
							&metrics.CounterOpts{
 | 
				
			||||||
 | 
								Subsystem:      taintEvictionControllerSubsystem,
 | 
				
			||||||
 | 
								Name:           "pod_deletions_total",
 | 
				
			||||||
 | 
								Help:           "Total number of Pods deleted by TaintEvictionController since its start.",
 | 
				
			||||||
 | 
								StabilityLevel: metrics.ALPHA,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// PodDeletionsLatency tracks the latency, in seconds, between the time when a taint effect has been activated
 | 
				
			||||||
 | 
						// for the Pod and its deletion.
 | 
				
			||||||
 | 
						PodDeletionsLatency = metrics.NewHistogram(
 | 
				
			||||||
 | 
							&metrics.HistogramOpts{
 | 
				
			||||||
 | 
								Subsystem:      taintEvictionControllerSubsystem,
 | 
				
			||||||
 | 
								Name:           "pod_deletion_duration_seconds",
 | 
				
			||||||
 | 
								Help:           "Latency, in seconds, between the time when a taint effect has been activated for the Pod and its deletion via TaintEvictionController.",
 | 
				
			||||||
 | 
								Buckets:        []float64{0.005, 0.025, 0.1, 0.5, 1, 2.5, 10, 30, 60, 120, 180, 240}, // 5ms to 4m
 | 
				
			||||||
 | 
								StabilityLevel: metrics.ALPHA,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var registerMetrics sync.Once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Register registers TaintEvictionController metrics.
 | 
				
			||||||
 | 
					func Register() {
 | 
				
			||||||
 | 
						registerMetrics.Do(func() {
 | 
				
			||||||
 | 
							legacyregistry.MustRegister(PodDeletionsTotal)
 | 
				
			||||||
 | 
							legacyregistry.MustRegister(PodDeletionsLatency)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
 | 
				
			|||||||
limitations under the License.
 | 
					limitations under the License.
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package scheduler
 | 
					package tainteviction
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
@@ -31,19 +31,22 @@ import (
 | 
				
			|||||||
	"k8s.io/apimachinery/pkg/types"
 | 
						"k8s.io/apimachinery/pkg/types"
 | 
				
			||||||
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
 | 
						utilruntime "k8s.io/apimachinery/pkg/util/runtime"
 | 
				
			||||||
	"k8s.io/apiserver/pkg/util/feature"
 | 
						"k8s.io/apiserver/pkg/util/feature"
 | 
				
			||||||
 | 
						corev1informers "k8s.io/client-go/informers/core/v1"
 | 
				
			||||||
	clientset "k8s.io/client-go/kubernetes"
 | 
						clientset "k8s.io/client-go/kubernetes"
 | 
				
			||||||
	"k8s.io/client-go/kubernetes/scheme"
 | 
						"k8s.io/client-go/kubernetes/scheme"
 | 
				
			||||||
	v1core "k8s.io/client-go/kubernetes/typed/core/v1"
 | 
						v1core "k8s.io/client-go/kubernetes/typed/core/v1"
 | 
				
			||||||
	corelisters "k8s.io/client-go/listers/core/v1"
 | 
						corelisters "k8s.io/client-go/listers/core/v1"
 | 
				
			||||||
 | 
						"k8s.io/client-go/tools/cache"
 | 
				
			||||||
	"k8s.io/client-go/tools/record"
 | 
						"k8s.io/client-go/tools/record"
 | 
				
			||||||
	"k8s.io/client-go/util/workqueue"
 | 
						"k8s.io/client-go/util/workqueue"
 | 
				
			||||||
 | 
						"k8s.io/klog/v2"
 | 
				
			||||||
	apipod "k8s.io/kubernetes/pkg/api/v1/pod"
 | 
						apipod "k8s.io/kubernetes/pkg/api/v1/pod"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/apis/core/helper"
 | 
						"k8s.io/kubernetes/pkg/apis/core/helper"
 | 
				
			||||||
	v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
 | 
						v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/controller/tainteviction/metrics"
 | 
				
			||||||
 | 
						controllerutil "k8s.io/kubernetes/pkg/controller/util/node"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/features"
 | 
						"k8s.io/kubernetes/pkg/features"
 | 
				
			||||||
	utilpod "k8s.io/kubernetes/pkg/util/pod"
 | 
						utilpod "k8s.io/kubernetes/pkg/util/pod"
 | 
				
			||||||
 | 
					 | 
				
			||||||
	"k8s.io/klog/v2"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
@@ -77,14 +80,18 @@ func hash(val string, max int) int {
 | 
				
			|||||||
// GetPodsByNodeNameFunc returns the list of pods assigned to the specified node.
 | 
					// GetPodsByNodeNameFunc returns the list of pods assigned to the specified node.
 | 
				
			||||||
type GetPodsByNodeNameFunc func(nodeName string) ([]*v1.Pod, error)
 | 
					type GetPodsByNodeNameFunc func(nodeName string) ([]*v1.Pod, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NoExecuteTaintManager listens to Taint/Toleration changes and is responsible for removing Pods
 | 
					// Controller listens to Taint/Toleration changes and is responsible for removing Pods
 | 
				
			||||||
// from Nodes tainted with NoExecute Taints.
 | 
					// from Nodes tainted with NoExecute Taints.
 | 
				
			||||||
type NoExecuteTaintManager struct {
 | 
					type Controller struct {
 | 
				
			||||||
 | 
						name string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	client                clientset.Interface
 | 
						client                clientset.Interface
 | 
				
			||||||
	broadcaster           record.EventBroadcaster
 | 
						broadcaster           record.EventBroadcaster
 | 
				
			||||||
	recorder              record.EventRecorder
 | 
						recorder              record.EventRecorder
 | 
				
			||||||
	podLister             corelisters.PodLister
 | 
						podLister             corelisters.PodLister
 | 
				
			||||||
 | 
						podListerSynced       cache.InformerSynced
 | 
				
			||||||
	nodeLister            corelisters.NodeLister
 | 
						nodeLister            corelisters.NodeLister
 | 
				
			||||||
 | 
						nodeListerSynced      cache.InformerSynced
 | 
				
			||||||
	getPodsAssignedToNode GetPodsByNodeNameFunc
 | 
						getPodsAssignedToNode GetPodsByNodeNameFunc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	taintEvictionQueue *TimedWorkerQueue
 | 
						taintEvictionQueue *TimedWorkerQueue
 | 
				
			||||||
@@ -99,11 +106,11 @@ type NoExecuteTaintManager struct {
 | 
				
			|||||||
	podUpdateQueue  workqueue.Interface
 | 
						podUpdateQueue  workqueue.Interface
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func deletePodHandler(c clientset.Interface, emitEventFunc func(types.NamespacedName)) func(ctx context.Context, args *WorkArgs) error {
 | 
					func deletePodHandler(c clientset.Interface, emitEventFunc func(types.NamespacedName), controllerName string) func(ctx context.Context, fireAt time.Time, args *WorkArgs) error {
 | 
				
			||||||
	return func(ctx context.Context, args *WorkArgs) error {
 | 
						return func(ctx context.Context, fireAt time.Time, args *WorkArgs) error {
 | 
				
			||||||
		ns := args.NamespacedName.Namespace
 | 
							ns := args.NamespacedName.Namespace
 | 
				
			||||||
		name := args.NamespacedName.Name
 | 
							name := args.NamespacedName.Name
 | 
				
			||||||
		klog.FromContext(ctx).Info("NoExecuteTaintManager is deleting pod", "pod", args.NamespacedName.String())
 | 
							klog.FromContext(ctx).Info("Deleting pod", "controller", controllerName, "pod", args.NamespacedName)
 | 
				
			||||||
		if emitEventFunc != nil {
 | 
							if emitEventFunc != nil {
 | 
				
			||||||
			emitEventFunc(args.NamespacedName)
 | 
								emitEventFunc(args.NamespacedName)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -111,6 +118,8 @@ func deletePodHandler(c clientset.Interface, emitEventFunc func(types.Namespaced
 | 
				
			|||||||
		for i := 0; i < retries; i++ {
 | 
							for i := 0; i < retries; i++ {
 | 
				
			||||||
			err = addConditionAndDeletePod(ctx, c, name, ns)
 | 
								err = addConditionAndDeletePod(ctx, c, name, ns)
 | 
				
			||||||
			if err == nil {
 | 
								if err == nil {
 | 
				
			||||||
 | 
									metrics.PodDeletionsTotal.Inc()
 | 
				
			||||||
 | 
									metrics.PodDeletionsLatency.Observe(float64(time.Since(fireAt) * time.Second))
 | 
				
			||||||
				break
 | 
									break
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			time.Sleep(10 * time.Millisecond)
 | 
								time.Sleep(10 * time.Millisecond)
 | 
				
			||||||
@@ -175,34 +184,106 @@ func getMinTolerationTime(tolerations []v1.Toleration) time.Duration {
 | 
				
			|||||||
	return time.Duration(minTolerationTime) * time.Second
 | 
						return time.Duration(minTolerationTime) * time.Second
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewNoExecuteTaintManager creates a new NoExecuteTaintManager that will use passed clientset to
 | 
					// New creates a new Controller that will use passed clientset to communicate with the API server.
 | 
				
			||||||
// communicate with the API server.
 | 
					func New(ctx context.Context, c clientset.Interface, podInformer corev1informers.PodInformer, nodeInformer corev1informers.NodeInformer, controllerName string) (*Controller, error) {
 | 
				
			||||||
func NewNoExecuteTaintManager(ctx context.Context, c clientset.Interface, podLister corelisters.PodLister, nodeLister corelisters.NodeLister, getPodsAssignedToNode GetPodsByNodeNameFunc) *NoExecuteTaintManager {
 | 
						logger := klog.FromContext(ctx)
 | 
				
			||||||
 | 
						metrics.Register()
 | 
				
			||||||
	eventBroadcaster := record.NewBroadcaster()
 | 
						eventBroadcaster := record.NewBroadcaster()
 | 
				
			||||||
	recorder := eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "taint-controller"})
 | 
						recorder := eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: controllerName})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tm := &NoExecuteTaintManager{
 | 
						podIndexer := podInformer.Informer().GetIndexer()
 | 
				
			||||||
		client:                c,
 | 
					 | 
				
			||||||
		broadcaster:           eventBroadcaster,
 | 
					 | 
				
			||||||
		recorder:              recorder,
 | 
					 | 
				
			||||||
		podLister:             podLister,
 | 
					 | 
				
			||||||
		nodeLister:            nodeLister,
 | 
					 | 
				
			||||||
		getPodsAssignedToNode: getPodsAssignedToNode,
 | 
					 | 
				
			||||||
		taintedNodes:          make(map[string][]v1.Taint),
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		nodeUpdateQueue: workqueue.NewNamed("noexec_taint_node"),
 | 
						tm := &Controller{
 | 
				
			||||||
		podUpdateQueue:  workqueue.NewNamed("noexec_taint_pod"),
 | 
							name: controllerName,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							client:           c,
 | 
				
			||||||
 | 
							broadcaster:      eventBroadcaster,
 | 
				
			||||||
 | 
							recorder:         recorder,
 | 
				
			||||||
 | 
							podLister:        podInformer.Lister(),
 | 
				
			||||||
 | 
							podListerSynced:  podInformer.Informer().HasSynced,
 | 
				
			||||||
 | 
							nodeLister:       nodeInformer.Lister(),
 | 
				
			||||||
 | 
							nodeListerSynced: nodeInformer.Informer().HasSynced,
 | 
				
			||||||
 | 
							getPodsAssignedToNode: func(nodeName string) ([]*v1.Pod, error) {
 | 
				
			||||||
 | 
								objs, err := podIndexer.ByIndex("spec.nodeName", nodeName)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return nil, err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								pods := make([]*v1.Pod, 0, len(objs))
 | 
				
			||||||
 | 
								for _, obj := range objs {
 | 
				
			||||||
 | 
									pod, ok := obj.(*v1.Pod)
 | 
				
			||||||
 | 
									if !ok {
 | 
				
			||||||
 | 
										continue
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									pods = append(pods, pod)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return pods, nil
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							taintedNodes: make(map[string][]v1.Taint),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							nodeUpdateQueue: workqueue.NewWithConfig(workqueue.QueueConfig{Name: "noexec_taint_node"}),
 | 
				
			||||||
 | 
							podUpdateQueue:  workqueue.NewWithConfig(workqueue.QueueConfig{Name: "noexec_taint_pod"}),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	tm.taintEvictionQueue = CreateWorkerQueue(deletePodHandler(c, tm.emitPodDeletionEvent))
 | 
						tm.taintEvictionQueue = CreateWorkerQueue(deletePodHandler(c, tm.emitPodDeletionEvent, tm.name))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return tm
 | 
						_, err := podInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
 | 
				
			||||||
 | 
							AddFunc: func(obj interface{}) {
 | 
				
			||||||
 | 
								pod := obj.(*v1.Pod)
 | 
				
			||||||
 | 
								tm.PodUpdated(nil, pod)
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							UpdateFunc: func(prev, obj interface{}) {
 | 
				
			||||||
 | 
								prevPod := prev.(*v1.Pod)
 | 
				
			||||||
 | 
								newPod := obj.(*v1.Pod)
 | 
				
			||||||
 | 
								tm.PodUpdated(prevPod, newPod)
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							DeleteFunc: func(obj interface{}) {
 | 
				
			||||||
 | 
								pod, isPod := obj.(*v1.Pod)
 | 
				
			||||||
 | 
								// We can get DeletedFinalStateUnknown instead of *v1.Pod here and we need to handle that correctly.
 | 
				
			||||||
 | 
								if !isPod {
 | 
				
			||||||
 | 
									deletedState, ok := obj.(cache.DeletedFinalStateUnknown)
 | 
				
			||||||
 | 
									if !ok {
 | 
				
			||||||
 | 
										logger.Error(nil, "Received unexpected object", "object", obj)
 | 
				
			||||||
 | 
										return
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									pod, ok = deletedState.Obj.(*v1.Pod)
 | 
				
			||||||
 | 
									if !ok {
 | 
				
			||||||
 | 
										logger.Error(nil, "DeletedFinalStateUnknown contained non-Pod object", "object", deletedState.Obj)
 | 
				
			||||||
 | 
										return
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								tm.PodUpdated(pod, nil)
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("unable to add pod event handler: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, err = nodeInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
 | 
				
			||||||
 | 
							AddFunc: controllerutil.CreateAddNodeHandler(func(node *v1.Node) error {
 | 
				
			||||||
 | 
								tm.NodeUpdated(nil, node)
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}),
 | 
				
			||||||
 | 
							UpdateFunc: controllerutil.CreateUpdateNodeHandler(func(oldNode, newNode *v1.Node) error {
 | 
				
			||||||
 | 
								tm.NodeUpdated(oldNode, newNode)
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}),
 | 
				
			||||||
 | 
							DeleteFunc: controllerutil.CreateDeleteNodeHandler(logger, func(node *v1.Node) error {
 | 
				
			||||||
 | 
								tm.NodeUpdated(node, nil)
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}),
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("unable to add node event handler: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return tm, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Run starts NoExecuteTaintManager which will run in loop until `stopCh` is closed.
 | 
					// Run starts the controller which will run in loop until `stopCh` is closed.
 | 
				
			||||||
func (tc *NoExecuteTaintManager) Run(ctx context.Context) {
 | 
					func (tc *Controller) Run(ctx context.Context) {
 | 
				
			||||||
	defer utilruntime.HandleCrash()
 | 
						defer utilruntime.HandleCrash()
 | 
				
			||||||
	logger := klog.FromContext(ctx)
 | 
						logger := klog.FromContext(ctx)
 | 
				
			||||||
	logger.Info("Starting NoExecuteTaintManager")
 | 
						logger.Info("Starting", "controller", tc.name)
 | 
				
			||||||
 | 
						defer logger.Info("Shutting down controller", "controller", tc.name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Start events processing pipeline.
 | 
						// Start events processing pipeline.
 | 
				
			||||||
	tc.broadcaster.StartStructuredLogging(0)
 | 
						tc.broadcaster.StartStructuredLogging(0)
 | 
				
			||||||
@@ -210,14 +291,18 @@ func (tc *NoExecuteTaintManager) Run(ctx context.Context) {
 | 
				
			|||||||
		logger.Info("Sending events to api server")
 | 
							logger.Info("Sending events to api server")
 | 
				
			||||||
		tc.broadcaster.StartRecordingToSink(&v1core.EventSinkImpl{Interface: tc.client.CoreV1().Events("")})
 | 
							tc.broadcaster.StartRecordingToSink(&v1core.EventSinkImpl{Interface: tc.client.CoreV1().Events("")})
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		logger.Error(nil, "kubeClient is nil when starting NodeController")
 | 
							logger.Error(nil, "kubeClient is nil", "controller", tc.name)
 | 
				
			||||||
		klog.FlushAndExit(klog.ExitFlushTimeout, 1)
 | 
							klog.FlushAndExit(klog.ExitFlushTimeout, 1)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	defer tc.broadcaster.Shutdown()
 | 
						defer tc.broadcaster.Shutdown()
 | 
				
			||||||
 | 
					 | 
				
			||||||
	defer tc.nodeUpdateQueue.ShutDown()
 | 
						defer tc.nodeUpdateQueue.ShutDown()
 | 
				
			||||||
	defer tc.podUpdateQueue.ShutDown()
 | 
						defer tc.podUpdateQueue.ShutDown()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// wait for the cache to be synced
 | 
				
			||||||
 | 
						if !cache.WaitForNamedCacheSync(tc.name, ctx.Done(), tc.podListerSynced, tc.nodeListerSynced) {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for i := 0; i < UpdateWorkerSize; i++ {
 | 
						for i := 0; i < UpdateWorkerSize; i++ {
 | 
				
			||||||
		tc.nodeUpdateChannels = append(tc.nodeUpdateChannels, make(chan nodeUpdateItem, NodeUpdateChannelSize))
 | 
							tc.nodeUpdateChannels = append(tc.nodeUpdateChannels, make(chan nodeUpdateItem, NodeUpdateChannelSize))
 | 
				
			||||||
		tc.podUpdateChannels = append(tc.podUpdateChannels, make(chan podUpdateItem, podUpdateChannelSize))
 | 
							tc.podUpdateChannels = append(tc.podUpdateChannels, make(chan podUpdateItem, podUpdateChannelSize))
 | 
				
			||||||
@@ -273,11 +358,11 @@ func (tc *NoExecuteTaintManager) Run(ctx context.Context) {
 | 
				
			|||||||
	wg.Wait()
 | 
						wg.Wait()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (tc *NoExecuteTaintManager) worker(ctx context.Context, worker int, done func(), stopCh <-chan struct{}) {
 | 
					func (tc *Controller) worker(ctx context.Context, worker int, done func(), stopCh <-chan struct{}) {
 | 
				
			||||||
	defer done()
 | 
						defer done()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// When processing events we want to prioritize Node updates over Pod updates,
 | 
						// When processing events we want to prioritize Node updates over Pod updates,
 | 
				
			||||||
	// as NodeUpdates that interest NoExecuteTaintManager should be handled as soon as possible -
 | 
						// as NodeUpdates that interest the controller should be handled as soon as possible -
 | 
				
			||||||
	// we don't want user (or system) to wait until PodUpdate queue is drained before it can
 | 
						// we don't want user (or system) to wait until PodUpdate queue is drained before it can
 | 
				
			||||||
	// start evicting Pods from tainted Nodes.
 | 
						// start evicting Pods from tainted Nodes.
 | 
				
			||||||
	for {
 | 
						for {
 | 
				
			||||||
@@ -307,7 +392,7 @@ func (tc *NoExecuteTaintManager) worker(ctx context.Context, worker int, done fu
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// PodUpdated is used to notify NoExecuteTaintManager about Pod changes.
 | 
					// PodUpdated is used to notify NoExecuteTaintManager about Pod changes.
 | 
				
			||||||
func (tc *NoExecuteTaintManager) PodUpdated(oldPod *v1.Pod, newPod *v1.Pod) {
 | 
					func (tc *Controller) PodUpdated(oldPod *v1.Pod, newPod *v1.Pod) {
 | 
				
			||||||
	podName := ""
 | 
						podName := ""
 | 
				
			||||||
	podNamespace := ""
 | 
						podNamespace := ""
 | 
				
			||||||
	nodeName := ""
 | 
						nodeName := ""
 | 
				
			||||||
@@ -339,7 +424,7 @@ func (tc *NoExecuteTaintManager) PodUpdated(oldPod *v1.Pod, newPod *v1.Pod) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NodeUpdated is used to notify NoExecuteTaintManager about Node changes.
 | 
					// NodeUpdated is used to notify NoExecuteTaintManager about Node changes.
 | 
				
			||||||
func (tc *NoExecuteTaintManager) NodeUpdated(oldNode *v1.Node, newNode *v1.Node) {
 | 
					func (tc *Controller) NodeUpdated(oldNode *v1.Node, newNode *v1.Node) {
 | 
				
			||||||
	nodeName := ""
 | 
						nodeName := ""
 | 
				
			||||||
	oldTaints := []v1.Taint{}
 | 
						oldTaints := []v1.Taint{}
 | 
				
			||||||
	if oldNode != nil {
 | 
						if oldNode != nil {
 | 
				
			||||||
@@ -363,13 +448,13 @@ func (tc *NoExecuteTaintManager) NodeUpdated(oldNode *v1.Node, newNode *v1.Node)
 | 
				
			|||||||
	tc.nodeUpdateQueue.Add(updateItem)
 | 
						tc.nodeUpdateQueue.Add(updateItem)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (tc *NoExecuteTaintManager) cancelWorkWithEvent(logger klog.Logger, nsName types.NamespacedName) {
 | 
					func (tc *Controller) cancelWorkWithEvent(logger klog.Logger, nsName types.NamespacedName) {
 | 
				
			||||||
	if tc.taintEvictionQueue.CancelWork(logger, nsName.String()) {
 | 
						if tc.taintEvictionQueue.CancelWork(logger, nsName.String()) {
 | 
				
			||||||
		tc.emitCancelPodDeletionEvent(nsName)
 | 
							tc.emitCancelPodDeletionEvent(nsName)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (tc *NoExecuteTaintManager) processPodOnNode(
 | 
					func (tc *Controller) processPodOnNode(
 | 
				
			||||||
	ctx context.Context,
 | 
						ctx context.Context,
 | 
				
			||||||
	podNamespacedName types.NamespacedName,
 | 
						podNamespacedName types.NamespacedName,
 | 
				
			||||||
	nodeName string,
 | 
						nodeName string,
 | 
				
			||||||
@@ -410,7 +495,7 @@ func (tc *NoExecuteTaintManager) processPodOnNode(
 | 
				
			|||||||
	tc.taintEvictionQueue.AddWork(ctx, NewWorkArgs(podNamespacedName.Name, podNamespacedName.Namespace), startTime, triggerTime)
 | 
						tc.taintEvictionQueue.AddWork(ctx, NewWorkArgs(podNamespacedName.Name, podNamespacedName.Namespace), startTime, triggerTime)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (tc *NoExecuteTaintManager) handlePodUpdate(ctx context.Context, podUpdate podUpdateItem) {
 | 
					func (tc *Controller) handlePodUpdate(ctx context.Context, podUpdate podUpdateItem) {
 | 
				
			||||||
	pod, err := tc.podLister.Pods(podUpdate.podNamespace).Get(podUpdate.podName)
 | 
						pod, err := tc.podLister.Pods(podUpdate.podNamespace).Get(podUpdate.podName)
 | 
				
			||||||
	logger := klog.FromContext(ctx)
 | 
						logger := klog.FromContext(ctx)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@@ -451,7 +536,7 @@ func (tc *NoExecuteTaintManager) handlePodUpdate(ctx context.Context, podUpdate
 | 
				
			|||||||
	tc.processPodOnNode(ctx, podNamespacedName, nodeName, pod.Spec.Tolerations, taints, time.Now())
 | 
						tc.processPodOnNode(ctx, podNamespacedName, nodeName, pod.Spec.Tolerations, taints, time.Now())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (tc *NoExecuteTaintManager) handleNodeUpdate(ctx context.Context, nodeUpdate nodeUpdateItem) {
 | 
					func (tc *Controller) handleNodeUpdate(ctx context.Context, nodeUpdate nodeUpdateItem) {
 | 
				
			||||||
	node, err := tc.nodeLister.Get(nodeUpdate.nodeName)
 | 
						node, err := tc.nodeLister.Get(nodeUpdate.nodeName)
 | 
				
			||||||
	logger := klog.FromContext(ctx)
 | 
						logger := klog.FromContext(ctx)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@@ -508,7 +593,7 @@ func (tc *NoExecuteTaintManager) handleNodeUpdate(ctx context.Context, nodeUpdat
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (tc *NoExecuteTaintManager) emitPodDeletionEvent(nsName types.NamespacedName) {
 | 
					func (tc *Controller) emitPodDeletionEvent(nsName types.NamespacedName) {
 | 
				
			||||||
	if tc.recorder == nil {
 | 
						if tc.recorder == nil {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -521,7 +606,7 @@ func (tc *NoExecuteTaintManager) emitPodDeletionEvent(nsName types.NamespacedNam
 | 
				
			|||||||
	tc.recorder.Eventf(ref, v1.EventTypeNormal, "TaintManagerEviction", "Marking for deletion Pod %s", nsName.String())
 | 
						tc.recorder.Eventf(ref, v1.EventTypeNormal, "TaintManagerEviction", "Marking for deletion Pod %s", nsName.String())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (tc *NoExecuteTaintManager) emitCancelPodDeletionEvent(nsName types.NamespacedName) {
 | 
					func (tc *Controller) emitCancelPodDeletionEvent(nsName types.NamespacedName) {
 | 
				
			||||||
	if tc.recorder == nil {
 | 
						if tc.recorder == nil {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
 | 
				
			|||||||
limitations under the License.
 | 
					limitations under the License.
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package scheduler
 | 
					package tainteviction
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
@@ -25,7 +25,7 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	"github.com/google/go-cmp/cmp"
 | 
						"github.com/google/go-cmp/cmp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	v1 "k8s.io/api/core/v1"
 | 
						corev1 "k8s.io/api/core/v1"
 | 
				
			||||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
						metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/fields"
 | 
						"k8s.io/apimachinery/pkg/fields"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/labels"
 | 
						"k8s.io/apimachinery/pkg/labels"
 | 
				
			||||||
@@ -44,16 +44,16 @@ import (
 | 
				
			|||||||
var timeForControllerToProgressForSanityCheck = 20 * time.Millisecond
 | 
					var timeForControllerToProgressForSanityCheck = 20 * time.Millisecond
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func getPodsAssignedToNode(ctx context.Context, c *fake.Clientset) GetPodsByNodeNameFunc {
 | 
					func getPodsAssignedToNode(ctx context.Context, c *fake.Clientset) GetPodsByNodeNameFunc {
 | 
				
			||||||
	return func(nodeName string) ([]*v1.Pod, error) {
 | 
						return func(nodeName string) ([]*corev1.Pod, error) {
 | 
				
			||||||
		selector := fields.SelectorFromSet(fields.Set{"spec.nodeName": nodeName})
 | 
							selector := fields.SelectorFromSet(fields.Set{"spec.nodeName": nodeName})
 | 
				
			||||||
		pods, err := c.CoreV1().Pods(v1.NamespaceAll).List(ctx, metav1.ListOptions{
 | 
							pods, err := c.CoreV1().Pods(corev1.NamespaceAll).List(ctx, metav1.ListOptions{
 | 
				
			||||||
			FieldSelector: selector.String(),
 | 
								FieldSelector: selector.String(),
 | 
				
			||||||
			LabelSelector: labels.Everything().String(),
 | 
								LabelSelector: labels.Everything().String(),
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return []*v1.Pod{}, fmt.Errorf("failed to get Pods assigned to node %v", nodeName)
 | 
								return []*corev1.Pod{}, fmt.Errorf("failed to get Pods assigned to node %v", nodeName)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		rPods := make([]*v1.Pod, len(pods.Items))
 | 
							rPods := make([]*corev1.Pod, len(pods.Items))
 | 
				
			||||||
		for i := range pods.Items {
 | 
							for i := range pods.Items {
 | 
				
			||||||
			rPods[i] = &pods.Items[i]
 | 
								rPods[i] = &pods.Items[i]
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -61,31 +61,31 @@ func getPodsAssignedToNode(ctx context.Context, c *fake.Clientset) GetPodsByNode
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func createNoExecuteTaint(index int) v1.Taint {
 | 
					func createNoExecuteTaint(index int) corev1.Taint {
 | 
				
			||||||
	now := metav1.Now()
 | 
						now := metav1.Now()
 | 
				
			||||||
	return v1.Taint{
 | 
						return corev1.Taint{
 | 
				
			||||||
		Key:       "testTaint" + fmt.Sprintf("%v", index),
 | 
							Key:       "testTaint" + fmt.Sprintf("%v", index),
 | 
				
			||||||
		Value:     "test" + fmt.Sprintf("%v", index),
 | 
							Value:     "test" + fmt.Sprintf("%v", index),
 | 
				
			||||||
		Effect:    v1.TaintEffectNoExecute,
 | 
							Effect:    corev1.TaintEffectNoExecute,
 | 
				
			||||||
		TimeAdded: &now,
 | 
							TimeAdded: &now,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func addToleration(pod *v1.Pod, index int, duration int64) *v1.Pod {
 | 
					func addToleration(pod *corev1.Pod, index int, duration int64) *corev1.Pod {
 | 
				
			||||||
	if pod.Annotations == nil {
 | 
						if pod.Annotations == nil {
 | 
				
			||||||
		pod.Annotations = map[string]string{}
 | 
							pod.Annotations = map[string]string{}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if duration < 0 {
 | 
						if duration < 0 {
 | 
				
			||||||
		pod.Spec.Tolerations = []v1.Toleration{{Key: "testTaint" + fmt.Sprintf("%v", index), Value: "test" + fmt.Sprintf("%v", index), Effect: v1.TaintEffectNoExecute}}
 | 
							pod.Spec.Tolerations = []corev1.Toleration{{Key: "testTaint" + fmt.Sprintf("%v", index), Value: "test" + fmt.Sprintf("%v", index), Effect: corev1.TaintEffectNoExecute}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		pod.Spec.Tolerations = []v1.Toleration{{Key: "testTaint" + fmt.Sprintf("%v", index), Value: "test" + fmt.Sprintf("%v", index), Effect: v1.TaintEffectNoExecute, TolerationSeconds: &duration}}
 | 
							pod.Spec.Tolerations = []corev1.Toleration{{Key: "testTaint" + fmt.Sprintf("%v", index), Value: "test" + fmt.Sprintf("%v", index), Effect: corev1.TaintEffectNoExecute, TolerationSeconds: &duration}}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return pod
 | 
						return pod
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func addTaintsToNode(node *v1.Node, key, value string, indices []int) *v1.Node {
 | 
					func addTaintsToNode(node *corev1.Node, key, value string, indices []int) *corev1.Node {
 | 
				
			||||||
	taints := []v1.Taint{}
 | 
						taints := []corev1.Taint{}
 | 
				
			||||||
	for _, index := range indices {
 | 
						for _, index := range indices {
 | 
				
			||||||
		taints = append(taints, createNoExecuteTaint(index))
 | 
							taints = append(taints, createNoExecuteTaint(index))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -93,11 +93,16 @@ func addTaintsToNode(node *v1.Node, key, value string, indices []int) *v1.Node {
 | 
				
			|||||||
	return node
 | 
						return node
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func setupNewNoExecuteTaintManager(ctx context.Context, fakeClientSet *fake.Clientset) (*NoExecuteTaintManager, cache.Indexer, cache.Indexer) {
 | 
					var alwaysReady = func() bool { return true }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func setupNewController(ctx context.Context, fakeClientSet *fake.Clientset) (*Controller, cache.Indexer, cache.Indexer) {
 | 
				
			||||||
	informerFactory := informers.NewSharedInformerFactory(fakeClientSet, 0)
 | 
						informerFactory := informers.NewSharedInformerFactory(fakeClientSet, 0)
 | 
				
			||||||
	podIndexer := informerFactory.Core().V1().Pods().Informer().GetIndexer()
 | 
						podIndexer := informerFactory.Core().V1().Pods().Informer().GetIndexer()
 | 
				
			||||||
	nodeIndexer := informerFactory.Core().V1().Nodes().Informer().GetIndexer()
 | 
						nodeIndexer := informerFactory.Core().V1().Nodes().Informer().GetIndexer()
 | 
				
			||||||
	mgr := NewNoExecuteTaintManager(ctx, fakeClientSet, informerFactory.Core().V1().Pods().Lister(), informerFactory.Core().V1().Nodes().Lister(), getPodsAssignedToNode(ctx, fakeClientSet))
 | 
						mgr, _ := New(ctx, fakeClientSet, informerFactory.Core().V1().Pods(), informerFactory.Core().V1().Nodes(), "taint-eviction-controller")
 | 
				
			||||||
 | 
						mgr.podListerSynced = alwaysReady
 | 
				
			||||||
 | 
						mgr.nodeListerSynced = alwaysReady
 | 
				
			||||||
 | 
						mgr.getPodsAssignedToNode = getPodsAssignedToNode(ctx, fakeClientSet)
 | 
				
			||||||
	return mgr, podIndexer, nodeIndexer
 | 
						return mgr, podIndexer, nodeIndexer
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -113,16 +118,16 @@ func (a durationSlice) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
 | 
				
			|||||||
func (a durationSlice) Less(i, j int) bool { return a[i].timestamp < a[j].timestamp }
 | 
					func (a durationSlice) Less(i, j int) bool { return a[i].timestamp < a[j].timestamp }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestFilterNoExecuteTaints(t *testing.T) {
 | 
					func TestFilterNoExecuteTaints(t *testing.T) {
 | 
				
			||||||
	taints := []v1.Taint{
 | 
						taints := []corev1.Taint{
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			Key:    "one",
 | 
								Key:    "one",
 | 
				
			||||||
			Value:  "one",
 | 
								Value:  "one",
 | 
				
			||||||
			Effect: v1.TaintEffectNoExecute,
 | 
								Effect: corev1.TaintEffectNoExecute,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			Key:    "two",
 | 
								Key:    "two",
 | 
				
			||||||
			Value:  "two",
 | 
								Value:  "two",
 | 
				
			||||||
			Effect: v1.TaintEffectNoSchedule,
 | 
								Effect: corev1.TaintEffectNoSchedule,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	taints = getNoExecuteTaints(taints)
 | 
						taints = getNoExecuteTaints(taints)
 | 
				
			||||||
@@ -134,8 +139,8 @@ func TestFilterNoExecuteTaints(t *testing.T) {
 | 
				
			|||||||
func TestCreatePod(t *testing.T) {
 | 
					func TestCreatePod(t *testing.T) {
 | 
				
			||||||
	testCases := []struct {
 | 
						testCases := []struct {
 | 
				
			||||||
		description                   string
 | 
							description                   string
 | 
				
			||||||
		pod                           *v1.Pod
 | 
							pod                           *corev1.Pod
 | 
				
			||||||
		taintedNodes                  map[string][]v1.Taint
 | 
							taintedNodes                  map[string][]corev1.Taint
 | 
				
			||||||
		expectPatch                   bool
 | 
							expectPatch                   bool
 | 
				
			||||||
		expectDelete                  bool
 | 
							expectDelete                  bool
 | 
				
			||||||
		enablePodDisruptionConditions bool
 | 
							enablePodDisruptionConditions bool
 | 
				
			||||||
@@ -143,19 +148,19 @@ func TestCreatePod(t *testing.T) {
 | 
				
			|||||||
		{
 | 
							{
 | 
				
			||||||
			description:  "not scheduled - ignore",
 | 
								description:  "not scheduled - ignore",
 | 
				
			||||||
			pod:          testutil.NewPod("pod1", ""),
 | 
								pod:          testutil.NewPod("pod1", ""),
 | 
				
			||||||
			taintedNodes: map[string][]v1.Taint{},
 | 
								taintedNodes: map[string][]corev1.Taint{},
 | 
				
			||||||
			expectDelete: false,
 | 
								expectDelete: false,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			description:  "scheduled on untainted Node",
 | 
								description:  "scheduled on untainted Node",
 | 
				
			||||||
			pod:          testutil.NewPod("pod1", "node1"),
 | 
								pod:          testutil.NewPod("pod1", "node1"),
 | 
				
			||||||
			taintedNodes: map[string][]v1.Taint{},
 | 
								taintedNodes: map[string][]corev1.Taint{},
 | 
				
			||||||
			expectDelete: false,
 | 
								expectDelete: false,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			description: "schedule on tainted Node",
 | 
								description: "schedule on tainted Node",
 | 
				
			||||||
			pod:         testutil.NewPod("pod1", "node1"),
 | 
								pod:         testutil.NewPod("pod1", "node1"),
 | 
				
			||||||
			taintedNodes: map[string][]v1.Taint{
 | 
								taintedNodes: map[string][]corev1.Taint{
 | 
				
			||||||
				"node1": {createNoExecuteTaint(1)},
 | 
									"node1": {createNoExecuteTaint(1)},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			expectDelete: true,
 | 
								expectDelete: true,
 | 
				
			||||||
@@ -163,7 +168,7 @@ func TestCreatePod(t *testing.T) {
 | 
				
			|||||||
		{
 | 
							{
 | 
				
			||||||
			description: "schedule on tainted Node; PodDisruptionConditions enabled",
 | 
								description: "schedule on tainted Node; PodDisruptionConditions enabled",
 | 
				
			||||||
			pod:         testutil.NewPod("pod1", "node1"),
 | 
								pod:         testutil.NewPod("pod1", "node1"),
 | 
				
			||||||
			taintedNodes: map[string][]v1.Taint{
 | 
								taintedNodes: map[string][]corev1.Taint{
 | 
				
			||||||
				"node1": {createNoExecuteTaint(1)},
 | 
									"node1": {createNoExecuteTaint(1)},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			expectPatch:                   true,
 | 
								expectPatch:                   true,
 | 
				
			||||||
@@ -173,7 +178,7 @@ func TestCreatePod(t *testing.T) {
 | 
				
			|||||||
		{
 | 
							{
 | 
				
			||||||
			description: "schedule on tainted Node with finite toleration",
 | 
								description: "schedule on tainted Node with finite toleration",
 | 
				
			||||||
			pod:         addToleration(testutil.NewPod("pod1", "node1"), 1, 100),
 | 
								pod:         addToleration(testutil.NewPod("pod1", "node1"), 1, 100),
 | 
				
			||||||
			taintedNodes: map[string][]v1.Taint{
 | 
								taintedNodes: map[string][]corev1.Taint{
 | 
				
			||||||
				"node1": {createNoExecuteTaint(1)},
 | 
									"node1": {createNoExecuteTaint(1)},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			expectDelete: false,
 | 
								expectDelete: false,
 | 
				
			||||||
@@ -181,7 +186,7 @@ func TestCreatePod(t *testing.T) {
 | 
				
			|||||||
		{
 | 
							{
 | 
				
			||||||
			description: "schedule on tainted Node with infinite toleration",
 | 
								description: "schedule on tainted Node with infinite toleration",
 | 
				
			||||||
			pod:         addToleration(testutil.NewPod("pod1", "node1"), 1, -1),
 | 
								pod:         addToleration(testutil.NewPod("pod1", "node1"), 1, -1),
 | 
				
			||||||
			taintedNodes: map[string][]v1.Taint{
 | 
								taintedNodes: map[string][]corev1.Taint{
 | 
				
			||||||
				"node1": {createNoExecuteTaint(1)},
 | 
									"node1": {createNoExecuteTaint(1)},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			expectDelete: false,
 | 
								expectDelete: false,
 | 
				
			||||||
@@ -189,7 +194,7 @@ func TestCreatePod(t *testing.T) {
 | 
				
			|||||||
		{
 | 
							{
 | 
				
			||||||
			description: "schedule on tainted Node with infinite ivalid toleration",
 | 
								description: "schedule on tainted Node with infinite ivalid toleration",
 | 
				
			||||||
			pod:         addToleration(testutil.NewPod("pod1", "node1"), 2, -1),
 | 
								pod:         addToleration(testutil.NewPod("pod1", "node1"), 2, -1),
 | 
				
			||||||
			taintedNodes: map[string][]v1.Taint{
 | 
								taintedNodes: map[string][]corev1.Taint{
 | 
				
			||||||
				"node1": {createNoExecuteTaint(1)},
 | 
									"node1": {createNoExecuteTaint(1)},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			expectDelete: true,
 | 
								expectDelete: true,
 | 
				
			||||||
@@ -200,8 +205,8 @@ func TestCreatePod(t *testing.T) {
 | 
				
			|||||||
		t.Run(item.description, func(t *testing.T) {
 | 
							t.Run(item.description, func(t *testing.T) {
 | 
				
			||||||
			defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.PodDisruptionConditions, item.enablePodDisruptionConditions)()
 | 
								defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.PodDisruptionConditions, item.enablePodDisruptionConditions)()
 | 
				
			||||||
			ctx, cancel := context.WithCancel(context.Background())
 | 
								ctx, cancel := context.WithCancel(context.Background())
 | 
				
			||||||
			fakeClientset := fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*item.pod}})
 | 
								fakeClientset := fake.NewSimpleClientset(&corev1.PodList{Items: []corev1.Pod{*item.pod}})
 | 
				
			||||||
			controller, podIndexer, _ := setupNewNoExecuteTaintManager(ctx, fakeClientset)
 | 
								controller, podIndexer, _ := setupNewController(ctx, fakeClientset)
 | 
				
			||||||
			controller.recorder = testutil.NewFakeRecorder()
 | 
								controller.recorder = testutil.NewFakeRecorder()
 | 
				
			||||||
			go controller.Run(ctx)
 | 
								go controller.Run(ctx)
 | 
				
			||||||
			controller.taintedNodes = item.taintedNodes
 | 
								controller.taintedNodes = item.taintedNodes
 | 
				
			||||||
@@ -221,10 +226,10 @@ func TestDeletePod(t *testing.T) {
 | 
				
			|||||||
	defer cancel()
 | 
						defer cancel()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fakeClientset := fake.NewSimpleClientset()
 | 
						fakeClientset := fake.NewSimpleClientset()
 | 
				
			||||||
	controller, _, _ := setupNewNoExecuteTaintManager(ctx, fakeClientset)
 | 
						controller, _, _ := setupNewController(ctx, fakeClientset)
 | 
				
			||||||
	controller.recorder = testutil.NewFakeRecorder()
 | 
						controller.recorder = testutil.NewFakeRecorder()
 | 
				
			||||||
	go controller.Run(ctx)
 | 
						go controller.Run(ctx)
 | 
				
			||||||
	controller.taintedNodes = map[string][]v1.Taint{
 | 
						controller.taintedNodes = map[string][]corev1.Taint{
 | 
				
			||||||
		"node1": {createNoExecuteTaint(1)},
 | 
							"node1": {createNoExecuteTaint(1)},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	controller.PodUpdated(testutil.NewPod("pod1", "node1"), nil)
 | 
						controller.PodUpdated(testutil.NewPod("pod1", "node1"), nil)
 | 
				
			||||||
@@ -235,10 +240,10 @@ func TestDeletePod(t *testing.T) {
 | 
				
			|||||||
func TestUpdatePod(t *testing.T) {
 | 
					func TestUpdatePod(t *testing.T) {
 | 
				
			||||||
	testCases := []struct {
 | 
						testCases := []struct {
 | 
				
			||||||
		description                   string
 | 
							description                   string
 | 
				
			||||||
		prevPod                       *v1.Pod
 | 
							prevPod                       *corev1.Pod
 | 
				
			||||||
		awaitForScheduledEviction     bool
 | 
							awaitForScheduledEviction     bool
 | 
				
			||||||
		newPod                        *v1.Pod
 | 
							newPod                        *corev1.Pod
 | 
				
			||||||
		taintedNodes                  map[string][]v1.Taint
 | 
							taintedNodes                  map[string][]corev1.Taint
 | 
				
			||||||
		expectPatch                   bool
 | 
							expectPatch                   bool
 | 
				
			||||||
		expectDelete                  bool
 | 
							expectDelete                  bool
 | 
				
			||||||
		enablePodDisruptionConditions bool
 | 
							enablePodDisruptionConditions bool
 | 
				
			||||||
@@ -247,7 +252,7 @@ func TestUpdatePod(t *testing.T) {
 | 
				
			|||||||
			description: "scheduling onto tainted Node results in patch and delete when PodDisruptionConditions enabled",
 | 
								description: "scheduling onto tainted Node results in patch and delete when PodDisruptionConditions enabled",
 | 
				
			||||||
			prevPod:     testutil.NewPod("pod1", ""),
 | 
								prevPod:     testutil.NewPod("pod1", ""),
 | 
				
			||||||
			newPod:      testutil.NewPod("pod1", "node1"),
 | 
								newPod:      testutil.NewPod("pod1", "node1"),
 | 
				
			||||||
			taintedNodes: map[string][]v1.Taint{
 | 
								taintedNodes: map[string][]corev1.Taint{
 | 
				
			||||||
				"node1": {createNoExecuteTaint(1)},
 | 
									"node1": {createNoExecuteTaint(1)},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			expectPatch:                   true,
 | 
								expectPatch:                   true,
 | 
				
			||||||
@@ -258,7 +263,7 @@ func TestUpdatePod(t *testing.T) {
 | 
				
			|||||||
			description: "scheduling onto tainted Node",
 | 
								description: "scheduling onto tainted Node",
 | 
				
			||||||
			prevPod:     testutil.NewPod("pod1", ""),
 | 
								prevPod:     testutil.NewPod("pod1", ""),
 | 
				
			||||||
			newPod:      testutil.NewPod("pod1", "node1"),
 | 
								newPod:      testutil.NewPod("pod1", "node1"),
 | 
				
			||||||
			taintedNodes: map[string][]v1.Taint{
 | 
								taintedNodes: map[string][]corev1.Taint{
 | 
				
			||||||
				"node1": {createNoExecuteTaint(1)},
 | 
									"node1": {createNoExecuteTaint(1)},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			expectDelete: true,
 | 
								expectDelete: true,
 | 
				
			||||||
@@ -267,7 +272,7 @@ func TestUpdatePod(t *testing.T) {
 | 
				
			|||||||
			description: "scheduling onto tainted Node with toleration",
 | 
								description: "scheduling onto tainted Node with toleration",
 | 
				
			||||||
			prevPod:     addToleration(testutil.NewPod("pod1", ""), 1, -1),
 | 
								prevPod:     addToleration(testutil.NewPod("pod1", ""), 1, -1),
 | 
				
			||||||
			newPod:      addToleration(testutil.NewPod("pod1", "node1"), 1, -1),
 | 
								newPod:      addToleration(testutil.NewPod("pod1", "node1"), 1, -1),
 | 
				
			||||||
			taintedNodes: map[string][]v1.Taint{
 | 
								taintedNodes: map[string][]corev1.Taint{
 | 
				
			||||||
				"node1": {createNoExecuteTaint(1)},
 | 
									"node1": {createNoExecuteTaint(1)},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			expectDelete: false,
 | 
								expectDelete: false,
 | 
				
			||||||
@@ -277,7 +282,7 @@ func TestUpdatePod(t *testing.T) {
 | 
				
			|||||||
			prevPod:                   addToleration(testutil.NewPod("pod1", "node1"), 1, 100),
 | 
								prevPod:                   addToleration(testutil.NewPod("pod1", "node1"), 1, 100),
 | 
				
			||||||
			newPod:                    testutil.NewPod("pod1", "node1"),
 | 
								newPod:                    testutil.NewPod("pod1", "node1"),
 | 
				
			||||||
			awaitForScheduledEviction: true,
 | 
								awaitForScheduledEviction: true,
 | 
				
			||||||
			taintedNodes: map[string][]v1.Taint{
 | 
								taintedNodes: map[string][]corev1.Taint{
 | 
				
			||||||
				"node1": {createNoExecuteTaint(1)},
 | 
									"node1": {createNoExecuteTaint(1)},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			expectDelete: true,
 | 
								expectDelete: true,
 | 
				
			||||||
@@ -287,7 +292,7 @@ func TestUpdatePod(t *testing.T) {
 | 
				
			|||||||
			prevPod:                   addToleration(testutil.NewPod("pod1", "node1"), 1, 1),
 | 
								prevPod:                   addToleration(testutil.NewPod("pod1", "node1"), 1, 1),
 | 
				
			||||||
			newPod:                    addToleration(testutil.NewPod("pod1", "node1"), 1, 100),
 | 
								newPod:                    addToleration(testutil.NewPod("pod1", "node1"), 1, 100),
 | 
				
			||||||
			awaitForScheduledEviction: true,
 | 
								awaitForScheduledEviction: true,
 | 
				
			||||||
			taintedNodes: map[string][]v1.Taint{
 | 
								taintedNodes: map[string][]corev1.Taint{
 | 
				
			||||||
				"node1": {createNoExecuteTaint(1)},
 | 
									"node1": {createNoExecuteTaint(1)},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			expectDelete: true,
 | 
								expectDelete: true,
 | 
				
			||||||
@@ -298,8 +303,8 @@ func TestUpdatePod(t *testing.T) {
 | 
				
			|||||||
		t.Run(item.description, func(t *testing.T) {
 | 
							t.Run(item.description, func(t *testing.T) {
 | 
				
			||||||
			defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.PodDisruptionConditions, item.enablePodDisruptionConditions)()
 | 
								defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.PodDisruptionConditions, item.enablePodDisruptionConditions)()
 | 
				
			||||||
			ctx, cancel := context.WithCancel(context.Background())
 | 
								ctx, cancel := context.WithCancel(context.Background())
 | 
				
			||||||
			fakeClientset := fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*item.prevPod}})
 | 
								fakeClientset := fake.NewSimpleClientset(&corev1.PodList{Items: []corev1.Pod{*item.prevPod}})
 | 
				
			||||||
			controller, podIndexer, _ := setupNewNoExecuteTaintManager(context.TODO(), fakeClientset)
 | 
								controller, podIndexer, _ := setupNewController(context.TODO(), fakeClientset)
 | 
				
			||||||
			controller.recorder = testutil.NewFakeRecorder()
 | 
								controller.recorder = testutil.NewFakeRecorder()
 | 
				
			||||||
			controller.taintedNodes = item.taintedNodes
 | 
								controller.taintedNodes = item.taintedNodes
 | 
				
			||||||
			go controller.Run(ctx)
 | 
								go controller.Run(ctx)
 | 
				
			||||||
@@ -330,14 +335,14 @@ func TestUpdatePod(t *testing.T) {
 | 
				
			|||||||
func TestCreateNode(t *testing.T) {
 | 
					func TestCreateNode(t *testing.T) {
 | 
				
			||||||
	testCases := []struct {
 | 
						testCases := []struct {
 | 
				
			||||||
		description  string
 | 
							description  string
 | 
				
			||||||
		pods         []v1.Pod
 | 
							pods         []corev1.Pod
 | 
				
			||||||
		node         *v1.Node
 | 
							node         *corev1.Node
 | 
				
			||||||
		expectPatch  bool
 | 
							expectPatch  bool
 | 
				
			||||||
		expectDelete bool
 | 
							expectDelete bool
 | 
				
			||||||
	}{
 | 
						}{
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			description: "Creating Node matching already assigned Pod",
 | 
								description: "Creating Node matching already assigned Pod",
 | 
				
			||||||
			pods: []v1.Pod{
 | 
								pods: []corev1.Pod{
 | 
				
			||||||
				*testutil.NewPod("pod1", "node1"),
 | 
									*testutil.NewPod("pod1", "node1"),
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			node:         testutil.NewNode("node1"),
 | 
								node:         testutil.NewNode("node1"),
 | 
				
			||||||
@@ -346,7 +351,7 @@ func TestCreateNode(t *testing.T) {
 | 
				
			|||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			description: "Creating tainted Node matching already assigned Pod",
 | 
								description: "Creating tainted Node matching already assigned Pod",
 | 
				
			||||||
			pods: []v1.Pod{
 | 
								pods: []corev1.Pod{
 | 
				
			||||||
				*testutil.NewPod("pod1", "node1"),
 | 
									*testutil.NewPod("pod1", "node1"),
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			node:         addTaintsToNode(testutil.NewNode("node1"), "testTaint1", "taint1", []int{1}),
 | 
								node:         addTaintsToNode(testutil.NewNode("node1"), "testTaint1", "taint1", []int{1}),
 | 
				
			||||||
@@ -355,7 +360,7 @@ func TestCreateNode(t *testing.T) {
 | 
				
			|||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			description: "Creating tainted Node matching already assigned tolerating Pod",
 | 
								description: "Creating tainted Node matching already assigned tolerating Pod",
 | 
				
			||||||
			pods: []v1.Pod{
 | 
								pods: []corev1.Pod{
 | 
				
			||||||
				*addToleration(testutil.NewPod("pod1", "node1"), 1, -1),
 | 
									*addToleration(testutil.NewPod("pod1", "node1"), 1, -1),
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			node:         addTaintsToNode(testutil.NewNode("node1"), "testTaint1", "taint1", []int{1}),
 | 
								node:         addTaintsToNode(testutil.NewNode("node1"), "testTaint1", "taint1", []int{1}),
 | 
				
			||||||
@@ -366,8 +371,8 @@ func TestCreateNode(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	for _, item := range testCases {
 | 
						for _, item := range testCases {
 | 
				
			||||||
		ctx, cancel := context.WithCancel(context.Background())
 | 
							ctx, cancel := context.WithCancel(context.Background())
 | 
				
			||||||
		fakeClientset := fake.NewSimpleClientset(&v1.PodList{Items: item.pods})
 | 
							fakeClientset := fake.NewSimpleClientset(&corev1.PodList{Items: item.pods})
 | 
				
			||||||
		controller, _, nodeIndexer := setupNewNoExecuteTaintManager(ctx, fakeClientset)
 | 
							controller, _, nodeIndexer := setupNewController(ctx, fakeClientset)
 | 
				
			||||||
		nodeIndexer.Add(item.node)
 | 
							nodeIndexer.Add(item.node)
 | 
				
			||||||
		controller.recorder = testutil.NewFakeRecorder()
 | 
							controller.recorder = testutil.NewFakeRecorder()
 | 
				
			||||||
		go controller.Run(ctx)
 | 
							go controller.Run(ctx)
 | 
				
			||||||
@@ -382,9 +387,9 @@ func TestCreateNode(t *testing.T) {
 | 
				
			|||||||
func TestDeleteNode(t *testing.T) {
 | 
					func TestDeleteNode(t *testing.T) {
 | 
				
			||||||
	ctx, cancel := context.WithCancel(context.Background())
 | 
						ctx, cancel := context.WithCancel(context.Background())
 | 
				
			||||||
	fakeClientset := fake.NewSimpleClientset()
 | 
						fakeClientset := fake.NewSimpleClientset()
 | 
				
			||||||
	controller, _, _ := setupNewNoExecuteTaintManager(ctx, fakeClientset)
 | 
						controller, _, _ := setupNewController(ctx, fakeClientset)
 | 
				
			||||||
	controller.recorder = testutil.NewFakeRecorder()
 | 
						controller.recorder = testutil.NewFakeRecorder()
 | 
				
			||||||
	controller.taintedNodes = map[string][]v1.Taint{
 | 
						controller.taintedNodes = map[string][]corev1.Taint{
 | 
				
			||||||
		"node1": {createNoExecuteTaint(1)},
 | 
							"node1": {createNoExecuteTaint(1)},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	go controller.Run(ctx)
 | 
						go controller.Run(ctx)
 | 
				
			||||||
@@ -406,9 +411,9 @@ func TestDeleteNode(t *testing.T) {
 | 
				
			|||||||
func TestUpdateNode(t *testing.T) {
 | 
					func TestUpdateNode(t *testing.T) {
 | 
				
			||||||
	testCases := []struct {
 | 
						testCases := []struct {
 | 
				
			||||||
		description                   string
 | 
							description                   string
 | 
				
			||||||
		pods                          []v1.Pod
 | 
							pods                          []corev1.Pod
 | 
				
			||||||
		oldNode                       *v1.Node
 | 
							oldNode                       *corev1.Node
 | 
				
			||||||
		newNode                       *v1.Node
 | 
							newNode                       *corev1.Node
 | 
				
			||||||
		expectPatch                   bool
 | 
							expectPatch                   bool
 | 
				
			||||||
		expectDelete                  bool
 | 
							expectDelete                  bool
 | 
				
			||||||
		additionalSleep               time.Duration
 | 
							additionalSleep               time.Duration
 | 
				
			||||||
@@ -416,7 +421,7 @@ func TestUpdateNode(t *testing.T) {
 | 
				
			|||||||
	}{
 | 
						}{
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			description: "Added taint, expect node patched and deleted when PodDisruptionConditions is enabled",
 | 
								description: "Added taint, expect node patched and deleted when PodDisruptionConditions is enabled",
 | 
				
			||||||
			pods: []v1.Pod{
 | 
								pods: []corev1.Pod{
 | 
				
			||||||
				*testutil.NewPod("pod1", "node1"),
 | 
									*testutil.NewPod("pod1", "node1"),
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			oldNode:                       testutil.NewNode("node1"),
 | 
								oldNode:                       testutil.NewNode("node1"),
 | 
				
			||||||
@@ -427,7 +432,7 @@ func TestUpdateNode(t *testing.T) {
 | 
				
			|||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			description: "Added taint",
 | 
								description: "Added taint",
 | 
				
			||||||
			pods: []v1.Pod{
 | 
								pods: []corev1.Pod{
 | 
				
			||||||
				*testutil.NewPod("pod1", "node1"),
 | 
									*testutil.NewPod("pod1", "node1"),
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			oldNode:      testutil.NewNode("node1"),
 | 
								oldNode:      testutil.NewNode("node1"),
 | 
				
			||||||
@@ -436,7 +441,7 @@ func TestUpdateNode(t *testing.T) {
 | 
				
			|||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			description: "Added tolerated taint",
 | 
								description: "Added tolerated taint",
 | 
				
			||||||
			pods: []v1.Pod{
 | 
								pods: []corev1.Pod{
 | 
				
			||||||
				*addToleration(testutil.NewPod("pod1", "node1"), 1, 100),
 | 
									*addToleration(testutil.NewPod("pod1", "node1"), 1, 100),
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			oldNode:      testutil.NewNode("node1"),
 | 
								oldNode:      testutil.NewNode("node1"),
 | 
				
			||||||
@@ -445,7 +450,7 @@ func TestUpdateNode(t *testing.T) {
 | 
				
			|||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			description: "Only one added taint tolerated",
 | 
								description: "Only one added taint tolerated",
 | 
				
			||||||
			pods: []v1.Pod{
 | 
								pods: []corev1.Pod{
 | 
				
			||||||
				*addToleration(testutil.NewPod("pod1", "node1"), 1, 100),
 | 
									*addToleration(testutil.NewPod("pod1", "node1"), 1, 100),
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			oldNode:      testutil.NewNode("node1"),
 | 
								oldNode:      testutil.NewNode("node1"),
 | 
				
			||||||
@@ -454,7 +459,7 @@ func TestUpdateNode(t *testing.T) {
 | 
				
			|||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			description: "Taint removed",
 | 
								description: "Taint removed",
 | 
				
			||||||
			pods: []v1.Pod{
 | 
								pods: []corev1.Pod{
 | 
				
			||||||
				*addToleration(testutil.NewPod("pod1", "node1"), 1, 1),
 | 
									*addToleration(testutil.NewPod("pod1", "node1"), 1, 1),
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			oldNode:         addTaintsToNode(testutil.NewNode("node1"), "testTaint1", "taint1", []int{1}),
 | 
								oldNode:         addTaintsToNode(testutil.NewNode("node1"), "testTaint1", "taint1", []int{1}),
 | 
				
			||||||
@@ -464,24 +469,24 @@ func TestUpdateNode(t *testing.T) {
 | 
				
			|||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			description: "Pod with multiple tolerations are evicted when first one runs out",
 | 
								description: "Pod with multiple tolerations are evicted when first one runs out",
 | 
				
			||||||
			pods: []v1.Pod{
 | 
								pods: []corev1.Pod{
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
					ObjectMeta: metav1.ObjectMeta{
 | 
										ObjectMeta: metav1.ObjectMeta{
 | 
				
			||||||
						Namespace: "default",
 | 
											Namespace: "default",
 | 
				
			||||||
						Name:      "pod1",
 | 
											Name:      "pod1",
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
					Spec: v1.PodSpec{
 | 
										Spec: corev1.PodSpec{
 | 
				
			||||||
						NodeName: "node1",
 | 
											NodeName: "node1",
 | 
				
			||||||
						Tolerations: []v1.Toleration{
 | 
											Tolerations: []corev1.Toleration{
 | 
				
			||||||
							{Key: "testTaint1", Value: "test1", Effect: v1.TaintEffectNoExecute, TolerationSeconds: &[]int64{1}[0]},
 | 
												{Key: "testTaint1", Value: "test1", Effect: corev1.TaintEffectNoExecute, TolerationSeconds: &[]int64{1}[0]},
 | 
				
			||||||
							{Key: "testTaint2", Value: "test2", Effect: v1.TaintEffectNoExecute, TolerationSeconds: &[]int64{100}[0]},
 | 
												{Key: "testTaint2", Value: "test2", Effect: corev1.TaintEffectNoExecute, TolerationSeconds: &[]int64{100}[0]},
 | 
				
			||||||
						},
 | 
											},
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
					Status: v1.PodStatus{
 | 
										Status: corev1.PodStatus{
 | 
				
			||||||
						Conditions: []v1.PodCondition{
 | 
											Conditions: []corev1.PodCondition{
 | 
				
			||||||
							{
 | 
												{
 | 
				
			||||||
								Type:   v1.PodReady,
 | 
													Type:   corev1.PodReady,
 | 
				
			||||||
								Status: v1.ConditionTrue,
 | 
													Status: corev1.ConditionTrue,
 | 
				
			||||||
							},
 | 
												},
 | 
				
			||||||
						},
 | 
											},
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
@@ -499,8 +504,8 @@ func TestUpdateNode(t *testing.T) {
 | 
				
			|||||||
			ctx, cancel := context.WithCancel(context.Background())
 | 
								ctx, cancel := context.WithCancel(context.Background())
 | 
				
			||||||
			defer cancel()
 | 
								defer cancel()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			fakeClientset := fake.NewSimpleClientset(&v1.PodList{Items: item.pods})
 | 
								fakeClientset := fake.NewSimpleClientset(&corev1.PodList{Items: item.pods})
 | 
				
			||||||
			controller, _, nodeIndexer := setupNewNoExecuteTaintManager(ctx, fakeClientset)
 | 
								controller, _, nodeIndexer := setupNewController(ctx, fakeClientset)
 | 
				
			||||||
			nodeIndexer.Add(item.newNode)
 | 
								nodeIndexer.Add(item.newNode)
 | 
				
			||||||
			controller.recorder = testutil.NewFakeRecorder()
 | 
								controller.recorder = testutil.NewFakeRecorder()
 | 
				
			||||||
			go controller.Run(ctx)
 | 
								go controller.Run(ctx)
 | 
				
			||||||
@@ -521,23 +526,23 @@ func TestUpdateNodeWithMultipleTaints(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	minute := int64(60)
 | 
						minute := int64(60)
 | 
				
			||||||
	pod := testutil.NewPod("pod1", "node1")
 | 
						pod := testutil.NewPod("pod1", "node1")
 | 
				
			||||||
	pod.Spec.Tolerations = []v1.Toleration{
 | 
						pod.Spec.Tolerations = []corev1.Toleration{
 | 
				
			||||||
		{Key: taint1.Key, Operator: v1.TolerationOpExists, Effect: v1.TaintEffectNoExecute},
 | 
							{Key: taint1.Key, Operator: corev1.TolerationOpExists, Effect: corev1.TaintEffectNoExecute},
 | 
				
			||||||
		{Key: taint2.Key, Operator: v1.TolerationOpExists, Effect: v1.TaintEffectNoExecute, TolerationSeconds: &minute},
 | 
							{Key: taint2.Key, Operator: corev1.TolerationOpExists, Effect: corev1.TaintEffectNoExecute, TolerationSeconds: &minute},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	podNamespacedName := types.NamespacedName{Namespace: pod.Namespace, Name: pod.Name}
 | 
						podNamespacedName := types.NamespacedName{Namespace: pod.Namespace, Name: pod.Name}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	untaintedNode := testutil.NewNode("node1")
 | 
						untaintedNode := testutil.NewNode("node1")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	doubleTaintedNode := testutil.NewNode("node1")
 | 
						doubleTaintedNode := testutil.NewNode("node1")
 | 
				
			||||||
	doubleTaintedNode.Spec.Taints = []v1.Taint{taint1, taint2}
 | 
						doubleTaintedNode.Spec.Taints = []corev1.Taint{taint1, taint2}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	singleTaintedNode := testutil.NewNode("node1")
 | 
						singleTaintedNode := testutil.NewNode("node1")
 | 
				
			||||||
	singleTaintedNode.Spec.Taints = []v1.Taint{taint1}
 | 
						singleTaintedNode.Spec.Taints = []corev1.Taint{taint1}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx, cancel := context.WithCancel(context.TODO())
 | 
						ctx, cancel := context.WithCancel(context.TODO())
 | 
				
			||||||
	fakeClientset := fake.NewSimpleClientset(pod)
 | 
						fakeClientset := fake.NewSimpleClientset(pod)
 | 
				
			||||||
	controller, _, nodeIndexer := setupNewNoExecuteTaintManager(ctx, fakeClientset)
 | 
						controller, _, nodeIndexer := setupNewController(ctx, fakeClientset)
 | 
				
			||||||
	controller.recorder = testutil.NewFakeRecorder()
 | 
						controller.recorder = testutil.NewFakeRecorder()
 | 
				
			||||||
	go controller.Run(ctx)
 | 
						go controller.Run(ctx)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -585,14 +590,14 @@ func TestUpdateNodeWithMultipleTaints(t *testing.T) {
 | 
				
			|||||||
func TestUpdateNodeWithMultiplePods(t *testing.T) {
 | 
					func TestUpdateNodeWithMultiplePods(t *testing.T) {
 | 
				
			||||||
	testCases := []struct {
 | 
						testCases := []struct {
 | 
				
			||||||
		description         string
 | 
							description         string
 | 
				
			||||||
		pods                []v1.Pod
 | 
							pods                []corev1.Pod
 | 
				
			||||||
		oldNode             *v1.Node
 | 
							oldNode             *corev1.Node
 | 
				
			||||||
		newNode             *v1.Node
 | 
							newNode             *corev1.Node
 | 
				
			||||||
		expectedDeleteTimes durationSlice
 | 
							expectedDeleteTimes durationSlice
 | 
				
			||||||
	}{
 | 
						}{
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			description: "Pods with different toleration times are evicted appropriately",
 | 
								description: "Pods with different toleration times are evicted appropriately",
 | 
				
			||||||
			pods: []v1.Pod{
 | 
								pods: []corev1.Pod{
 | 
				
			||||||
				*testutil.NewPod("pod1", "node1"),
 | 
									*testutil.NewPod("pod1", "node1"),
 | 
				
			||||||
				*addToleration(testutil.NewPod("pod2", "node1"), 1, 1),
 | 
									*addToleration(testutil.NewPod("pod2", "node1"), 1, 1),
 | 
				
			||||||
				*addToleration(testutil.NewPod("pod3", "node1"), 1, -1),
 | 
									*addToleration(testutil.NewPod("pod3", "node1"), 1, -1),
 | 
				
			||||||
@@ -606,7 +611,7 @@ func TestUpdateNodeWithMultiplePods(t *testing.T) {
 | 
				
			|||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			description: "Evict all pods not matching all taints instantly",
 | 
								description: "Evict all pods not matching all taints instantly",
 | 
				
			||||||
			pods: []v1.Pod{
 | 
								pods: []corev1.Pod{
 | 
				
			||||||
				*testutil.NewPod("pod1", "node1"),
 | 
									*testutil.NewPod("pod1", "node1"),
 | 
				
			||||||
				*addToleration(testutil.NewPod("pod2", "node1"), 1, 1),
 | 
									*addToleration(testutil.NewPod("pod2", "node1"), 1, 1),
 | 
				
			||||||
				*addToleration(testutil.NewPod("pod3", "node1"), 1, -1),
 | 
									*addToleration(testutil.NewPod("pod3", "node1"), 1, -1),
 | 
				
			||||||
@@ -625,9 +630,9 @@ func TestUpdateNodeWithMultiplePods(t *testing.T) {
 | 
				
			|||||||
			ctx, cancel := context.WithCancel(context.Background())
 | 
								ctx, cancel := context.WithCancel(context.Background())
 | 
				
			||||||
			defer cancel()
 | 
								defer cancel()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			fakeClientset := fake.NewSimpleClientset(&v1.PodList{Items: item.pods})
 | 
								fakeClientset := fake.NewSimpleClientset(&corev1.PodList{Items: item.pods})
 | 
				
			||||||
			sort.Sort(item.expectedDeleteTimes)
 | 
								sort.Sort(item.expectedDeleteTimes)
 | 
				
			||||||
			controller, _, nodeIndexer := setupNewNoExecuteTaintManager(ctx, fakeClientset)
 | 
								controller, _, nodeIndexer := setupNewController(ctx, fakeClientset)
 | 
				
			||||||
			nodeIndexer.Add(item.newNode)
 | 
								nodeIndexer.Add(item.newNode)
 | 
				
			||||||
			controller.recorder = testutil.NewFakeRecorder()
 | 
								controller.recorder = testutil.NewFakeRecorder()
 | 
				
			||||||
			go controller.Run(ctx)
 | 
								go controller.Run(ctx)
 | 
				
			||||||
@@ -704,15 +709,15 @@ func TestGetMinTolerationTime(t *testing.T) {
 | 
				
			|||||||
	oneSec := 1 * time.Second
 | 
						oneSec := 1 * time.Second
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tests := []struct {
 | 
						tests := []struct {
 | 
				
			||||||
		tolerations []v1.Toleration
 | 
							tolerations []corev1.Toleration
 | 
				
			||||||
		expected    time.Duration
 | 
							expected    time.Duration
 | 
				
			||||||
	}{
 | 
						}{
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			tolerations: []v1.Toleration{},
 | 
								tolerations: []corev1.Toleration{},
 | 
				
			||||||
			expected:    0,
 | 
								expected:    0,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			tolerations: []v1.Toleration{
 | 
								tolerations: []corev1.Toleration{
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
					TolerationSeconds: nil,
 | 
										TolerationSeconds: nil,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
@@ -720,7 +725,7 @@ func TestGetMinTolerationTime(t *testing.T) {
 | 
				
			|||||||
			expected: -1,
 | 
								expected: -1,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			tolerations: []v1.Toleration{
 | 
								tolerations: []corev1.Toleration{
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
					TolerationSeconds: &one,
 | 
										TolerationSeconds: &one,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
@@ -732,7 +737,7 @@ func TestGetMinTolerationTime(t *testing.T) {
 | 
				
			|||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			tolerations: []v1.Toleration{
 | 
								tolerations: []corev1.Toleration{
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
					TolerationSeconds: &one,
 | 
										TolerationSeconds: &one,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
@@ -743,7 +748,7 @@ func TestGetMinTolerationTime(t *testing.T) {
 | 
				
			|||||||
			expected: oneSec,
 | 
								expected: oneSec,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			tolerations: []v1.Toleration{
 | 
								tolerations: []corev1.Toleration{
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
					TolerationSeconds: nil,
 | 
										TolerationSeconds: nil,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
@@ -770,17 +775,17 @@ func TestGetMinTolerationTime(t *testing.T) {
 | 
				
			|||||||
func TestEventualConsistency(t *testing.T) {
 | 
					func TestEventualConsistency(t *testing.T) {
 | 
				
			||||||
	testCases := []struct {
 | 
						testCases := []struct {
 | 
				
			||||||
		description  string
 | 
							description  string
 | 
				
			||||||
		pods         []v1.Pod
 | 
							pods         []corev1.Pod
 | 
				
			||||||
		prevPod      *v1.Pod
 | 
							prevPod      *corev1.Pod
 | 
				
			||||||
		newPod       *v1.Pod
 | 
							newPod       *corev1.Pod
 | 
				
			||||||
		oldNode      *v1.Node
 | 
							oldNode      *corev1.Node
 | 
				
			||||||
		newNode      *v1.Node
 | 
							newNode      *corev1.Node
 | 
				
			||||||
		expectPatch  bool
 | 
							expectPatch  bool
 | 
				
			||||||
		expectDelete bool
 | 
							expectDelete bool
 | 
				
			||||||
	}{
 | 
						}{
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			description: "existing pod2 scheduled onto tainted Node",
 | 
								description: "existing pod2 scheduled onto tainted Node",
 | 
				
			||||||
			pods: []v1.Pod{
 | 
								pods: []corev1.Pod{
 | 
				
			||||||
				*testutil.NewPod("pod1", "node1"),
 | 
									*testutil.NewPod("pod1", "node1"),
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			prevPod:      testutil.NewPod("pod2", ""),
 | 
								prevPod:      testutil.NewPod("pod2", ""),
 | 
				
			||||||
@@ -792,7 +797,7 @@ func TestEventualConsistency(t *testing.T) {
 | 
				
			|||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			description: "existing pod2 with taint toleration scheduled onto tainted Node",
 | 
								description: "existing pod2 with taint toleration scheduled onto tainted Node",
 | 
				
			||||||
			pods: []v1.Pod{
 | 
								pods: []corev1.Pod{
 | 
				
			||||||
				*testutil.NewPod("pod1", "node1"),
 | 
									*testutil.NewPod("pod1", "node1"),
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			prevPod:      addToleration(testutil.NewPod("pod2", ""), 1, 100),
 | 
								prevPod:      addToleration(testutil.NewPod("pod2", ""), 1, 100),
 | 
				
			||||||
@@ -804,7 +809,7 @@ func TestEventualConsistency(t *testing.T) {
 | 
				
			|||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			description: "new pod2 created on tainted Node",
 | 
								description: "new pod2 created on tainted Node",
 | 
				
			||||||
			pods: []v1.Pod{
 | 
								pods: []corev1.Pod{
 | 
				
			||||||
				*testutil.NewPod("pod1", "node1"),
 | 
									*testutil.NewPod("pod1", "node1"),
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			prevPod:      nil,
 | 
								prevPod:      nil,
 | 
				
			||||||
@@ -816,7 +821,7 @@ func TestEventualConsistency(t *testing.T) {
 | 
				
			|||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			description: "new pod2 with tait toleration created on tainted Node",
 | 
								description: "new pod2 with tait toleration created on tainted Node",
 | 
				
			||||||
			pods: []v1.Pod{
 | 
								pods: []corev1.Pod{
 | 
				
			||||||
				*testutil.NewPod("pod1", "node1"),
 | 
									*testutil.NewPod("pod1", "node1"),
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			prevPod:      nil,
 | 
								prevPod:      nil,
 | 
				
			||||||
@@ -833,8 +838,8 @@ func TestEventualConsistency(t *testing.T) {
 | 
				
			|||||||
			ctx, cancel := context.WithCancel(context.Background())
 | 
								ctx, cancel := context.WithCancel(context.Background())
 | 
				
			||||||
			defer cancel()
 | 
								defer cancel()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			fakeClientset := fake.NewSimpleClientset(&v1.PodList{Items: item.pods})
 | 
								fakeClientset := fake.NewSimpleClientset(&corev1.PodList{Items: item.pods})
 | 
				
			||||||
			controller, podIndexer, nodeIndexer := setupNewNoExecuteTaintManager(ctx, fakeClientset)
 | 
								controller, podIndexer, nodeIndexer := setupNewController(ctx, fakeClientset)
 | 
				
			||||||
			nodeIndexer.Add(item.newNode)
 | 
								nodeIndexer.Add(item.newNode)
 | 
				
			||||||
			controller.recorder = testutil.NewFakeRecorder()
 | 
								controller.recorder = testutil.NewFakeRecorder()
 | 
				
			||||||
			go controller.Run(ctx)
 | 
								go controller.Run(ctx)
 | 
				
			||||||
@@ -899,19 +904,19 @@ func TestPodDeletionEvent(t *testing.T) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	t.Run("emitPodDeletionEvent", func(t *testing.T) {
 | 
						t.Run("emitPodDeletionEvent", func(t *testing.T) {
 | 
				
			||||||
		controller := &NoExecuteTaintManager{}
 | 
							controller := &Controller{}
 | 
				
			||||||
		recorder := testutil.NewFakeRecorder()
 | 
							recorder := testutil.NewFakeRecorder()
 | 
				
			||||||
		controller.recorder = recorder
 | 
							controller.recorder = recorder
 | 
				
			||||||
		controller.emitPodDeletionEvent(types.NamespacedName{
 | 
							controller.emitPodDeletionEvent(types.NamespacedName{
 | 
				
			||||||
			Name:      "test",
 | 
								Name:      "test",
 | 
				
			||||||
			Namespace: "test",
 | 
								Namespace: "test",
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
		want := []*v1.Event{
 | 
							want := []*corev1.Event{
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				ObjectMeta: metav1.ObjectMeta{
 | 
									ObjectMeta: metav1.ObjectMeta{
 | 
				
			||||||
					Namespace: "test",
 | 
										Namespace: "test",
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
				InvolvedObject: v1.ObjectReference{
 | 
									InvolvedObject: corev1.ObjectReference{
 | 
				
			||||||
					Kind:       "Pod",
 | 
										Kind:       "Pod",
 | 
				
			||||||
					APIVersion: "v1",
 | 
										APIVersion: "v1",
 | 
				
			||||||
					Namespace:  "test",
 | 
										Namespace:  "test",
 | 
				
			||||||
@@ -921,7 +926,7 @@ func TestPodDeletionEvent(t *testing.T) {
 | 
				
			|||||||
				Type:    "Normal",
 | 
									Type:    "Normal",
 | 
				
			||||||
				Count:   1,
 | 
									Count:   1,
 | 
				
			||||||
				Message: "Marking for deletion Pod test/test",
 | 
									Message: "Marking for deletion Pod test/test",
 | 
				
			||||||
				Source:  v1.EventSource{Component: "nodeControllerTest"},
 | 
									Source:  corev1.EventSource{Component: "nodeControllerTest"},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if diff := cmp.Diff(want, recorder.Events, cmp.FilterPath(f, cmp.Ignore())); len(diff) > 0 {
 | 
							if diff := cmp.Diff(want, recorder.Events, cmp.FilterPath(f, cmp.Ignore())); len(diff) > 0 {
 | 
				
			||||||
@@ -930,19 +935,19 @@ func TestPodDeletionEvent(t *testing.T) {
 | 
				
			|||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	t.Run("emitCancelPodDeletionEvent", func(t *testing.T) {
 | 
						t.Run("emitCancelPodDeletionEvent", func(t *testing.T) {
 | 
				
			||||||
		controller := &NoExecuteTaintManager{}
 | 
							controller := &Controller{}
 | 
				
			||||||
		recorder := testutil.NewFakeRecorder()
 | 
							recorder := testutil.NewFakeRecorder()
 | 
				
			||||||
		controller.recorder = recorder
 | 
							controller.recorder = recorder
 | 
				
			||||||
		controller.emitCancelPodDeletionEvent(types.NamespacedName{
 | 
							controller.emitCancelPodDeletionEvent(types.NamespacedName{
 | 
				
			||||||
			Name:      "test",
 | 
								Name:      "test",
 | 
				
			||||||
			Namespace: "test",
 | 
								Namespace: "test",
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
		want := []*v1.Event{
 | 
							want := []*corev1.Event{
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				ObjectMeta: metav1.ObjectMeta{
 | 
									ObjectMeta: metav1.ObjectMeta{
 | 
				
			||||||
					Namespace: "test",
 | 
										Namespace: "test",
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
				InvolvedObject: v1.ObjectReference{
 | 
									InvolvedObject: corev1.ObjectReference{
 | 
				
			||||||
					Kind:       "Pod",
 | 
										Kind:       "Pod",
 | 
				
			||||||
					APIVersion: "v1",
 | 
										APIVersion: "v1",
 | 
				
			||||||
					Namespace:  "test",
 | 
										Namespace:  "test",
 | 
				
			||||||
@@ -952,7 +957,7 @@ func TestPodDeletionEvent(t *testing.T) {
 | 
				
			|||||||
				Type:    "Normal",
 | 
									Type:    "Normal",
 | 
				
			||||||
				Count:   1,
 | 
									Count:   1,
 | 
				
			||||||
				Message: "Cancelling deletion of Pod test/test",
 | 
									Message: "Cancelling deletion of Pod test/test",
 | 
				
			||||||
				Source:  v1.EventSource{Component: "nodeControllerTest"},
 | 
									Source:  corev1.EventSource{Component: "nodeControllerTest"},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if diff := cmp.Diff(want, recorder.Events, cmp.FilterPath(f, cmp.Ignore())); len(diff) > 0 {
 | 
							if diff := cmp.Diff(want, recorder.Events, cmp.FilterPath(f, cmp.Ignore())); len(diff) > 0 {
 | 
				
			||||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
 | 
				
			|||||||
limitations under the License.
 | 
					limitations under the License.
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package scheduler
 | 
					package tainteviction
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
@@ -38,7 +38,9 @@ func (w *WorkArgs) KeyFromWorkArgs() string {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// NewWorkArgs is a helper function to create new `WorkArgs`
 | 
					// NewWorkArgs is a helper function to create new `WorkArgs`
 | 
				
			||||||
func NewWorkArgs(name, namespace string) *WorkArgs {
 | 
					func NewWorkArgs(name, namespace string) *WorkArgs {
 | 
				
			||||||
	return &WorkArgs{types.NamespacedName{Namespace: namespace, Name: name}}
 | 
						return &WorkArgs{
 | 
				
			||||||
 | 
							NamespacedName: types.NamespacedName{Namespace: namespace, Name: name},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TimedWorker is a responsible for executing a function no earlier than at FireAt time.
 | 
					// TimedWorker is a responsible for executing a function no earlier than at FireAt time.
 | 
				
			||||||
@@ -50,13 +52,13 @@ type TimedWorker struct {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// createWorker creates a TimedWorker that will execute `f` not earlier than `fireAt`.
 | 
					// createWorker creates a TimedWorker that will execute `f` not earlier than `fireAt`.
 | 
				
			||||||
func createWorker(ctx context.Context, args *WorkArgs, createdAt time.Time, fireAt time.Time, f func(ctx context.Context, args *WorkArgs) error, clock clock.WithDelayedExecution) *TimedWorker {
 | 
					func createWorker(ctx context.Context, args *WorkArgs, createdAt time.Time, fireAt time.Time, f func(ctx context.Context, fireAt time.Time, args *WorkArgs) error, clock clock.WithDelayedExecution) *TimedWorker {
 | 
				
			||||||
	delay := fireAt.Sub(createdAt)
 | 
						delay := fireAt.Sub(createdAt)
 | 
				
			||||||
	logger := klog.FromContext(ctx)
 | 
						logger := klog.FromContext(ctx)
 | 
				
			||||||
	fWithErrorLogging := func() {
 | 
						fWithErrorLogging := func() {
 | 
				
			||||||
		err := f(ctx, args)
 | 
							err := f(ctx, fireAt, args)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			logger.Error(err, "NodeLifecycle: timed worker failed")
 | 
								logger.Error(err, "TaintEvictionController: timed worker failed")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if delay <= 0 {
 | 
						if delay <= 0 {
 | 
				
			||||||
@@ -84,13 +86,13 @@ type TimedWorkerQueue struct {
 | 
				
			|||||||
	sync.Mutex
 | 
						sync.Mutex
 | 
				
			||||||
	// map of workers keyed by string returned by 'KeyFromWorkArgs' from the given worker.
 | 
						// map of workers keyed by string returned by 'KeyFromWorkArgs' from the given worker.
 | 
				
			||||||
	workers  map[string]*TimedWorker
 | 
						workers  map[string]*TimedWorker
 | 
				
			||||||
	workFunc func(ctx context.Context, args *WorkArgs) error
 | 
						workFunc func(ctx context.Context, fireAt time.Time, args *WorkArgs) error
 | 
				
			||||||
	clock    clock.WithDelayedExecution
 | 
						clock    clock.WithDelayedExecution
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CreateWorkerQueue creates a new TimedWorkerQueue for workers that will execute
 | 
					// CreateWorkerQueue creates a new TimedWorkerQueue for workers that will execute
 | 
				
			||||||
// given function `f`.
 | 
					// given function `f`.
 | 
				
			||||||
func CreateWorkerQueue(f func(ctx context.Context, args *WorkArgs) error) *TimedWorkerQueue {
 | 
					func CreateWorkerQueue(f func(ctx context.Context, fireAt time.Time, args *WorkArgs) error) *TimedWorkerQueue {
 | 
				
			||||||
	return &TimedWorkerQueue{
 | 
						return &TimedWorkerQueue{
 | 
				
			||||||
		workers:  make(map[string]*TimedWorker),
 | 
							workers:  make(map[string]*TimedWorker),
 | 
				
			||||||
		workFunc: f,
 | 
							workFunc: f,
 | 
				
			||||||
@@ -98,9 +100,9 @@ func CreateWorkerQueue(f func(ctx context.Context, args *WorkArgs) error) *Timed
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (q *TimedWorkerQueue) getWrappedWorkerFunc(key string) func(ctx context.Context, args *WorkArgs) error {
 | 
					func (q *TimedWorkerQueue) getWrappedWorkerFunc(key string) func(ctx context.Context, fireAt time.Time, args *WorkArgs) error {
 | 
				
			||||||
	return func(ctx context.Context, args *WorkArgs) error {
 | 
						return func(ctx context.Context, fireAt time.Time, args *WorkArgs) error {
 | 
				
			||||||
		err := q.workFunc(ctx, args)
 | 
							err := q.workFunc(ctx, fireAt, args)
 | 
				
			||||||
		q.Lock()
 | 
							q.Lock()
 | 
				
			||||||
		defer q.Unlock()
 | 
							defer q.Unlock()
 | 
				
			||||||
		if err == nil {
 | 
							if err == nil {
 | 
				
			||||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
 | 
				
			|||||||
limitations under the License.
 | 
					limitations under the License.
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package scheduler
 | 
					package tainteviction
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
@@ -31,7 +31,7 @@ func TestExecute(t *testing.T) {
 | 
				
			|||||||
	testVal := int32(0)
 | 
						testVal := int32(0)
 | 
				
			||||||
	wg := sync.WaitGroup{}
 | 
						wg := sync.WaitGroup{}
 | 
				
			||||||
	wg.Add(5)
 | 
						wg.Add(5)
 | 
				
			||||||
	queue := CreateWorkerQueue(func(ctx context.Context, args *WorkArgs) error {
 | 
						queue := CreateWorkerQueue(func(ctx context.Context, fireAt time.Time, args *WorkArgs) error {
 | 
				
			||||||
		atomic.AddInt32(&testVal, 1)
 | 
							atomic.AddInt32(&testVal, 1)
 | 
				
			||||||
		wg.Done()
 | 
							wg.Done()
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
@@ -59,7 +59,7 @@ func TestExecuteDelayed(t *testing.T) {
 | 
				
			|||||||
	testVal := int32(0)
 | 
						testVal := int32(0)
 | 
				
			||||||
	wg := sync.WaitGroup{}
 | 
						wg := sync.WaitGroup{}
 | 
				
			||||||
	wg.Add(5)
 | 
						wg.Add(5)
 | 
				
			||||||
	queue := CreateWorkerQueue(func(ctx context.Context, args *WorkArgs) error {
 | 
						queue := CreateWorkerQueue(func(ctx context.Context, fireAt time.Time, args *WorkArgs) error {
 | 
				
			||||||
		atomic.AddInt32(&testVal, 1)
 | 
							atomic.AddInt32(&testVal, 1)
 | 
				
			||||||
		wg.Done()
 | 
							wg.Done()
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
@@ -90,7 +90,7 @@ func TestCancel(t *testing.T) {
 | 
				
			|||||||
	testVal := int32(0)
 | 
						testVal := int32(0)
 | 
				
			||||||
	wg := sync.WaitGroup{}
 | 
						wg := sync.WaitGroup{}
 | 
				
			||||||
	wg.Add(3)
 | 
						wg.Add(3)
 | 
				
			||||||
	queue := CreateWorkerQueue(func(ctx context.Context, args *WorkArgs) error {
 | 
						queue := CreateWorkerQueue(func(ctx context.Context, fireAt time.Time, args *WorkArgs) error {
 | 
				
			||||||
		atomic.AddInt32(&testVal, 1)
 | 
							atomic.AddInt32(&testVal, 1)
 | 
				
			||||||
		wg.Done()
 | 
							wg.Done()
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
@@ -124,7 +124,7 @@ func TestCancelAndReadd(t *testing.T) {
 | 
				
			|||||||
	testVal := int32(0)
 | 
						testVal := int32(0)
 | 
				
			||||||
	wg := sync.WaitGroup{}
 | 
						wg := sync.WaitGroup{}
 | 
				
			||||||
	wg.Add(4)
 | 
						wg.Add(4)
 | 
				
			||||||
	queue := CreateWorkerQueue(func(ctx context.Context, args *WorkArgs) error {
 | 
						queue := CreateWorkerQueue(func(ctx context.Context, fireAt time.Time, args *WorkArgs) error {
 | 
				
			||||||
		atomic.AddInt32(&testVal, 1)
 | 
							atomic.AddInt32(&testVal, 1)
 | 
				
			||||||
		wg.Done()
 | 
							wg.Done()
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
@@ -729,6 +729,13 @@ const (
 | 
				
			|||||||
	// https://github.com/kubernetes/kubernetes/issues/111516
 | 
						// https://github.com/kubernetes/kubernetes/issues/111516
 | 
				
			||||||
	SecurityContextDeny featuregate.Feature = "SecurityContextDeny"
 | 
						SecurityContextDeny featuregate.Feature = "SecurityContextDeny"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// owner: @atosatto @yuanchen8911
 | 
				
			||||||
 | 
						// kep: http://kep.k8s.io/3902
 | 
				
			||||||
 | 
						// beta: v1.29
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// Decouples Taint Eviction Controller, performing taint-based Pod eviction, from Node Lifecycle Controller.
 | 
				
			||||||
 | 
						SeparateTaintEvictionController featuregate.Feature = "SeparateTaintEvictionController"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// owner: @xuzhenglun
 | 
						// owner: @xuzhenglun
 | 
				
			||||||
	// kep: http://kep.k8s.io/3682
 | 
						// kep: http://kep.k8s.io/3682
 | 
				
			||||||
	// alpha: v1.27
 | 
						// alpha: v1.27
 | 
				
			||||||
@@ -1093,6 +1100,8 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	SecurityContextDeny: {Default: false, PreRelease: featuregate.Alpha},
 | 
						SecurityContextDeny: {Default: false, PreRelease: featuregate.Alpha},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						SeparateTaintEvictionController: {Default: true, PreRelease: featuregate.Beta},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ServiceNodePortStaticSubrange: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // GA in 1.29; remove in 1.31
 | 
						ServiceNodePortStaticSubrange: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // GA in 1.29; remove in 1.31
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	SidecarContainers: {Default: false, PreRelease: featuregate.Alpha},
 | 
						SidecarContainers: {Default: false, PreRelease: featuregate.Alpha},
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -32,8 +32,10 @@ import (
 | 
				
			|||||||
	restclient "k8s.io/client-go/rest"
 | 
						restclient "k8s.io/client-go/rest"
 | 
				
			||||||
	featuregatetesting "k8s.io/component-base/featuregate/testing"
 | 
						featuregatetesting "k8s.io/component-base/featuregate/testing"
 | 
				
			||||||
	"k8s.io/klog/v2"
 | 
						"k8s.io/klog/v2"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/cmd/kube-controller-manager/names"
 | 
				
			||||||
	podutil "k8s.io/kubernetes/pkg/api/v1/pod"
 | 
						podutil "k8s.io/kubernetes/pkg/api/v1/pod"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/controller/nodelifecycle"
 | 
						"k8s.io/kubernetes/pkg/controller/nodelifecycle"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/controller/tainteviction"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/features"
 | 
						"k8s.io/kubernetes/pkg/features"
 | 
				
			||||||
	"k8s.io/kubernetes/plugin/pkg/admission/defaulttolerationseconds"
 | 
						"k8s.io/kubernetes/plugin/pkg/admission/defaulttolerationseconds"
 | 
				
			||||||
	"k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction"
 | 
						"k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction"
 | 
				
			||||||
@@ -49,13 +51,34 @@ func TestEvictionForNoExecuteTaintAddedByUser(t *testing.T) {
 | 
				
			|||||||
	nodeIndex := 1 // the exact node doesn't matter, pick one
 | 
						nodeIndex := 1 // the exact node doesn't matter, pick one
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tests := map[string]struct {
 | 
						tests := map[string]struct {
 | 
				
			||||||
		enablePodDisruptionConditions bool
 | 
							enablePodDisruptionConditions          bool
 | 
				
			||||||
 | 
							enableSeparateTaintEvictionController  bool
 | 
				
			||||||
 | 
							startStandaloneTaintEvictionController bool
 | 
				
			||||||
 | 
							wantPodEvicted                         bool
 | 
				
			||||||
	}{
 | 
						}{
 | 
				
			||||||
		"Test eviciton for NoExecute taint added by user; pod condition added when PodDisruptionConditions enabled": {
 | 
							"Test eviction for NoExecute taint added by user; pod condition added when PodDisruptionConditions enabled; separate taint eviction controller disabled": {
 | 
				
			||||||
			enablePodDisruptionConditions: true,
 | 
								enablePodDisruptionConditions:          true,
 | 
				
			||||||
 | 
								enableSeparateTaintEvictionController:  false,
 | 
				
			||||||
 | 
								startStandaloneTaintEvictionController: false,
 | 
				
			||||||
 | 
								wantPodEvicted:                         true,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"Test eviciton for NoExecute taint added by user; no pod condition added when PodDisruptionConditions disabled": {
 | 
							"Test eviction for NoExecute taint added by user; no pod condition added when PodDisruptionConditions disabled; separate taint eviction controller disabled": {
 | 
				
			||||||
			enablePodDisruptionConditions: false,
 | 
								enablePodDisruptionConditions:          false,
 | 
				
			||||||
 | 
								enableSeparateTaintEvictionController:  false,
 | 
				
			||||||
 | 
								startStandaloneTaintEvictionController: false,
 | 
				
			||||||
 | 
								wantPodEvicted:                         true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"Test eviction for NoExecute taint added by user; separate taint eviction controller enabled but not started": {
 | 
				
			||||||
 | 
								enablePodDisruptionConditions:          false,
 | 
				
			||||||
 | 
								enableSeparateTaintEvictionController:  true,
 | 
				
			||||||
 | 
								startStandaloneTaintEvictionController: false,
 | 
				
			||||||
 | 
								wantPodEvicted:                         false,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"Test eviction for NoExecute taint added by user; separate taint eviction controller enabled and started": {
 | 
				
			||||||
 | 
								enablePodDisruptionConditions:          false,
 | 
				
			||||||
 | 
								enableSeparateTaintEvictionController:  true,
 | 
				
			||||||
 | 
								startStandaloneTaintEvictionController: true,
 | 
				
			||||||
 | 
								wantPodEvicted:                         true,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -102,6 +125,7 @@ func TestEvictionForNoExecuteTaintAddedByUser(t *testing.T) {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.PodDisruptionConditions, test.enablePodDisruptionConditions)()
 | 
								defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.PodDisruptionConditions, test.enablePodDisruptionConditions)()
 | 
				
			||||||
 | 
								defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.SeparateTaintEvictionController, test.enableSeparateTaintEvictionController)()
 | 
				
			||||||
			testCtx := testutils.InitTestAPIServer(t, "taint-no-execute", nil)
 | 
								testCtx := testutils.InitTestAPIServer(t, "taint-no-execute", nil)
 | 
				
			||||||
			cs := testCtx.ClientSet
 | 
								cs := testCtx.ClientSet
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -138,6 +162,18 @@ func TestEvictionForNoExecuteTaintAddedByUser(t *testing.T) {
 | 
				
			|||||||
			// Run all controllers
 | 
								// Run all controllers
 | 
				
			||||||
			go nc.Run(testCtx.Ctx)
 | 
								go nc.Run(testCtx.Ctx)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Start TaintManager
 | 
				
			||||||
 | 
								if test.startStandaloneTaintEvictionController {
 | 
				
			||||||
 | 
									tm, _ := tainteviction.New(
 | 
				
			||||||
 | 
										testCtx.Ctx,
 | 
				
			||||||
 | 
										testCtx.ClientSet,
 | 
				
			||||||
 | 
										externalInformers.Core().V1().Pods(),
 | 
				
			||||||
 | 
										externalInformers.Core().V1().Nodes(),
 | 
				
			||||||
 | 
										names.TaintEvictionController,
 | 
				
			||||||
 | 
									)
 | 
				
			||||||
 | 
									go tm.Run(testCtx.Ctx)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			for index := range nodes {
 | 
								for index := range nodes {
 | 
				
			||||||
				nodes[index], err = cs.CoreV1().Nodes().Create(testCtx.Ctx, nodes[index], metav1.CreateOptions{})
 | 
									nodes[index], err = cs.CoreV1().Nodes().Create(testCtx.Ctx, nodes[index], metav1.CreateOptions{})
 | 
				
			||||||
				if err != nil {
 | 
									if err != nil {
 | 
				
			||||||
@@ -155,9 +191,12 @@ func TestEvictionForNoExecuteTaintAddedByUser(t *testing.T) {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			err = wait.PollUntilContextTimeout(testCtx.Ctx, time.Second, time.Second*20, true, testutils.PodIsGettingEvicted(cs, testPod.Namespace, testPod.Name))
 | 
								err = wait.PollUntilContextTimeout(testCtx.Ctx, time.Second, time.Second*20, true, testutils.PodIsGettingEvicted(cs, testPod.Namespace, testPod.Name))
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil && test.wantPodEvicted {
 | 
				
			||||||
				t.Fatalf("Error %q in test %q when waiting for terminating pod: %q", err, name, klog.KObj(testPod))
 | 
									t.Fatalf("Test Failed: error %v while waiting for pod %q to be evicted", err, klog.KObj(testPod))
 | 
				
			||||||
 | 
								} else if !wait.Interrupted(err) && !test.wantPodEvicted {
 | 
				
			||||||
 | 
									t.Fatalf("Test Failed: unexpected eviction of pod %q", klog.KObj(testPod))
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			testPod, err = cs.CoreV1().Pods(testCtx.NS.Name).Get(testCtx.Ctx, testPod.Name, metav1.GetOptions{})
 | 
								testPod, err = cs.CoreV1().Pods(testCtx.NS.Name).Get(testCtx.Ctx, testPod.Name, metav1.GetOptions{})
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				t.Fatalf("Test Failed: error: %q, while getting updated pod", err)
 | 
									t.Fatalf("Test Failed: error: %q, while getting updated pod", err)
 | 
				
			||||||
@@ -196,23 +235,34 @@ func TestTaintBasedEvictions(t *testing.T) {
 | 
				
			|||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	tests := []struct {
 | 
						tests := []struct {
 | 
				
			||||||
		name                        string
 | 
							name                                  string
 | 
				
			||||||
		nodeTaints                  []v1.Taint
 | 
							nodeTaints                            []v1.Taint
 | 
				
			||||||
		nodeConditions              []v1.NodeCondition
 | 
							nodeConditions                        []v1.NodeCondition
 | 
				
			||||||
		pod                         *v1.Pod
 | 
							pod                                   *v1.Pod
 | 
				
			||||||
		tolerationSeconds           int64
 | 
							tolerationSeconds                     int64
 | 
				
			||||||
		expectedWaitForPodCondition string
 | 
							expectedWaitForPodCondition           string
 | 
				
			||||||
 | 
							enableSeparateTaintEvictionController bool
 | 
				
			||||||
	}{
 | 
						}{
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			name:                        "Taint based evictions for NodeNotReady and 200 tolerationseconds",
 | 
								name:                                  "Taint based evictions for NodeNotReady and 200 tolerationseconds; separate taint eviction controller disabled",
 | 
				
			||||||
			nodeTaints:                  []v1.Taint{{Key: v1.TaintNodeNotReady, Effect: v1.TaintEffectNoExecute}},
 | 
								nodeTaints:                            []v1.Taint{{Key: v1.TaintNodeNotReady, Effect: v1.TaintEffectNoExecute}},
 | 
				
			||||||
			nodeConditions:              []v1.NodeCondition{{Type: v1.NodeReady, Status: v1.ConditionFalse}},
 | 
								nodeConditions:                        []v1.NodeCondition{{Type: v1.NodeReady, Status: v1.ConditionFalse}},
 | 
				
			||||||
			pod:                         testPod.DeepCopy(),
 | 
								pod:                                   testPod.DeepCopy(),
 | 
				
			||||||
			tolerationSeconds:           200,
 | 
								tolerationSeconds:                     200,
 | 
				
			||||||
			expectedWaitForPodCondition: "updated with tolerationSeconds of 200",
 | 
								expectedWaitForPodCondition:           "updated with tolerationSeconds of 200",
 | 
				
			||||||
 | 
								enableSeparateTaintEvictionController: false,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			name:           "Taint based evictions for NodeNotReady with no pod tolerations",
 | 
								name:                                  "Taint based evictions for NodeNotReady and 200 tolerationseconds; separate taint eviction controller enabled",
 | 
				
			||||||
 | 
								nodeTaints:                            []v1.Taint{{Key: v1.TaintNodeNotReady, Effect: v1.TaintEffectNoExecute}},
 | 
				
			||||||
 | 
								nodeConditions:                        []v1.NodeCondition{{Type: v1.NodeReady, Status: v1.ConditionFalse}},
 | 
				
			||||||
 | 
								pod:                                   testPod.DeepCopy(),
 | 
				
			||||||
 | 
								tolerationSeconds:                     200,
 | 
				
			||||||
 | 
								expectedWaitForPodCondition:           "updated with tolerationSeconds of 200",
 | 
				
			||||||
 | 
								enableSeparateTaintEvictionController: true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:           "Taint based evictions for NodeNotReady with no pod tolerations; separate taint eviction controller disabled",
 | 
				
			||||||
			nodeTaints:     []v1.Taint{{Key: v1.TaintNodeNotReady, Effect: v1.TaintEffectNoExecute}},
 | 
								nodeTaints:     []v1.Taint{{Key: v1.TaintNodeNotReady, Effect: v1.TaintEffectNoExecute}},
 | 
				
			||||||
			nodeConditions: []v1.NodeCondition{{Type: v1.NodeReady, Status: v1.ConditionFalse}},
 | 
								nodeConditions: []v1.NodeCondition{{Type: v1.NodeReady, Status: v1.ConditionFalse}},
 | 
				
			||||||
			pod: &v1.Pod{
 | 
								pod: &v1.Pod{
 | 
				
			||||||
@@ -223,21 +273,55 @@ func TestTaintBasedEvictions(t *testing.T) {
 | 
				
			|||||||
					},
 | 
										},
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			tolerationSeconds:           300,
 | 
								tolerationSeconds:                     300,
 | 
				
			||||||
			expectedWaitForPodCondition: "updated with tolerationSeconds=300",
 | 
								expectedWaitForPodCondition:           "updated with tolerationSeconds=300",
 | 
				
			||||||
 | 
								enableSeparateTaintEvictionController: false,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			name:                        "Taint based evictions for NodeNotReady and 0 tolerationseconds",
 | 
								name:           "Taint based evictions for NodeNotReady with no pod tolerations; separate taint eviction controller enabled",
 | 
				
			||||||
			nodeTaints:                  []v1.Taint{{Key: v1.TaintNodeNotReady, Effect: v1.TaintEffectNoExecute}},
 | 
								nodeTaints:     []v1.Taint{{Key: v1.TaintNodeNotReady, Effect: v1.TaintEffectNoExecute}},
 | 
				
			||||||
			nodeConditions:              []v1.NodeCondition{{Type: v1.NodeReady, Status: v1.ConditionFalse}},
 | 
								nodeConditions: []v1.NodeCondition{{Type: v1.NodeReady, Status: v1.ConditionFalse}},
 | 
				
			||||||
			pod:                         testPod.DeepCopy(),
 | 
								pod: &v1.Pod{
 | 
				
			||||||
			tolerationSeconds:           0,
 | 
									ObjectMeta: metav1.ObjectMeta{Name: "testpod1"},
 | 
				
			||||||
			expectedWaitForPodCondition: "terminating",
 | 
									Spec: v1.PodSpec{
 | 
				
			||||||
 | 
										Containers: []v1.Container{
 | 
				
			||||||
 | 
											{Name: "container", Image: imageutils.GetPauseImageName()},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								tolerationSeconds:                     300,
 | 
				
			||||||
 | 
								expectedWaitForPodCondition:           "updated with tolerationSeconds=300",
 | 
				
			||||||
 | 
								enableSeparateTaintEvictionController: true,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			name:           "Taint based evictions for NodeUnreachable",
 | 
								name:                                  "Taint based evictions for NodeNotReady and 0 tolerationseconds; separate taint eviction controller disabled",
 | 
				
			||||||
			nodeTaints:     []v1.Taint{{Key: v1.TaintNodeUnreachable, Effect: v1.TaintEffectNoExecute}},
 | 
								nodeTaints:                            []v1.Taint{{Key: v1.TaintNodeNotReady, Effect: v1.TaintEffectNoExecute}},
 | 
				
			||||||
			nodeConditions: []v1.NodeCondition{{Type: v1.NodeReady, Status: v1.ConditionUnknown}},
 | 
								nodeConditions:                        []v1.NodeCondition{{Type: v1.NodeReady, Status: v1.ConditionFalse}},
 | 
				
			||||||
 | 
								pod:                                   testPod.DeepCopy(),
 | 
				
			||||||
 | 
								tolerationSeconds:                     0,
 | 
				
			||||||
 | 
								expectedWaitForPodCondition:           "terminating",
 | 
				
			||||||
 | 
								enableSeparateTaintEvictionController: false,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:                                  "Taint based evictions for NodeNotReady and 0 tolerationseconds; separate taint eviction controller enabled",
 | 
				
			||||||
 | 
								nodeTaints:                            []v1.Taint{{Key: v1.TaintNodeNotReady, Effect: v1.TaintEffectNoExecute}},
 | 
				
			||||||
 | 
								nodeConditions:                        []v1.NodeCondition{{Type: v1.NodeReady, Status: v1.ConditionFalse}},
 | 
				
			||||||
 | 
								pod:                                   testPod.DeepCopy(),
 | 
				
			||||||
 | 
								tolerationSeconds:                     0,
 | 
				
			||||||
 | 
								expectedWaitForPodCondition:           "terminating",
 | 
				
			||||||
 | 
								enableSeparateTaintEvictionController: true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:                                  "Taint based evictions for NodeUnreachable; separate taint eviction controller disabled",
 | 
				
			||||||
 | 
								nodeTaints:                            []v1.Taint{{Key: v1.TaintNodeUnreachable, Effect: v1.TaintEffectNoExecute}},
 | 
				
			||||||
 | 
								nodeConditions:                        []v1.NodeCondition{{Type: v1.NodeReady, Status: v1.ConditionUnknown}},
 | 
				
			||||||
 | 
								enableSeparateTaintEvictionController: false,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:                                  "Taint based evictions for NodeUnreachable; separate taint eviction controller enabled",
 | 
				
			||||||
 | 
								nodeTaints:                            []v1.Taint{{Key: v1.TaintNodeUnreachable, Effect: v1.TaintEffectNoExecute}},
 | 
				
			||||||
 | 
								nodeConditions:                        []v1.NodeCondition{{Type: v1.NodeReady, Status: v1.ConditionUnknown}},
 | 
				
			||||||
 | 
								enableSeparateTaintEvictionController: true,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -249,6 +333,8 @@ func TestTaintBasedEvictions(t *testing.T) {
 | 
				
			|||||||
	)
 | 
						)
 | 
				
			||||||
	for _, test := range tests {
 | 
						for _, test := range tests {
 | 
				
			||||||
		t.Run(test.name, func(t *testing.T) {
 | 
							t.Run(test.name, func(t *testing.T) {
 | 
				
			||||||
 | 
								defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.SeparateTaintEvictionController, test.enableSeparateTaintEvictionController)()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			testCtx := testutils.InitTestAPIServer(t, "taint-based-evictions", admission)
 | 
								testCtx := testutils.InitTestAPIServer(t, "taint-based-evictions", admission)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Build clientset and informers for controllers.
 | 
								// Build clientset and informers for controllers.
 | 
				
			||||||
@@ -288,6 +374,18 @@ func TestTaintBasedEvictions(t *testing.T) {
 | 
				
			|||||||
			// Run the controller
 | 
								// Run the controller
 | 
				
			||||||
			go nc.Run(testCtx.Ctx)
 | 
								go nc.Run(testCtx.Ctx)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Start TaintEvictionController
 | 
				
			||||||
 | 
								if test.enableSeparateTaintEvictionController {
 | 
				
			||||||
 | 
									tm, _ := tainteviction.New(
 | 
				
			||||||
 | 
										testCtx.Ctx,
 | 
				
			||||||
 | 
										testCtx.ClientSet,
 | 
				
			||||||
 | 
										externalInformers.Core().V1().Pods(),
 | 
				
			||||||
 | 
										externalInformers.Core().V1().Nodes(),
 | 
				
			||||||
 | 
										names.TaintEvictionController,
 | 
				
			||||||
 | 
									)
 | 
				
			||||||
 | 
									go tm.Run(testCtx.Ctx)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			nodeRes := v1.ResourceList{
 | 
								nodeRes := v1.ResourceList{
 | 
				
			||||||
				v1.ResourceCPU:    resource.MustParse("4000m"),
 | 
									v1.ResourceCPU:    resource.MustParse("4000m"),
 | 
				
			||||||
				v1.ResourceMemory: resource.MustParse("16Gi"),
 | 
									v1.ResourceMemory: resource.MustParse("16Gi"),
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user