Repository: kubernetes/kubernetes
File: test/e2e/framework/node/resource.go (Go)

Code before:
}

// Calculate available resource. nodeAvailable = nodeAllocatable - sum(podAllocated).
// If resourceName is "CPU", the values returned are in MilliValues.
func GetNodeAllocatableAndAvailableValues(ctx context.Context, c clientset.Interface, n *v1.Node, resourceName v1.ResourceName) (int64, int64, error) {
	var nodeAllocatable int64
	switch resourceName {
	case v1.ResourceCPU:
		nodeAllocatable = n.Status.Allocatable.Cpu().MilliValue()
	case v1.ResourceMemory:
		nodeAllocatable = n.Status.Allocatable.Memory().Value()
	default:
		return 0, 0, fmt.Errorf("unexpected resource type %q; expected either 'CPU' or 'Memory'", resourceName)
	}

	gomega.Expect(n.Status.Allocatable).ShouldNot(gomega.BeEmpty(), "allocatable")
	podAllocated := int64(0)

	// Exclude pods that are in the Succeeded or Failed states
	selector := fmt.Sprintf("spec.nodeName=%s,status.phase!=%v,status.phase!=%v", n.Name, v1.PodSucceeded, v1.PodFailed)
	listOptions := metav1.ListOptions{FieldSelector: selector}
	podList, err := c.CoreV1().Pods(metav1.NamespaceAll).List(ctx, listOptions)
	if err != nil {
		return 0, 0, fmt.Errorf("error getting list of pods: %w", err)
	}
	framework.Logf("Found %d pods on node '%s'", len(podList.Items), n.Name)
	for _, pod := range podList.Items {
		podRequest := resourceapi.GetResourceRequest(&pod, resourceName)
		podAllocated += podRequest
	}
	nodeAvailable := nodeAllocatable - podAllocated
	if nodeAvailable < 0 {
		return nodeAvailable, nodeAllocatable, fmt.Errorf("unexpected negative value of nodeAvailable %d, while attempting to get node allocatable and available values", nodeAvailable)
	}

	return nodeAllocatable, nodeAvailable, nil
}

// GetNodeHeartbeatTime returns the timestamp of the last status update of the node.
func GetNodeHeartbeatTime(node *v1.Node) metav1.Time {
	for _, condition := range node.Status.Conditions {
		if condition.Type == v1.NodeReady {
			return condition.LastHeartbeatTime
		}
	}
	return metav1.Time{}
}

// PodNodePairs return podNode pairs for all pods in a namespace
func PodNodePairs(ctx context.Context, c clientset.Interface, ns string) ([]PodNode, error) {
	var result []PodNode

Review comment (refactor):
Helper functions should not log.

Imagine using this helper while polling for the expected state. Each loop iteration would log something, which would be way too much output.

Code after:
}

// GetNodeAllocatableAndAvailableQuantities with resourceName set to v1.ResourceCPU ( or set to v1.ResourceMemory ) returns
//   - the first return value, upon success is the allocatable portion of a node's total "CPU" ( or "Memory" ) capacity that's available for pods to use,
//     calculated by subtracting "CPU" ( or "Memory") resources reserved for node's operating system, Kubelet and other
//     system daemons from the node's total "CPU" ( or "Memory" ) resource, upon failure is empty.
//   - the second return value, upon success is the available portion of a node's allocatable "CPU" capacity, calculated by subtracting "CPU"
//     ( or "Memory" ) resource reserved for node's pods from the node's allocatable "CPU" ( or "Memory" ) resource, upon failure is empty.
//   - the third return value, upon success is nil, upon failure is the coresponding error.
func GetNodeAllocatableAndAvailableQuantities(ctx context.Context, c clientset.Interface, n *v1.Node, resourceName v1.ResourceName) (resource.Quantity, resource.Quantity, error) {
	var nodeAllocatable, podAllocated, nodeAvailable resource.Quantity
	switch resourceName {
	case v1.ResourceCPU:
		nodeAllocatable = n.Status.Allocatable.Cpu().DeepCopy()
	case v1.ResourceMemory:
		nodeAllocatable = n.Status.Allocatable.Memory().DeepCopy()
	default:
		return resource.Quantity{}, resource.Quantity{}, fmt.Errorf("unexpected resource type %q; expected either 'CPU' or 'Memory'", resourceName)
	}

	if n.Status.Allocatable == nil {
		return resource.Quantity{}, resource.Quantity{}, fmt.Errorf("unexpected empty value of allocatable, while attempting to get node's allocatable %s", resourceName.String())
	}

	// Exclude pods that are in the Succeeded or Failed states
	selector := fmt.Sprintf("spec.nodeName=%s,status.phase!=%v,status.phase!=%v", n.Name, v1.PodSucceeded, v1.PodFailed)
	listOptions := metav1.ListOptions{FieldSelector: selector}
	podList, err := c.CoreV1().Pods(metav1.NamespaceAll).List(ctx, listOptions)
	if err != nil {
		return resource.Quantity{}, resource.Quantity{}, fmt.Errorf("error getting list of pods: %w, while attempting to get node's allocatable %s", err, resourceName.String())
	}

	for _, pod := range podList.Items {
		podRequest := resourceapi.GetResourceRequestQuantity(&pod, resourceName)
		podAllocated.Add(podRequest)
	}
	nodeAvailable = nodeAllocatable.DeepCopy()
	nodeAvailable.Sub(podAllocated)
	if nodeAvailable.Sign() < 0 {
		return resource.Quantity{}, resource.Quantity{}, fmt.Errorf("unexpected negative value of nodeAvailable %s, while attempting to get node's allocatable and available %s", nodeAvailable.String(), resourceName.String())
	}

	return nodeAllocatable, nodeAvailable, nil
}

// GetNodeHeartbeatTime returns the timestamp of the last status update of the node.
func GetNodeHeartbeatTime(node *v1.Node) metav1.Time {
	for _, condition := range node.Status.Conditions {
		if condition.Type == v1.NodeReady {
			return condition.LastHeartbeatTime
		}