k8s.io/kubernetes@v1.29.3/test/e2e/framework/node/wait_test.go (about) 1 /* 2 Copyright 2019 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 "errors" 22 "testing" 23 24 v1 "k8s.io/api/core/v1" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 "k8s.io/apimachinery/pkg/runtime" 27 "k8s.io/client-go/kubernetes/fake" 28 k8stesting "k8s.io/client-go/testing" 29 ) 30 31 // TestCheckReadyForTests specifically is concerned about the multi-node logic 32 // since single node checks are in TestReadyForTests. 33 func TestCheckReadyForTests(t *testing.T) { 34 // This is a duplicate definition of the constant in pkg/controller/service/controller.go 35 labelNodeRoleControlPlane := "node-role.kubernetes.io/control-plane" 36 37 fromVanillaNode := func(f func(*v1.Node)) v1.Node { 38 vanillaNode := &v1.Node{ 39 ObjectMeta: metav1.ObjectMeta{Name: "test-node"}, 40 Status: v1.NodeStatus{ 41 Conditions: []v1.NodeCondition{ 42 {Type: v1.NodeReady, Status: v1.ConditionTrue}, 43 }, 44 }, 45 } 46 f(vanillaNode) 47 return *vanillaNode 48 } 49 50 tcs := []struct { 51 desc string 52 nonblockingTaints string 53 allowedNotReadyNodes int 54 nodes []v1.Node 55 nodeListErr error 56 expected bool 57 expectedErr string 58 }{ 59 { 60 desc: "Vanilla node should pass", 61 nodes: []v1.Node{ 62 fromVanillaNode(func(n *v1.Node) {}), 63 }, 64 expected: true, 65 }, { 66 desc: "Default value for nonblocking taints tolerates control plane taint", 67 nonblockingTaints: `node-role.kubernetes.io/control-plane`, 68 nodes: []v1.Node{ 69 fromVanillaNode(func(n *v1.Node) { 70 n.Spec.Taints = []v1.Taint{{Key: labelNodeRoleControlPlane, Effect: v1.TaintEffectNoSchedule}} 71 }), 72 }, 73 expected: true, 74 }, { 75 desc: "Tainted node should fail if effect is TaintEffectNoExecute", 76 nonblockingTaints: "bar", 77 nodes: []v1.Node{ 78 fromVanillaNode(func(n *v1.Node) { 79 n.Spec.Taints = []v1.Taint{{Key: "foo", Effect: v1.TaintEffectNoExecute}} 80 })}, 81 expected: false, 82 }, { 83 desc: "Tainted node can be allowed via allowedNotReadyNodes", 84 nonblockingTaints: "bar", 85 allowedNotReadyNodes: 1, 86 nodes: []v1.Node{ 87 fromVanillaNode(func(n *v1.Node) { 88 n.Spec.Taints = []v1.Taint{{Key: "foo", Effect: v1.TaintEffectNoExecute}} 89 })}, 90 expected: true, 91 }, { 92 desc: "Multi-node, all OK", 93 nodes: []v1.Node{ 94 fromVanillaNode(func(n *v1.Node) {}), 95 fromVanillaNode(func(n *v1.Node) {}), 96 }, 97 expected: true, 98 }, { 99 desc: "Multi-node, single blocking node blocks", 100 nodes: []v1.Node{ 101 fromVanillaNode(func(n *v1.Node) {}), 102 fromVanillaNode(func(n *v1.Node) { 103 n.Spec.Taints = []v1.Taint{{Key: "foo", Effect: v1.TaintEffectNoSchedule}} 104 }), 105 }, 106 expected: false, 107 }, { 108 desc: "Multi-node, single blocking node allowed via allowedNotReadyNodes", 109 allowedNotReadyNodes: 1, 110 nodes: []v1.Node{ 111 fromVanillaNode(func(n *v1.Node) {}), 112 fromVanillaNode(func(n *v1.Node) { 113 n.Spec.Taints = []v1.Taint{{Key: "foo", Effect: v1.TaintEffectNoSchedule}} 114 }), 115 }, 116 expected: true, 117 }, { 118 desc: "Multi-node, single blocking node allowed via nonblocking taint", 119 nonblockingTaints: "foo", 120 nodes: []v1.Node{ 121 fromVanillaNode(func(n *v1.Node) {}), 122 fromVanillaNode(func(n *v1.Node) { 123 n.Spec.Taints = []v1.Taint{{Key: "foo", Effect: v1.TaintEffectNoSchedule}} 124 }), 125 }, 126 expected: true, 127 }, { 128 desc: "Multi-node, both blocking nodes allowed via separate nonblocking taints", 129 nonblockingTaints: "foo,bar", 130 nodes: []v1.Node{ 131 fromVanillaNode(func(n *v1.Node) {}), 132 fromVanillaNode(func(n *v1.Node) { 133 n.Spec.Taints = []v1.Taint{{Key: "foo", Effect: v1.TaintEffectNoSchedule}} 134 }), 135 fromVanillaNode(func(n *v1.Node) { 136 n.Spec.Taints = []v1.Taint{{Key: "bar", Effect: v1.TaintEffectNoSchedule}} 137 }), 138 }, 139 expected: true, 140 }, { 141 desc: "Multi-node, one blocking node allowed via nonblocking taints still blocked", 142 nonblockingTaints: "foo,notbar", 143 nodes: []v1.Node{ 144 fromVanillaNode(func(n *v1.Node) {}), 145 fromVanillaNode(func(n *v1.Node) { 146 n.Spec.Taints = []v1.Taint{{Key: "foo", Effect: v1.TaintEffectNoSchedule}} 147 }), 148 fromVanillaNode(func(n *v1.Node) { 149 n.Spec.Taints = []v1.Taint{{Key: "bar", Effect: v1.TaintEffectNoSchedule}} 150 }), 151 }, 152 expected: false, 153 }, { 154 desc: "Errors from node list are reported", 155 nodeListErr: errors.New("Forced error"), 156 expected: false, 157 expectedErr: "Forced error", 158 }, 159 } 160 161 // Only determines some logging functionality; not relevant so set to a large value. 162 testLargeClusterThreshold := 1000 163 164 for _, tc := range tcs { 165 t.Run(tc.desc, func(t *testing.T) { 166 c := fake.NewSimpleClientset() 167 c.PrependReactor("list", "nodes", func(action k8stesting.Action) (handled bool, ret runtime.Object, err error) { 168 nodeList := &v1.NodeList{Items: tc.nodes} 169 return true, nodeList, tc.nodeListErr 170 }) 171 checkFunc := CheckReadyForTests(context.Background(), c, tc.nonblockingTaints, tc.allowedNotReadyNodes, testLargeClusterThreshold) 172 // The check function returns "false, nil" during its 173 // first two calls, therefore we have to try several 174 // times until we get the expected error. 175 for attempt := 0; attempt <= 3; attempt++ { 176 out, err := checkFunc(context.Background()) 177 expected := tc.expected 178 expectedErr := tc.expectedErr 179 if tc.nodeListErr != nil && attempt < 2 { 180 expected = false 181 expectedErr = "" 182 } 183 if out != expected { 184 t.Errorf("Expected %v but got %v", expected, out) 185 } 186 switch { 187 case err == nil && expectedErr != "": 188 t.Errorf("attempt #%d: expected error %q nil", attempt, expectedErr) 189 case err != nil && err.Error() != expectedErr: 190 t.Errorf("attempt #%d: expected error %q but got %q", attempt, expectedErr, err.Error()) 191 } 192 } 193 }) 194 } 195 } 196 197 func TestReadyForTests(t *testing.T) { 198 fromVanillaNode := func(f func(*v1.Node)) *v1.Node { 199 vanillaNode := &v1.Node{ 200 ObjectMeta: metav1.ObjectMeta{Name: "test-node"}, 201 Status: v1.NodeStatus{ 202 Conditions: []v1.NodeCondition{ 203 {Type: v1.NodeReady, Status: v1.ConditionTrue}, 204 }, 205 }, 206 } 207 f(vanillaNode) 208 return vanillaNode 209 } 210 _ = fromVanillaNode 211 tcs := []struct { 212 desc string 213 node *v1.Node 214 nonblockingTaints string 215 expected bool 216 }{ 217 { 218 desc: "Vanilla node should pass", 219 node: fromVanillaNode(func(n *v1.Node) { 220 }), 221 expected: true, 222 }, { 223 desc: "Vanilla node should pass with non-applicable nonblocking taint", 224 nonblockingTaints: "foo", 225 node: fromVanillaNode(func(n *v1.Node) { 226 }), 227 expected: true, 228 }, { 229 desc: "Tainted node should pass if effect is TaintEffectPreferNoSchedule", 230 node: fromVanillaNode(func(n *v1.Node) { 231 n.Spec.Taints = []v1.Taint{{Key: "foo", Effect: v1.TaintEffectPreferNoSchedule}} 232 }), 233 expected: true, 234 }, { 235 desc: "Tainted node should fail if effect is TaintEffectNoExecute", 236 node: fromVanillaNode(func(n *v1.Node) { 237 n.Spec.Taints = []v1.Taint{{Key: "foo", Effect: v1.TaintEffectNoExecute}} 238 }), 239 expected: false, 240 }, { 241 desc: "Tainted node should fail", 242 node: fromVanillaNode(func(n *v1.Node) { 243 n.Spec.Taints = []v1.Taint{{Key: "foo", Effect: v1.TaintEffectNoSchedule}} 244 }), 245 expected: false, 246 }, { 247 desc: "Tainted node should pass if nonblocking", 248 nonblockingTaints: "foo", 249 node: fromVanillaNode(func(n *v1.Node) { 250 n.Spec.Taints = []v1.Taint{{Key: "foo", Effect: v1.TaintEffectNoSchedule}} 251 }), 252 expected: true, 253 }, { 254 desc: "Node with network not ready fails", 255 node: fromVanillaNode(func(n *v1.Node) { 256 n.Status.Conditions = append(n.Status.Conditions, 257 v1.NodeCondition{Type: v1.NodeNetworkUnavailable, Status: v1.ConditionTrue}, 258 ) 259 }), 260 expected: false, 261 }, { 262 desc: "Node fails unless NodeReady status", 263 node: fromVanillaNode(func(n *v1.Node) { 264 n.Status.Conditions = []v1.NodeCondition{} 265 }), 266 expected: false, 267 }, 268 } 269 270 for _, tc := range tcs { 271 t.Run(tc.desc, func(t *testing.T) { 272 out := readyForTests(tc.node, tc.nonblockingTaints) 273 if out != tc.expected { 274 t.Errorf("Expected %v but got %v", tc.expected, out) 275 } 276 }) 277 } 278 }