volcano.sh/volcano@v1.9.0/test/e2e/util/node.go (about) 1 /* 2 Copyright 2021 The Volcano 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 util 18 19 import ( 20 "context" 21 "encoding/json" 22 "fmt" 23 24 . "github.com/onsi/gomega" 25 26 v1 "k8s.io/api/core/v1" 27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 "k8s.io/apimachinery/pkg/types" 29 "k8s.io/apimachinery/pkg/util/strategicpatch" 30 31 schedulerapi "volcano.sh/volcano/pkg/scheduler/api" 32 ) 33 34 func ClusterSize(ctx *TestContext, req v1.ResourceList) int32 { 35 nodes, err := ctx.Kubeclient.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{}) 36 Expect(err).NotTo(HaveOccurred(), "failed to list nodes") 37 38 pods, err := ctx.Kubeclient.CoreV1().Pods(metav1.NamespaceAll).List(context.TODO(), metav1.ListOptions{}) 39 Expect(err).NotTo(HaveOccurred(), "failed to list pods") 40 41 used := map[string]*schedulerapi.Resource{} 42 43 for _, pod := range pods.Items { 44 nodeName := pod.Spec.NodeName 45 if len(nodeName) == 0 || pod.DeletionTimestamp != nil { 46 continue 47 } 48 49 if pod.Status.Phase == v1.PodSucceeded || pod.Status.Phase == v1.PodFailed { 50 continue 51 } 52 53 if _, found := used[nodeName]; !found { 54 used[nodeName] = schedulerapi.EmptyResource() 55 } 56 57 for _, c := range pod.Spec.Containers { 58 req := schedulerapi.NewResource(c.Resources.Requests) 59 used[nodeName].Add(req) 60 } 61 } 62 63 res := int32(0) 64 65 for _, node := range nodes.Items { 66 // skip node with taints 67 if len(node.Spec.Taints) != 0 { 68 continue 69 } 70 71 alloc := schedulerapi.NewResource(node.Status.Allocatable) 72 slot := schedulerapi.NewResource(req) 73 74 // remove used resources 75 if res, found := used[node.Name]; found { 76 alloc.Sub(res) 77 } 78 79 for slot.LessEqual(alloc, schedulerapi.Zero) { 80 alloc.Sub(slot) 81 res++ 82 } 83 } 84 Expect(res).Should(BeNumerically(">=", 1), 85 "Current cluster does not have enough resource for request") 86 return res 87 } 88 89 // ClusterNodeNumber returns the number of untainted nodes 90 func ClusterNodeNumber(ctx *TestContext) int { 91 nodes, err := ctx.Kubeclient.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{}) 92 Expect(err).NotTo(HaveOccurred(), "failed to list nodes") 93 94 nn := 0 95 for _, node := range nodes.Items { 96 if len(node.Spec.Taints) != 0 { 97 continue 98 } 99 nn++ 100 } 101 102 return nn 103 } 104 105 func ComputeNode(ctx *TestContext, req v1.ResourceList) (string, int32) { 106 nodes, err := ctx.Kubeclient.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{}) 107 Expect(err).NotTo(HaveOccurred(), "failed to list nodes") 108 109 pods, err := ctx.Kubeclient.CoreV1().Pods(metav1.NamespaceAll).List(context.TODO(), metav1.ListOptions{}) 110 Expect(err).NotTo(HaveOccurred(), "failed to list pods") 111 112 used := map[string]*schedulerapi.Resource{} 113 114 for _, pod := range pods.Items { 115 nodeName := pod.Spec.NodeName 116 if len(nodeName) == 0 || pod.DeletionTimestamp != nil { 117 continue 118 } 119 120 if pod.Status.Phase == v1.PodSucceeded || pod.Status.Phase == v1.PodFailed { 121 continue 122 } 123 124 if _, found := used[nodeName]; !found { 125 used[nodeName] = schedulerapi.EmptyResource() 126 } 127 128 for _, c := range pod.Spec.Containers { 129 req := schedulerapi.NewResource(c.Resources.Requests) 130 used[nodeName].Add(req) 131 } 132 } 133 134 for _, node := range nodes.Items { 135 if len(node.Spec.Taints) != 0 { 136 continue 137 } 138 139 res := int32(0) 140 141 alloc := schedulerapi.NewResource(node.Status.Allocatable) 142 slot := schedulerapi.NewResource(req) 143 144 // remove used resources 145 if res, found := used[node.Name]; found { 146 alloc.Sub(res) 147 } 148 149 for slot.LessEqual(alloc, schedulerapi.Zero) { 150 alloc.Sub(slot) 151 res++ 152 } 153 154 if res > 0 { 155 return node.Name, res 156 } 157 } 158 159 return "", 0 160 } 161 162 // TaintAllNodes taints all nodes in the cluster 163 func TaintAllNodes(ctx *TestContext, taints []v1.Taint) error { 164 nodes, err := ctx.Kubeclient.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{}) 165 Expect(err).NotTo(HaveOccurred(), "failed to list nodes") 166 167 for _, node := range nodes.Items { 168 newNode := node.DeepCopy() 169 170 newTaints := newNode.Spec.Taints 171 for _, t := range taints { 172 found := false 173 for _, nt := range newTaints { 174 if nt.Key == t.Key { 175 found = true 176 break 177 } 178 } 179 180 if !found { 181 newTaints = append(newTaints, t) 182 } 183 } 184 185 newNode.Spec.Taints = newTaints 186 187 patchBytes, err := preparePatchBytesforNode(node.Name, &node, newNode) 188 Expect(err).NotTo(HaveOccurred(), "failed to prepare patch bytes for node %s", node.Name) 189 190 _, err = ctx.Kubeclient.CoreV1().Nodes().Patch(context.TODO(), node.Name, types.StrategicMergePatchType, patchBytes, metav1.PatchOptions{}) 191 Expect(err).NotTo(HaveOccurred(), "failed to taint node %s", node.Name) 192 } 193 194 return nil 195 } 196 197 func RemoveTaintsFromAllNodes(ctx *TestContext, taints []v1.Taint) error { 198 nodes, err := ctx.Kubeclient.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{}) 199 Expect(err).NotTo(HaveOccurred(), "failed to list nodes") 200 201 for _, node := range nodes.Items { 202 if len(node.Spec.Taints) == 0 { 203 continue 204 } 205 206 newNode := node.DeepCopy() 207 208 var newTaints []v1.Taint 209 for _, nt := range newNode.Spec.Taints { 210 found := false 211 for _, t := range taints { 212 if nt.Key == t.Key { 213 found = true 214 break 215 } 216 } 217 218 if !found { 219 newTaints = append(newTaints, nt) 220 } 221 } 222 newNode.Spec.Taints = newTaints 223 224 patchBytes, err := preparePatchBytesforNode(node.Name, &node, newNode) 225 Expect(err).NotTo(HaveOccurred(), "failed to prepare patch bytes for node %s", node.Name) 226 227 _, err = ctx.Kubeclient.CoreV1().Nodes().Patch(context.TODO(), node.Name, types.StrategicMergePatchType, patchBytes, metav1.PatchOptions{}) 228 Expect(err).NotTo(HaveOccurred(), "failed to remove taints from node %s", node.Name) 229 } 230 231 return nil 232 } 233 234 // IsNodeReady returns the node ready status 235 func IsNodeReady(node *v1.Node) bool { 236 for _, c := range node.Status.Conditions { 237 if c.Type == v1.NodeReady { 238 return c.Status == v1.ConditionTrue 239 } 240 } 241 return false 242 } 243 244 func preparePatchBytesforNode(nodeName string, oldNode *v1.Node, newNode *v1.Node) ([]byte, error) { 245 oldData, err := json.Marshal(oldNode) 246 if err != nil { 247 return nil, fmt.Errorf("failed to Marshal oldData for node %q: %v", nodeName, err) 248 } 249 250 newData, err := json.Marshal(newNode) 251 if err != nil { 252 return nil, fmt.Errorf("failed to Marshal newData for node %q: %v", nodeName, err) 253 } 254 255 patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, v1.Node{}) 256 if err != nil { 257 return nil, fmt.Errorf("failed to CreateTwoWayMergePatch for node %q: %v", nodeName, err) 258 } 259 260 return patchBytes, nil 261 } 262 263 func satisfyMinNodesRequirements(ctx *TestContext, num int) bool { 264 nodes, err := ctx.Kubeclient.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{}) 265 Expect(err).NotTo(HaveOccurred(), "failed to list nodes") 266 267 taintedNodes := 0 268 for _, node := range nodes.Items { 269 if len(node.Spec.Taints) != 0 { 270 taintedNodes++ 271 } 272 } 273 return num <= len(nodes.Items)-taintedNodes 274 }