k8s.io/kubernetes@v1.29.3/test/integration/framework/util.go (about) 1 /* 2 Copyright 2017 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 // TODO: This file can potentially be moved to a common place used by both e2e and integration tests. 18 19 package framework 20 21 import ( 22 "context" 23 "fmt" 24 "testing" 25 "time" 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 "k8s.io/klog/v2" 32 nodectlr "k8s.io/kubernetes/pkg/controller/nodelifecycle" 33 ) 34 35 const ( 36 // poll is how often to Poll pods, nodes and claims. 37 poll = 2 * time.Second 38 39 // singleCallTimeout is how long to try single API calls (like 'get' or 'list'). Used to prevent 40 // transient failures from failing tests. 41 singleCallTimeout = 5 * time.Minute 42 ) 43 44 // CreateNamespaceOrDie creates a namespace. 45 func CreateNamespaceOrDie(c clientset.Interface, baseName string, t testing.TB) *v1.Namespace { 46 ns := &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: baseName}} 47 result, err := c.CoreV1().Namespaces().Create(context.TODO(), ns, metav1.CreateOptions{}) 48 if err != nil { 49 t.Fatalf("Failed to create namespace: %v", err) 50 } 51 return result 52 } 53 54 // DeleteNamespaceOrDie deletes a namespace. 55 func DeleteNamespaceOrDie(c clientset.Interface, ns *v1.Namespace, t testing.TB) { 56 err := c.CoreV1().Namespaces().Delete(context.TODO(), ns.Name, metav1.DeleteOptions{}) 57 if err != nil { 58 t.Fatalf("Failed to delete namespace: %v", err) 59 } 60 } 61 62 // waitListAllNodes is a wrapper around listing nodes supporting retries. 63 func waitListAllNodes(c clientset.Interface) (*v1.NodeList, error) { 64 var nodes *v1.NodeList 65 var err error 66 if wait.PollImmediate(poll, singleCallTimeout, func() (bool, error) { 67 nodes, err = c.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{}) 68 if err != nil { 69 return false, err 70 } 71 return true, nil 72 }) != nil { 73 return nodes, err 74 } 75 return nodes, nil 76 } 77 78 // Filter filters nodes in NodeList in place, removing nodes that do not 79 // satisfy the given condition 80 func Filter(nodeList *v1.NodeList, fn func(node v1.Node) bool) { 81 var l []v1.Node 82 83 for _, node := range nodeList.Items { 84 if fn(node) { 85 l = append(l, node) 86 } 87 } 88 nodeList.Items = l 89 } 90 91 // IsNodeSchedulable returns true if: 92 // 1) doesn't have "unschedulable" field set 93 // 2) it also returns true from IsNodeReady 94 func IsNodeSchedulable(node *v1.Node) bool { 95 if node == nil { 96 return false 97 } 98 return !node.Spec.Unschedulable && IsNodeReady(node) 99 } 100 101 // IsNodeReady returns true if: 102 // 1) it's Ready condition is set to true 103 // 2) doesn't have NetworkUnavailable condition set to true 104 func IsNodeReady(node *v1.Node) bool { 105 nodeReady := IsConditionSetAsExpected(node, v1.NodeReady, true) 106 networkReady := isConditionUnset(node, v1.NodeNetworkUnavailable) || 107 IsConditionSetAsExpectedSilent(node, v1.NodeNetworkUnavailable, false) 108 return nodeReady && networkReady 109 } 110 111 // IsConditionSetAsExpected returns a wantTrue value if the node has a match to the conditionType, otherwise returns an opposite value of the wantTrue with detailed logging. 112 func IsConditionSetAsExpected(node *v1.Node, conditionType v1.NodeConditionType, wantTrue bool) bool { 113 return isNodeConditionSetAsExpected(node, conditionType, wantTrue, false) 114 } 115 116 // IsConditionSetAsExpectedSilent returns a wantTrue value if the node has a match to the conditionType, otherwise returns an opposite value of the wantTrue. 117 func IsConditionSetAsExpectedSilent(node *v1.Node, conditionType v1.NodeConditionType, wantTrue bool) bool { 118 return isNodeConditionSetAsExpected(node, conditionType, wantTrue, true) 119 } 120 121 // isConditionUnset returns true if conditions of the given node do not have a match to the given conditionType, otherwise false. 122 func isConditionUnset(node *v1.Node, conditionType v1.NodeConditionType) bool { 123 for _, cond := range node.Status.Conditions { 124 if cond.Type == conditionType { 125 return false 126 } 127 } 128 return true 129 } 130 131 // isNodeConditionSetAsExpected checks a node for a condition, and returns 'true' if the wanted value is the same as the condition value, useful for polling until a condition on a node is met. 132 func isNodeConditionSetAsExpected(node *v1.Node, conditionType v1.NodeConditionType, wantTrue, silent bool) bool { 133 // Check the node readiness condition (logging all). 134 for _, cond := range node.Status.Conditions { 135 // Ensure that the condition type and the status matches as desired. 136 if cond.Type == conditionType { 137 // For NodeReady condition we need to check Taints as well 138 if cond.Type == v1.NodeReady { 139 hasNodeControllerTaints := false 140 // For NodeReady we need to check if Taints are gone as well 141 taints := node.Spec.Taints 142 for _, taint := range taints { 143 if taint.MatchTaint(nodectlr.UnreachableTaintTemplate) || taint.MatchTaint(nodectlr.NotReadyTaintTemplate) { 144 hasNodeControllerTaints = true 145 break 146 } 147 } 148 if wantTrue { 149 if (cond.Status == v1.ConditionTrue) && !hasNodeControllerTaints { 150 return true 151 } 152 msg := "" 153 if !hasNodeControllerTaints { 154 msg = fmt.Sprintf("Condition %s of node %s is %v instead of %t. Reason: %v, message: %v", 155 conditionType, node.Name, cond.Status == v1.ConditionTrue, wantTrue, cond.Reason, cond.Message) 156 } else { 157 msg = fmt.Sprintf("Condition %s of node %s is %v, but Node is tainted by NodeController with %v. Failure", 158 conditionType, node.Name, cond.Status == v1.ConditionTrue, taints) 159 } 160 if !silent { 161 klog.Infof(msg) 162 } 163 return false 164 } 165 // TODO: check if the Node is tainted once we enable NC notReady/unreachable taints by default 166 if cond.Status != v1.ConditionTrue { 167 return true 168 } 169 if !silent { 170 klog.Infof("Condition %s of node %s is %v instead of %t. Reason: %v, message: %v", 171 conditionType, node.Name, cond.Status == v1.ConditionTrue, wantTrue, cond.Reason, cond.Message) 172 } 173 return false 174 } 175 if (wantTrue && (cond.Status == v1.ConditionTrue)) || (!wantTrue && (cond.Status != v1.ConditionTrue)) { 176 return true 177 } 178 if !silent { 179 klog.Infof("Condition %s of node %s is %v instead of %t. Reason: %v, message: %v", 180 conditionType, node.Name, cond.Status == v1.ConditionTrue, wantTrue, cond.Reason, cond.Message) 181 } 182 return false 183 } 184 185 } 186 if !silent { 187 klog.Infof("Couldn't find condition %v on node %v", conditionType, node.Name) 188 } 189 return false 190 }