k8s.io/kubernetes@v1.29.3/test/e2e/framework/node/helper.go (about) 1 /* 2 Copyright 2014 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package node 18 19 import ( 20 "context" 21 "fmt" 22 "time" 23 24 "github.com/onsi/ginkgo/v2" 25 "github.com/onsi/gomega" 26 27 v1 "k8s.io/api/core/v1" 28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 29 "k8s.io/apimachinery/pkg/util/wait" 30 clientset "k8s.io/client-go/kubernetes" 31 testutils "k8s.io/kubernetes/test/utils" 32 33 "k8s.io/kubernetes/test/e2e/framework" 34 ) 35 36 const ( 37 // Minimal number of nodes for the cluster to be considered large. 38 largeClusterThreshold = 100 39 ) 40 41 // WaitForAllNodesSchedulable waits up to timeout for all 42 // (but TestContext.AllowedNotReadyNodes) to become schedulable. 43 func WaitForAllNodesSchedulable(ctx context.Context, c clientset.Interface, timeout time.Duration) error { 44 if framework.TestContext.AllowedNotReadyNodes == -1 { 45 return nil 46 } 47 48 framework.Logf("Waiting up to %v for all (but %d) nodes to be schedulable", timeout, framework.TestContext.AllowedNotReadyNodes) 49 return wait.PollImmediateWithContext( 50 ctx, 51 30*time.Second, 52 timeout, 53 CheckReadyForTests(ctx, c, framework.TestContext.NonblockingTaints, framework.TestContext.AllowedNotReadyNodes, largeClusterThreshold), 54 ) 55 } 56 57 // AddOrUpdateLabelOnNode adds the given label key and value to the given node or updates value. 58 func AddOrUpdateLabelOnNode(c clientset.Interface, nodeName string, labelKey, labelValue string) { 59 framework.ExpectNoError(testutils.AddLabelsToNode(c, nodeName, map[string]string{labelKey: labelValue})) 60 } 61 62 // ExpectNodeHasLabel expects that the given node has the given label pair. 63 func ExpectNodeHasLabel(ctx context.Context, c clientset.Interface, nodeName string, labelKey string, labelValue string) { 64 ginkgo.By("verifying the node has the label " + labelKey + " " + labelValue) 65 node, err := c.CoreV1().Nodes().Get(ctx, nodeName, metav1.GetOptions{}) 66 framework.ExpectNoError(err) 67 gomega.Expect(node.Labels).To(gomega.HaveKeyWithValue(labelKey, labelValue)) 68 } 69 70 // RemoveLabelOffNode is for cleaning up labels temporarily added to node, 71 // won't fail if target label doesn't exist or has been removed. 72 func RemoveLabelOffNode(c clientset.Interface, nodeName string, labelKey string) { 73 ginkgo.By("removing the label " + labelKey + " off the node " + nodeName) 74 framework.ExpectNoError(testutils.RemoveLabelOffNode(c, nodeName, []string{labelKey})) 75 76 ginkgo.By("verifying the node doesn't have the label " + labelKey) 77 framework.ExpectNoError(testutils.VerifyLabelsRemoved(c, nodeName, []string{labelKey})) 78 } 79 80 // ExpectNodeHasTaint expects that the node has the given taint. 81 func ExpectNodeHasTaint(ctx context.Context, c clientset.Interface, nodeName string, taint *v1.Taint) { 82 ginkgo.By("verifying the node has the taint " + taint.ToString()) 83 if has, err := NodeHasTaint(ctx, c, nodeName, taint); !has { 84 framework.ExpectNoError(err) 85 framework.Failf("Failed to find taint %s on node %s", taint.ToString(), nodeName) 86 } 87 } 88 89 // NodeHasTaint returns true if the node has the given taint, else returns false. 90 func NodeHasTaint(ctx context.Context, c clientset.Interface, nodeName string, taint *v1.Taint) (bool, error) { 91 node, err := c.CoreV1().Nodes().Get(ctx, nodeName, metav1.GetOptions{}) 92 if err != nil { 93 return false, err 94 } 95 96 nodeTaints := node.Spec.Taints 97 98 if len(nodeTaints) == 0 || !taintExists(nodeTaints, taint) { 99 return false, nil 100 } 101 return true, nil 102 } 103 104 // AllNodesReady checks whether all registered nodes are ready. Setting -1 on 105 // framework.TestContext.AllowedNotReadyNodes will bypass the post test node readiness check. 106 // TODO: we should change the AllNodesReady call in AfterEach to WaitForAllNodesHealthy, 107 // and figure out how to do it in a configurable way, as we can't expect all setups to run 108 // default test add-ons. 109 func AllNodesReady(ctx context.Context, c clientset.Interface, timeout time.Duration) error { 110 if err := allNodesReady(ctx, c, timeout); err != nil { 111 return fmt.Errorf("checking for ready nodes: %w", err) 112 } 113 return nil 114 } 115 116 func allNodesReady(ctx context.Context, c clientset.Interface, timeout time.Duration) error { 117 if framework.TestContext.AllowedNotReadyNodes == -1 { 118 return nil 119 } 120 121 framework.Logf("Waiting up to %v for all (but %d) nodes to be ready", timeout, framework.TestContext.AllowedNotReadyNodes) 122 123 var notReady []*v1.Node 124 err := wait.PollUntilContextTimeout(ctx, framework.Poll, timeout, true, func(ctx context.Context) (bool, error) { 125 notReady = nil 126 // It should be OK to list unschedulable Nodes here. 127 nodes, err := c.CoreV1().Nodes().List(ctx, metav1.ListOptions{}) 128 if err != nil { 129 return false, err 130 } 131 for i := range nodes.Items { 132 node := &nodes.Items[i] 133 if !IsConditionSetAsExpected(node, v1.NodeReady, true) { 134 notReady = append(notReady, node) 135 } 136 } 137 // Framework allows for <TestContext.AllowedNotReadyNodes> nodes to be non-ready, 138 // to make it possible e.g. for incorrect deployment of some small percentage 139 // of nodes (which we allow in cluster validation). Some nodes that are not 140 // provisioned correctly at startup will never become ready (e.g. when something 141 // won't install correctly), so we can't expect them to be ready at any point. 142 return len(notReady) <= framework.TestContext.AllowedNotReadyNodes, nil 143 }) 144 145 if err != nil && !wait.Interrupted(err) { 146 return err 147 } 148 149 if len(notReady) > framework.TestContext.AllowedNotReadyNodes { 150 msg := "" 151 for _, node := range notReady { 152 msg = fmt.Sprintf("%s, %s", msg, node.Name) 153 } 154 return fmt.Errorf("Not ready nodes: %#v", msg) 155 } 156 return nil 157 } 158 159 // taintExists checks if the given taint exists in list of taints. Returns true if exists false otherwise. 160 func taintExists(taints []v1.Taint, taintToFind *v1.Taint) bool { 161 for _, taint := range taints { 162 if taint.MatchTaint(taintToFind) { 163 return true 164 } 165 } 166 return false 167 }