func()

in pkg/controllers/internalmembercluster/v1beta1/member_controller.go [466:551]


func (r *Reconciler) updateResourceStats(ctx context.Context, imc *clusterv1beta1.InternalMemberCluster) error {
	klog.V(2).InfoS("Updating resource usage status", "internalMemberCluster", klog.KObj(imc))
	// List all the nodes.
	var nodes corev1.NodeList
	if err := r.memberClient.List(ctx, &nodes); err != nil {
		return fmt.Errorf("failed to list nodes for member cluster %s: %w", klog.KObj(imc), err)
	}

	// Prepare the total and allocatable capacities.
	var capacityCPU, capacityMemory, allocatableCPU, allocatableMemory resource.Quantity

	for _, node := range nodes.Items {
		capacityCPU.Add(*(node.Status.Capacity.Cpu()))
		capacityMemory.Add(*(node.Status.Capacity.Memory()))
		allocatableCPU.Add(*(node.Status.Allocatable.Cpu()))
		allocatableMemory.Add(*(node.Status.Allocatable.Memory()))
	}

	imc.Status.Properties = map[clusterv1beta1.PropertyName]clusterv1beta1.PropertyValue{
		propertyprovider.NodeCountProperty: {
			Value:           fmt.Sprintf("%d", len(nodes.Items)),
			ObservationTime: metav1.Now(),
		},
	}
	imc.Status.ResourceUsage.Capacity = corev1.ResourceList{
		corev1.ResourceCPU:    capacityCPU,
		corev1.ResourceMemory: capacityMemory,
	}
	imc.Status.ResourceUsage.Allocatable = corev1.ResourceList{
		corev1.ResourceCPU:    allocatableCPU,
		corev1.ResourceMemory: allocatableMemory,
	}

	// List all the pods.
	//
	// Note: this can be a very heavy operation, especially in large clusters. For such clusters,
	// it is recommended that a property provider is set up to summarize the available capacity
	// information in a more efficient manner.
	var pods corev1.PodList
	listLimitOpt := client.Limit(podListLimit)
	if err := r.memberClient.List(ctx, &pods, listLimitOpt); err != nil {
		return fmt.Errorf("failed to list pods for member cluster: %w", err)
	}
	if len(pods.Items) == podListLimit {
		klog.Warningf("The number of pods in the member cluster has reached or exceeded the limit %d; the available capacity reported might be inaccurate, consider setting up a property provider instead", podListLimit)
	}

	// Prepare the available capacities.
	availableCPU := allocatableCPU.DeepCopy()
	availableMemory := allocatableMemory.DeepCopy()
	for pidx := range pods.Items {
		p := pods.Items[pidx]

		if len(p.Spec.NodeName) == 0 || p.Status.Phase == corev1.PodSucceeded || p.Status.Phase == corev1.PodFailed {
			// Skip pods that are not yet scheduled to a node, or have already completed/failed.
			continue
		}

		requestedCPUCapacity := resource.Quantity{}
		requestedMemoryCapacity := resource.Quantity{}
		for cidx := range p.Spec.Containers {
			c := p.Spec.Containers[cidx]
			requestedCPUCapacity.Add(c.Resources.Requests[corev1.ResourceCPU])
			requestedMemoryCapacity.Add(c.Resources.Requests[corev1.ResourceMemory])
		}

		availableCPU.Sub(requestedCPUCapacity)
		availableMemory.Sub(requestedMemoryCapacity)
	}

	// Do a sanity check to avoid inconsistencies.
	if availableCPU.Cmp(resource.Quantity{}) < 0 {
		availableCPU = resource.Quantity{}
	}
	if availableMemory.Cmp(resource.Quantity{}) < 0 {
		availableMemory = resource.Quantity{}
	}

	imc.Status.ResourceUsage.Available = corev1.ResourceList{
		corev1.ResourceCPU:    availableCPU,
		corev1.ResourceMemory: availableMemory,
	}
	imc.Status.ResourceUsage.ObservationTime = metav1.Now()

	return nil
}