Skip to content

Commit cfe3763

Browse files
authored
Merge pull request kubernetes#89806 from tanjunchen/e2e-framework-20200403
test/e2e/framework:remove direct k8s.io/kubernetes depedencies
2 parents 28c442e + 87f2ef5 commit cfe3763

File tree

2 files changed

+129
-3
lines changed

2 files changed

+129
-3
lines changed

test/e2e/framework/node/BUILD

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,16 @@ go_library(
1010
importpath = "k8s.io/kubernetes/test/e2e/framework/node",
1111
visibility = ["//visibility:public"],
1212
deps = [
13-
"//pkg/controller:go_default_library",
1413
"//staging/src/k8s.io/api/core/v1:go_default_library",
1514
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
1615
"//staging/src/k8s.io/apimachinery/pkg/fields:go_default_library",
16+
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
1717
"//staging/src/k8s.io/apimachinery/pkg/util/rand:go_default_library",
1818
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
19+
"//staging/src/k8s.io/apimachinery/pkg/util/strategicpatch:go_default_library",
1920
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
2021
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
22+
"//staging/src/k8s.io/client-go/util/retry:go_default_library",
2123
"//test/e2e/framework/log:go_default_library",
2224
"//test/e2e/system:go_default_library",
2325
"//test/utils:go_default_library",

test/e2e/framework/node/resource.go

Lines changed: 126 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package node
1818

1919
import (
2020
"context"
21+
"encoding/json"
2122
"fmt"
2223
"net"
2324
"strings"
@@ -28,10 +29,13 @@ import (
2829

2930
v1 "k8s.io/api/core/v1"
3031
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
32+
"k8s.io/apimachinery/pkg/types"
3133
"k8s.io/apimachinery/pkg/util/rand"
3234
"k8s.io/apimachinery/pkg/util/sets"
35+
"k8s.io/apimachinery/pkg/util/strategicpatch"
36+
"k8s.io/apimachinery/pkg/util/wait"
3337
clientset "k8s.io/client-go/kubernetes"
34-
"k8s.io/kubernetes/pkg/controller"
38+
clientretry "k8s.io/client-go/util/retry"
3539
e2elog "k8s.io/kubernetes/test/e2e/framework/log"
3640
"k8s.io/kubernetes/test/e2e/system"
3741
)
@@ -62,6 +66,13 @@ var (
6266
Key: v1.TaintNodeNotReady,
6367
Effect: v1.TaintEffectNoExecute,
6468
}
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+
}
6576
)
6677

6778
// 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
550561

551562
// RemoveTaintOffNode removes the given taint from the given node.
552563
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)
554565

555566
// TODO use wrapper methods in expect.go after removing core e2e dependency on node
556567
gomega.ExpectWithOffset(2, err).NotTo(gomega.HaveOccurred())
557568
verifyThatTaintIsGone(c, nodeName, &taint)
558569
}
559570

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+
560684
func verifyThatTaintIsGone(c clientset.Interface, nodeName string, taint *v1.Taint) {
561685
ginkgo.By("verifying the node doesn't have the taint " + taint.ToString())
562686
nodeUpdated, err := c.CoreV1().Nodes().Get(context.TODO(), nodeName, metav1.GetOptions{})

0 commit comments

Comments
 (0)