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  }