diff --git a/main.go b/main.go index 7814a4d..4034a01 100644 --- a/main.go +++ b/main.go @@ -4,133 +4,112 @@ import ( "context" "flag" "fmt" - "log" - "os" - "path/filepath" - "sort" - _ "strings" - corev1 "k8s.io/api/core/v1" - resource "k8s.io/apimachinery/pkg/api/resource" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/clientcmd" - metricsv "k8s.io/metrics/pkg/client/clientset/versioned/typed/metrics/v1beta1" + metricsv "k8s.io/metrics/pkg/client/clientset/versioned" + "log" + "os" + "path/filepath" + "sort" "github.com/olekukonko/tablewriter" ) -type PodInfo struct { - PodName string - Containers []ContainerInfo -} - -type ContainerInfo struct { - Name string - CPU resource.Quantity - Mem resource.Quantity -} - func main() { homeDir, err := os.UserHomeDir() if err != nil { log.Fatalf("Cannot get home directory: %v", err) } - + // kubeconfig := flag.String("kubeconfig", "/Users/abcd/.kube/config", "location to your kubeconfig file") kubeconfig := flag.String("kubeconfig", filepath.Join(homeDir, ".kube", "config"), "(optional) absolute path to the kubeconfig file") - podsOrderByCPU := flag.Bool("pods-order-by-cpu", false, "(optional) order pods by CPU usage instead of memory usage") - flag.Parse() + podsOrder := flag.String("pods-order-by", "memory", "order pods listing by 'cpu' or 'memory'") config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig) if err != nil { - log.Fatalf("Error building kubeconfig: %v", err) + panic(err.Error()) } clientset, err := kubernetes.NewForConfig(config) if err != nil { - log.Fatalf("Error creating clientset: %v", err) + panic(err.Error()) } - metricsClientset, err := metricsv.NewForConfig(config) + metricsClient, err := metricsv.NewForConfig(config) if err != nil { - log.Fatalf("Error creating metrics clientset: %v", err) + panic(err.Error()) } - nodes, err := clientset.CoreV1().Nodes().List(context.Background(), v1.ListOptions{}) + nodeMetricsList, err := metricsClient.MetricsV1beta1().NodeMetricses().List(context.Background(), v1.ListOptions{}) if err != nil { - log.Fatalf("Error getting nodes: %v", err) + panic(err.Error()) } - for _, node := range nodes.Items { - fmt.Println("Node: ", node.Name) - metrics, err := metricsClientset.NodeMetricses().Get(context.TODO(), node.Name, v1.GetOptions{}) - if err != nil { - re - log.Fatalf("Error getting node metrics: %v", err) - } - // fmt.Printf("CPU Usage: %.3f cores\n", float64(metrics.Usage[corev1.ResourceCPU].MilliValue())/1000) - // fmt.Printf("Memory Usage: %.3f Mi\n", float64(metrics.Usage[corev1.ResourceMemory].Value())/1024/1024) + table := tablewriter.NewWriter(os.Stdout) + table.SetHeader([]string{"Node", "CPU Usage (Cores)", "Memory Usage (GiB)"}) + + for _, metrics := range nodeMetricsList.Items { cpuUsage := metrics.Usage[corev1.ResourceCPU] - cpuUsageMilli := cpuUsage.AsDec().UnscaledBig().Int64() - fmt.Printf("CPU Usage: %.3f cores\n", float64(cpuUsageMilli)/1000) + cpuUsageCores := float64(cpuUsage.MilliValue()) / 1000 memoryUsage := metrics.Usage[corev1.ResourceMemory] - memoryUsageMi := memoryUsage.ScaledValue(resource.Mega) - fmt.Printf("Memory Usage: %.3f Mi\n", float64(memoryUsageMi)) + memoryUsageGib := float64(memoryUsage.Value()) / 1024 / 1024 / 1024 + table.Append([]string{metrics.Name, fmt.Sprintf("%.3f", cpuUsageCores), fmt.Sprintf("%.3f", memoryUsageGib)}) } + table.Render() + pods, err := clientset.CoreV1().Pods("").List(context.Background(), v1.ListOptions{}) if err != nil { - log.Fatalf("Error getting pods: %v", err) + panic(err.Error()) + } + + type PodInfo struct { + PodName string + ContainerName string + CPUUsage float64 + MemoryUsage float64 } - podInfos := make([]PodInfo, 0) + var podInfoList []PodInfo + for _, pod := range pods.Items { - podMetrics, err := metricsClientset.PodMetricses(pod.Namespace).Get(context.TODO(), pod.Name, v1.GetOptions{}) + podMetrics, err := metricsClient.MetricsV1beta1().PodMetricses(pod.Namespace).Get(context.Background(), pod.Name, v1.GetOptions{}) if err != nil { - log.Printf("Error getting pod metrics: %v", err) + fmt.Println("Error getting pod metrics:", err) continue } - containerInfos := make([]ContainerInfo, len(podMetrics.Containers)) - for i, container := range podMetrics.Containers { - containerInfos[i] = ContainerInfo{ - Name: container.Name, - CPU: container.Usage[corev1.ResourceCPU], - Mem: container.Usage[corev1.ResourceMemory], - } + + for _, container := range podMetrics.Containers { + cpuUsage := container.Usage[corev1.ResourceCPU] + cpuUsageCores := float64(cpuUsage.MilliValue()) / 1000 + + memoryUsage := container.Usage[corev1.ResourceMemory] + memoryUsageMib := float64(memoryUsage.Value()) / 1024 / 1024 + + podInfoList = append(podInfoList, PodInfo{pod.Name, container.Name, cpuUsageCores, memoryUsageMib}) } - podInfos = append(podInfos, PodInfo{PodName: pod.Name, Containers: containerInfos}) } - sort.Slice(podInfos, func(i, j int) bool { - if *podsOrderByCPU { - return float64(podInfos[i].Containers[0].CPU.MilliValue()) > float64(podInfos[j].Containers[0].CPU.MilliValue()) - } - return float64(podInfos[i].Containers[0].Mem.Value()) > float64(podInfos[j].Containers[0].Mem.Value()) - }) - - // Only display top 10 pods - data := make([][]string, 0) - for i, podInfo := range podInfos { - if i >= 10 { - break - } - for _, container := range podInfo.Containers { - data = append(data, []string{ - podInfo.PodName, - container.Name, - fmt.Sprintf("%.3f", float64(container.CPU.MilliValue())/1000), - fmt.Sprintf("%.3f Mi", float64(container.Mem.Value())/1024/1024), - }) - } + switch *podsOrder { + case "cpu": + sort.Slice(podInfoList, func(i, j int) bool { return podInfoList[i].CPUUsage > podInfoList[j].CPUUsage }) + case "memory": + sort.Slice(podInfoList, func(i, j int) bool { return podInfoList[i].MemoryUsage > podInfoList[j].MemoryUsage }) } - table := tablewriter.NewWriter(os.Stdout) - table.SetHeader([]string{"Pod", "Container", "CPU Usage (cores)", "Memory Usage (Mi)"}) + if len(podInfoList) > 10 { + podInfoList = podInfoList[:10] + } - for _, v := range data { - table.Append(v) + table = tablewriter.NewWriter(os.Stdout) + table.SetHeader([]string{"Pod", "Container", "CPU Usage (Cores)", "Memory Usage (MiB)"}) + + for _, podInfo := range podInfoList { + table.Append([]string{podInfo.PodName, podInfo.ContainerName, fmt.Sprintf("%.3f", podInfo.CPUUsage), fmt.Sprintf("%.3f", podInfo.MemoryUsage)}) } + table.Render() }