package nodes import ( "context" "sync" v1 "k8s.io/api/core/v1" "k8s.io/client-go/kubernetes" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) type TotalAllocatable struct { CPU uint Memory uint } func GetAllocatableResources(clientset kubernetes.Interface) (*TotalAllocatable, error) { nodeList, err := clientset.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{}) if err != nil { return nil, err } var totCPU uint64 = 0 var totMem uint64 = 0 for _, node := range nodeList.Items { capac := node.Status.Allocatable totCPU += uint64(capac.Cpu().MilliValue()) totMem += capac.Memory().AsDec().UnscaledBig().Uint64() } return &TotalAllocatable{ CPU: uint(totCPU), Memory: uint(totMem), }, nil } type NodeUsage struct { cpuReqs string memoryReqs string ephemeralStorageReqs string fractionCpuReqs float64 fractionCpuLimits float64 fractionMemoryReqs float64 fractionMemoryLimits float64 fractionEphemeralStorageReqs float64 fractionEphemeralStorageLimits float64 } type NodeWithUsageData struct { Name string `json:"name"` Labels map[string]string `json:"labels"` CpuReqs string `json:"cpu_reqs"` MemoryReqs string `json:"memory_reqs"` EphemeralStorageReqs string `json:"ephemeral_storage_reqs"` FractionCpuReqs float64 `json:"fraction_cpu_reqs"` FractionCpuLimits float64 `json:"fraction_cpu_limits"` FractionMemoryReqs float64 `json:"fraction_memory_reqs"` FractionMemoryLimits float64 `json:"fraction_memory_limits"` FractionEphemeralStorageReqs float64 `json:"fraction_ephemeral_storage_reqs"` FractionEphemeralStorageLimits float64 `json:"fraction_ephemeral_storage_limits"` Condition []v1.NodeCondition `json:"node_conditions"` } func (nu *NodeUsage) Externalize(node v1.Node) *NodeWithUsageData { return &NodeWithUsageData{ Name: node.Name, Labels: node.Labels, CpuReqs: nu.cpuReqs, MemoryReqs: nu.memoryReqs, EphemeralStorageReqs: nu.ephemeralStorageReqs, FractionCpuReqs: nu.fractionCpuReqs, FractionCpuLimits: nu.fractionCpuLimits, FractionMemoryReqs: nu.fractionMemoryReqs, FractionMemoryLimits: nu.fractionMemoryLimits, FractionEphemeralStorageReqs: nu.fractionEphemeralStorageReqs, FractionEphemeralStorageLimits: nu.fractionEphemeralStorageLimits, Condition: node.Status.Conditions, } } func GetNodesUsage(clientset kubernetes.Interface) []*NodeWithUsageData { nodeList, _ := clientset.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{}) extNodeList := make([]*NodeWithUsageData, len(nodeList.Items)) var wg sync.WaitGroup for i := range nodeList.Items { index := i currentNode := &nodeList.Items[index] wg.Add(1) go func() { defer wg.Done() podList := getPodsForNode(clientset, currentNode.Name) nodeUsage := DescribeNodeResource(podList, currentNode) extNodeList[index] = nodeUsage.Externalize(*currentNode) }() } wg.Wait() return extNodeList } func getPodsForNode(clientset kubernetes.Interface, nodeName string) *v1.PodList { podList, _ := clientset.CoreV1().Pods("").List(context.TODO(), metav1.ListOptions{ FieldSelector: "spec.nodeName=" + nodeName + ",status.phase=Running", }) return podList } type NodeDetails struct { NodeWithUsageData AllocatableCpu int64 `json:"allocatable_cpu"` AllocatableMemory string `json:"allocatable_memory"` } func DescribeNode(clientset kubernetes.Interface, nodeName string) *NodeDetails { node, _ := clientset.CoreV1().Nodes().Get(context.TODO(), nodeName, metav1.GetOptions{}) podList := getPodsForNode(clientset, node.Name) nodeUsage := DescribeNodeResource(podList, node) extNodeUsage := nodeUsage.Externalize(*node) return &NodeDetails{ NodeWithUsageData: *extNodeUsage, AllocatableCpu: node.Status.Allocatable.Cpu().MilliValue(), AllocatableMemory: node.Status.Allocatable.Memory().String(), } } func ListNodesByLabels(clientset kubernetes.Interface, labelSelector string) ([]v1.Node, error) { nodes, err := clientset.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{ LabelSelector: labelSelector, }) if err != nil { return nil, err } return nodes.Items, nil }