@@ -18,6 +18,7 @@ package node
18
18
19
19
import (
20
20
"context"
21
+ "encoding/json"
21
22
"fmt"
22
23
"net"
23
24
"strings"
@@ -28,10 +29,13 @@ import (
28
29
29
30
v1 "k8s.io/api/core/v1"
30
31
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
32
+ "k8s.io/apimachinery/pkg/types"
31
33
"k8s.io/apimachinery/pkg/util/rand"
32
34
"k8s.io/apimachinery/pkg/util/sets"
35
+ "k8s.io/apimachinery/pkg/util/strategicpatch"
36
+ "k8s.io/apimachinery/pkg/util/wait"
33
37
clientset "k8s.io/client-go/kubernetes"
34
- "k8s.io/kubernetes/pkg/controller "
38
+ clientretry "k8s.io/client-go/util/retry "
35
39
e2elog "k8s.io/kubernetes/test/e2e/framework/log"
36
40
"k8s.io/kubernetes/test/e2e/system"
37
41
)
62
66
Key : v1 .TaintNodeNotReady ,
63
67
Effect : v1 .TaintEffectNoExecute ,
64
68
}
69
+
70
+ // updateTaintBackOff contains the maximum retries and the wait interval between two retries.
71
+ updateTaintBackOff = wait.Backoff {
72
+ Steps : 5 ,
73
+ Duration : 100 * time .Millisecond ,
74
+ Jitter : 1.0 ,
75
+ }
65
76
)
66
77
67
78
// PodNode is a pod-node pair indicating which node a given pod is running on
@@ -550,13 +561,126 @@ func CreatePodsPerNodeForSimpleApp(c clientset.Interface, namespace, appName str
550
561
551
562
// RemoveTaintOffNode removes the given taint from the given node.
552
563
func RemoveTaintOffNode (c clientset.Interface , nodeName string , taint v1.Taint ) {
553
- err := controller . RemoveTaintOffNode (c , nodeName , nil , & taint )
564
+ err := removeNodeTaint (c , nodeName , nil , & taint )
554
565
555
566
// TODO use wrapper methods in expect.go after removing core e2e dependency on node
556
567
gomega .ExpectWithOffset (2 , err ).NotTo (gomega .HaveOccurred ())
557
568
verifyThatTaintIsGone (c , nodeName , & taint )
558
569
}
559
570
571
+ // removeNodeTaint is for cleaning up taints temporarily added to node,
572
+ // won't fail if target taint doesn't exist or has been removed.
573
+ // If passed a node it'll check if there's anything to be done, if taint is not present it won't issue
574
+ // any API calls.
575
+ func removeNodeTaint (c clientset.Interface , nodeName string , node * v1.Node , taints ... * v1.Taint ) error {
576
+ if len (taints ) == 0 {
577
+ return nil
578
+ }
579
+ // Short circuit for limiting amount of API calls.
580
+ if node != nil {
581
+ match := false
582
+ for _ , taint := range taints {
583
+ if taintExists (node .Spec .Taints , taint ) {
584
+ match = true
585
+ break
586
+ }
587
+ }
588
+ if ! match {
589
+ return nil
590
+ }
591
+ }
592
+
593
+ firstTry := true
594
+ return clientretry .RetryOnConflict (updateTaintBackOff , func () error {
595
+ var err error
596
+ var oldNode * v1.Node
597
+ // First we try getting node from the API server cache, as it's cheaper. If it fails
598
+ // we get it from etcd to be sure to have fresh data.
599
+ if firstTry {
600
+ oldNode , err = c .CoreV1 ().Nodes ().Get (context .TODO (), nodeName , metav1.GetOptions {ResourceVersion : "0" })
601
+ firstTry = false
602
+ } else {
603
+ oldNode , err = c .CoreV1 ().Nodes ().Get (context .TODO (), nodeName , metav1.GetOptions {})
604
+ }
605
+ if err != nil {
606
+ return err
607
+ }
608
+
609
+ var newNode * v1.Node
610
+ oldNodeCopy := oldNode
611
+ updated := false
612
+ for _ , taint := range taints {
613
+ curNewNode , ok , err := removeTaint (oldNodeCopy , taint )
614
+ if err != nil {
615
+ return fmt .Errorf ("failed to remove taint of node" )
616
+ }
617
+ updated = updated || ok
618
+ newNode = curNewNode
619
+ oldNodeCopy = curNewNode
620
+ }
621
+ if ! updated {
622
+ return nil
623
+ }
624
+ return patchNodeTaints (c , nodeName , oldNode , newNode )
625
+ })
626
+ }
627
+
628
+ // patchNodeTaints patches node's taints.
629
+ func patchNodeTaints (c clientset.Interface , nodeName string , oldNode * v1.Node , newNode * v1.Node ) error {
630
+ oldData , err := json .Marshal (oldNode )
631
+ if err != nil {
632
+ return fmt .Errorf ("failed to marshal old node %#v for node %q: %v" , oldNode , nodeName , err )
633
+ }
634
+
635
+ newTaints := newNode .Spec .Taints
636
+ newNodeClone := oldNode .DeepCopy ()
637
+ newNodeClone .Spec .Taints = newTaints
638
+ newData , err := json .Marshal (newNodeClone )
639
+ if err != nil {
640
+ return fmt .Errorf ("failed to marshal new node %#v for node %q: %v" , newNodeClone , nodeName , err )
641
+ }
642
+
643
+ patchBytes , err := strategicpatch .CreateTwoWayMergePatch (oldData , newData , v1.Node {})
644
+ if err != nil {
645
+ return fmt .Errorf ("failed to create patch for node %q: %v" , nodeName , err )
646
+ }
647
+
648
+ _ , err = c .CoreV1 ().Nodes ().Patch (context .TODO (), nodeName , types .StrategicMergePatchType , patchBytes , metav1.PatchOptions {})
649
+ return err
650
+ }
651
+
652
+ // removeTaint tries to remove a taint from annotations list. Returns a new copy of updated Node and true if something was updated
653
+ // false otherwise.
654
+ func removeTaint (node * v1.Node , taint * v1.Taint ) (* v1.Node , bool , error ) {
655
+ newNode := node .DeepCopy ()
656
+ nodeTaints := newNode .Spec .Taints
657
+ if len (nodeTaints ) == 0 {
658
+ return newNode , false , nil
659
+ }
660
+
661
+ if ! taintExists (nodeTaints , taint ) {
662
+ return newNode , false , nil
663
+ }
664
+
665
+ newTaints , _ := deleteTaint (nodeTaints , taint )
666
+ newNode .Spec .Taints = newTaints
667
+ return newNode , true , nil
668
+ }
669
+
670
+ // deleteTaint removes all the taints that have the same key and effect to given taintToDelete.
671
+ func deleteTaint (taints []v1.Taint , taintToDelete * v1.Taint ) ([]v1.Taint , bool ) {
672
+ var newTaints []v1.Taint
673
+ deleted := false
674
+ for i := range taints {
675
+ if taintToDelete .MatchTaint (& taints [i ]) {
676
+ deleted = true
677
+ continue
678
+ }
679
+ newTaints = append (newTaints , taints [i ])
680
+ }
681
+ return newTaints , deleted
682
+ }
683
+
560
684
func verifyThatTaintIsGone (c clientset.Interface , nodeName string , taint * v1.Taint ) {
561
685
ginkgo .By ("verifying the node doesn't have the taint " + taint .ToString ())
562
686
nodeUpdated , err := c .CoreV1 ().Nodes ().Get (context .TODO (), nodeName , metav1.GetOptions {})
0 commit comments