sigs.k8s.io/cluster-api-provider-azure@v1.14.3/test/e2e/kubernetes/node/node.go (about) 1 //go:build e2e 2 // +build e2e 3 4 /* 5 Copyright 2020 The Kubernetes Authors. 6 7 Licensed under the Apache License, Version 2.0 (the "License"); 8 you may not use this file except in compliance with the License. 9 You may obtain a copy of the License at 10 11 http://www.apache.org/licenses/LICENSE-2.0 12 13 Unless required by applicable law or agreed to in writing, software 14 distributed under the License is distributed on an "AS IS" BASIS, 15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 See the License for the specific language governing permissions and 17 limitations under the License. 18 */ 19 20 package node 21 22 import ( 23 "context" 24 "encoding/json" 25 "fmt" 26 "log" 27 "strings" 28 "time" 29 30 . "github.com/onsi/gomega" 31 corev1 "k8s.io/api/core/v1" 32 "k8s.io/apimachinery/pkg/api/equality" 33 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 34 "k8s.io/apimachinery/pkg/types" 35 "k8s.io/apimachinery/pkg/util/strategicpatch" 36 "k8s.io/client-go/kubernetes" 37 "sigs.k8s.io/cluster-api-provider-azure/test/e2e/kubernetes/windows" 38 ) 39 40 const ( 41 nodeOperationTimeout = 30 * time.Second 42 nodeOperationSleepBetweenRetries = 3 * time.Second 43 ) 44 45 func GetWindowsVersion(ctx context.Context, clientset *kubernetes.Clientset) (windows.OSVersion, error) { 46 options := metav1.ListOptions{ 47 LabelSelector: "kubernetes.io/os=windows", 48 } 49 var result *corev1.NodeList 50 Eventually(func(g Gomega) { 51 var err error 52 result, err = clientset.CoreV1().Nodes().List(ctx, options) 53 g.Expect(err).NotTo(HaveOccurred()) 54 g.Expect(result.Items).NotTo(BeEmpty()) 55 }, nodeOperationTimeout, nodeOperationSleepBetweenRetries).Should(Succeed()) 56 57 kernalVersion := result.Items[0].Status.NodeInfo.KernelVersion 58 kernalVersions := strings.Split(kernalVersion, ".") 59 if len(kernalVersions) != 4 { 60 return windows.Unknown, fmt.Errorf("not a valid Windows kernel version: %s", kernalVersion) 61 } 62 63 switch kernalVersions[2] { 64 case "17763": 65 return windows.LTSC2019, nil 66 default: 67 return windows.LTSC2019, nil 68 } 69 } 70 71 func TaintNode(clientset *kubernetes.Clientset, options metav1.ListOptions, taint *corev1.Taint) error { 72 var result *corev1.NodeList 73 Eventually(func(g Gomega) { 74 var err error 75 result, err = clientset.CoreV1().Nodes().List(context.Background(), options) 76 g.Expect(err).NotTo(HaveOccurred()) 77 g.Expect(result.Items).NotTo(BeEmpty()) 78 }, nodeOperationTimeout, nodeOperationSleepBetweenRetries).Should(Succeed()) 79 80 for i := range result.Items { 81 newNode, needsUpdate := addOrUpdateTaint(&result.Items[i], taint) 82 if !needsUpdate { 83 continue 84 } 85 86 err := PatchNodeTaints(clientset, newNode.Name, &result.Items[i], newNode) 87 if err != nil { 88 return err 89 } 90 } 91 92 return nil 93 } 94 95 // PatchNodeTaints is taken from https://github.com/kubernetes/kubernetes/blob/v1.21.1/staging/src/k8s.io/cloud-provider/node/helpers/taints.go#L91 96 func PatchNodeTaints(clientset *kubernetes.Clientset, nodeName string, oldNode *corev1.Node, newNode *corev1.Node) error { 97 oldData, err := json.Marshal(oldNode) 98 if err != nil { 99 return fmt.Errorf("failed to marshal old node %#v for node %q: %w", oldNode, nodeName, err) 100 } 101 102 newTaints := newNode.Spec.Taints 103 newNodeClone := oldNode.DeepCopy() 104 newNodeClone.Spec.Taints = newTaints 105 newData, err := json.Marshal(newNodeClone) 106 if err != nil { 107 return fmt.Errorf("failed to marshal new node %#v for node %q: %w", newNodeClone, nodeName, err) 108 } 109 110 patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, corev1.Node{}) 111 if err != nil { 112 return fmt.Errorf("failed to create patch for node %q: %w", nodeName, err) 113 } 114 115 Eventually(func(g Gomega) { 116 _, err := clientset.CoreV1().Nodes().Patch(context.Background(), nodeName, types.StrategicMergePatchType, patchBytes, metav1.PatchOptions{}) 117 if err != nil { 118 log.Printf("Error updating node taints on node %s:%s\n", nodeName, err.Error()) 119 } 120 g.Expect(err).NotTo(HaveOccurred()) 121 }, nodeOperationTimeout, nodeOperationSleepBetweenRetries).Should(Succeed()) 122 return err 123 } 124 125 // From https://github.com/kubernetes/kubernetes/blob/v1.21.1/staging/src/k8s.io/cloud-provider/node/helpers/taints.go#L116 126 // addOrUpdateTaint tries to add a taint to annotations list. Returns a new copy of updated Node and true if something was updated 127 // false otherwise. 128 func addOrUpdateTaint(node *corev1.Node, taint *corev1.Taint) (*corev1.Node, bool) { 129 newNode := node.DeepCopy() 130 nodeTaints := newNode.Spec.Taints 131 132 var newTaints []corev1.Taint 133 updated := false 134 for i := range nodeTaints { 135 if taint.MatchTaint(&nodeTaints[i]) { 136 if equality.Semantic.DeepEqual(*taint, nodeTaints[i]) { 137 return newNode, false 138 } 139 newTaints = append(newTaints, *taint) 140 updated = true 141 continue 142 } 143 144 newTaints = append(newTaints, nodeTaints[i]) 145 } 146 147 if !updated { 148 newTaints = append(newTaints, *taint) 149 } 150 151 newNode.Spec.Taints = newTaints 152 return newNode, true 153 }