Skip to content

Commit 37cda82

Browse files
authored
Merge pull request kubernetes#93722 from liggitt/taint-evict
Do not evict pods which tolerate all NoExecute taints
2 parents 854ef30 + 892bdf9 commit 37cda82

File tree

3 files changed

+86
-5
lines changed

3 files changed

+86
-5
lines changed

pkg/controller/nodelifecycle/scheduler/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ go_test(
4242
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
4343
"//staging/src/k8s.io/apimachinery/pkg/fields:go_default_library",
4444
"//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library",
45+
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
4546
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
4647
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
4748
"//staging/src/k8s.io/client-go/testing:go_default_library",

pkg/controller/nodelifecycle/scheduler/taint_manager.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,8 @@ func (tc *NoExecuteTaintManager) processPodOnNode(
358358
minTolerationTime := getMinTolerationTime(usedTolerations)
359359
// getMinTolerationTime returns negative value to denote infinite toleration.
360360
if minTolerationTime < 0 {
361-
klog.V(4).Infof("New tolerations for %v tolerate forever. Scheduled deletion won't be cancelled if already scheduled.", podNamespacedName.String())
361+
klog.V(4).Infof("Current tolerations for %v tolerate forever, cancelling any scheduled deletion.", podNamespacedName.String())
362+
tc.cancelWorkWithEvent(podNamespacedName)
362363
return
363364
}
364365

pkg/controller/nodelifecycle/scheduler/taint_manager_test.go

Lines changed: 83 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
v1 "k8s.io/api/core/v1"
2828
"k8s.io/apimachinery/pkg/fields"
2929
"k8s.io/apimachinery/pkg/labels"
30+
"k8s.io/apimachinery/pkg/types"
3031
"k8s.io/client-go/kubernetes/fake"
3132
"k8s.io/kubernetes/pkg/controller/testutil"
3233

@@ -83,10 +84,20 @@ func (p *podHolder) setPod(pod *v1.Pod) {
8384
}
8485

8586
type nodeHolder struct {
87+
lock sync.Mutex
88+
8689
node *v1.Node
8790
}
8891

92+
func (n *nodeHolder) setNode(node *v1.Node) {
93+
n.lock.Lock()
94+
defer n.lock.Unlock()
95+
n.node = node
96+
}
97+
8998
func (n *nodeHolder) getNode(name string) (*v1.Node, error) {
99+
n.lock.Lock()
100+
defer n.lock.Unlock()
90101
return n.node, nil
91102
}
92103

@@ -362,7 +373,7 @@ func TestCreateNode(t *testing.T) {
362373
for _, item := range testCases {
363374
stopCh := make(chan struct{})
364375
fakeClientset := fake.NewSimpleClientset(&v1.PodList{Items: item.pods})
365-
controller := NewNoExecuteTaintManager(fakeClientset, getPodFromClientset(fakeClientset), (&nodeHolder{item.node}).getNode, getPodsAssignedToNode(fakeClientset))
376+
controller := NewNoExecuteTaintManager(fakeClientset, getPodFromClientset(fakeClientset), (&nodeHolder{node: item.node}).getNode, getPodsAssignedToNode(fakeClientset))
366377
controller.recorder = testutil.NewFakeRecorder()
367378
go controller.Run(stopCh)
368379
controller.NodeUpdated(nil, item.node)
@@ -483,7 +494,7 @@ func TestUpdateNode(t *testing.T) {
483494
for _, item := range testCases {
484495
stopCh := make(chan struct{})
485496
fakeClientset := fake.NewSimpleClientset(&v1.PodList{Items: item.pods})
486-
controller := NewNoExecuteTaintManager(fakeClientset, getPodFromClientset(fakeClientset), (&nodeHolder{item.newNode}).getNode, getPodsAssignedToNode(fakeClientset))
497+
controller := NewNoExecuteTaintManager(fakeClientset, getPodFromClientset(fakeClientset), (&nodeHolder{node: item.newNode}).getNode, getPodsAssignedToNode(fakeClientset))
487498
controller.recorder = testutil.NewFakeRecorder()
488499
go controller.Run(stopCh)
489500
controller.NodeUpdated(item.oldNode, item.newNode)
@@ -506,6 +517,74 @@ func TestUpdateNode(t *testing.T) {
506517
}
507518
}
508519

520+
func TestUpdateNodeWithMultipleTaints(t *testing.T) {
521+
taint1 := createNoExecuteTaint(1)
522+
taint2 := createNoExecuteTaint(2)
523+
524+
minute := int64(60)
525+
pod := testutil.NewPod("pod1", "node1")
526+
pod.Spec.Tolerations = []v1.Toleration{
527+
{Key: taint1.Key, Operator: v1.TolerationOpExists, Effect: v1.TaintEffectNoExecute},
528+
{Key: taint2.Key, Operator: v1.TolerationOpExists, Effect: v1.TaintEffectNoExecute, TolerationSeconds: &minute},
529+
}
530+
podNamespacedName := types.NamespacedName{Namespace: pod.Namespace, Name: pod.Name}
531+
532+
untaintedNode := testutil.NewNode("node1")
533+
534+
doubleTaintedNode := testutil.NewNode("node1")
535+
doubleTaintedNode.Spec.Taints = []v1.Taint{taint1, taint2}
536+
537+
singleTaintedNode := testutil.NewNode("node1")
538+
singleTaintedNode.Spec.Taints = []v1.Taint{taint1}
539+
540+
stopCh := make(chan struct{})
541+
fakeClientset := fake.NewSimpleClientset(pod)
542+
holder := &nodeHolder{node: untaintedNode}
543+
controller := NewNoExecuteTaintManager(fakeClientset, getPodFromClientset(fakeClientset), (holder).getNode, getPodsAssignedToNode(fakeClientset))
544+
controller.recorder = testutil.NewFakeRecorder()
545+
go controller.Run(stopCh)
546+
547+
// no taint
548+
holder.setNode(untaintedNode)
549+
controller.handleNodeUpdate(nodeUpdateItem{"node1"})
550+
// verify pod is not queued for deletion
551+
if controller.taintEvictionQueue.GetWorkerUnsafe(podNamespacedName.String()) != nil {
552+
t.Fatalf("pod queued for deletion with no taints")
553+
}
554+
555+
// no taint -> infinitely tolerated taint
556+
holder.setNode(singleTaintedNode)
557+
controller.handleNodeUpdate(nodeUpdateItem{"node1"})
558+
// verify pod is not queued for deletion
559+
if controller.taintEvictionQueue.GetWorkerUnsafe(podNamespacedName.String()) != nil {
560+
t.Fatalf("pod queued for deletion with permanently tolerated taint")
561+
}
562+
563+
// infinitely tolerated taint -> temporarily tolerated taint
564+
holder.setNode(doubleTaintedNode)
565+
controller.handleNodeUpdate(nodeUpdateItem{"node1"})
566+
// verify pod is queued for deletion
567+
if controller.taintEvictionQueue.GetWorkerUnsafe(podNamespacedName.String()) == nil {
568+
t.Fatalf("pod not queued for deletion after addition of temporarily tolerated taint")
569+
}
570+
571+
// temporarily tolerated taint -> infinitely tolerated taint
572+
holder.setNode(singleTaintedNode)
573+
controller.handleNodeUpdate(nodeUpdateItem{"node1"})
574+
// verify pod is not queued for deletion
575+
if controller.taintEvictionQueue.GetWorkerUnsafe(podNamespacedName.String()) != nil {
576+
t.Fatalf("pod queued for deletion after removal of temporarily tolerated taint")
577+
}
578+
579+
// verify pod is not deleted
580+
for _, action := range fakeClientset.Actions() {
581+
if action.GetVerb() == "delete" && action.GetResource().Resource == "pods" {
582+
t.Error("Unexpected deletion")
583+
}
584+
}
585+
close(stopCh)
586+
}
587+
509588
func TestUpdateNodeWithMultiplePods(t *testing.T) {
510589
testCases := []struct {
511590
description string
@@ -549,7 +628,7 @@ func TestUpdateNodeWithMultiplePods(t *testing.T) {
549628
stopCh := make(chan struct{})
550629
fakeClientset := fake.NewSimpleClientset(&v1.PodList{Items: item.pods})
551630
sort.Sort(item.expectedDeleteTimes)
552-
controller := NewNoExecuteTaintManager(fakeClientset, getPodFromClientset(fakeClientset), (&nodeHolder{item.newNode}).getNode, getPodsAssignedToNode(fakeClientset))
631+
controller := NewNoExecuteTaintManager(fakeClientset, getPodFromClientset(fakeClientset), (&nodeHolder{node: item.newNode}).getNode, getPodsAssignedToNode(fakeClientset))
553632
controller.recorder = testutil.NewFakeRecorder()
554633
go controller.Run(stopCh)
555634
controller.NodeUpdated(item.oldNode, item.newNode)
@@ -749,7 +828,7 @@ func TestEventualConsistency(t *testing.T) {
749828
stopCh := make(chan struct{})
750829
fakeClientset := fake.NewSimpleClientset(&v1.PodList{Items: item.pods})
751830
holder := &podHolder{}
752-
controller := NewNoExecuteTaintManager(fakeClientset, holder.getPod, (&nodeHolder{item.newNode}).getNode, getPodsAssignedToNode(fakeClientset))
831+
controller := NewNoExecuteTaintManager(fakeClientset, holder.getPod, (&nodeHolder{node: item.newNode}).getNode, getPodsAssignedToNode(fakeClientset))
753832
controller.recorder = testutil.NewFakeRecorder()
754833
go controller.Run(stopCh)
755834

0 commit comments

Comments
 (0)