151 lines
4.1 KiB
Go
151 lines
4.1 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sort"
|
|
"time"
|
|
|
|
"github.com/dustin/go-humanize"
|
|
corev1 "k8s.io/api/core/v1"
|
|
v1 "k8s.io/api/core/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/client-go/kubernetes"
|
|
metricsv "k8s.io/metrics/pkg/client/clientset/versioned"
|
|
)
|
|
|
|
type PodInfo struct {
|
|
Name string
|
|
Namespace string
|
|
Status string
|
|
ReadyContainers int
|
|
ContainerCount int
|
|
RestartCount int
|
|
CPUUsage string
|
|
MemoryUsage string
|
|
Age time.Duration
|
|
Node string
|
|
IsStarting bool // Added to track if pod is starting or restarting
|
|
}
|
|
|
|
func refreshData(clientset *kubernetes.Clientset, metricsClient *metricsv.Clientset, namespaces []string) ([]PodInfo, error) {
|
|
pods, err := clientset.CoreV1().Pods("").List(context.Background(), metav1.ListOptions{})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error listing pods: %v", err)
|
|
}
|
|
|
|
// Create a map for faster namespace lookups
|
|
namespaceMap := make(map[string]bool)
|
|
for _, ns := range namespaces {
|
|
namespaceMap[ns] = true
|
|
}
|
|
|
|
podMetricsMap := make(map[string]map[string]struct {
|
|
CPU int64
|
|
Memory int64
|
|
})
|
|
if metricsClient != nil {
|
|
podMetrics, err := metricsClient.MetricsV1beta1().PodMetricses("").List(context.Background(), metav1.ListOptions{})
|
|
if err == nil {
|
|
for _, metric := range podMetrics.Items {
|
|
nsKey := metric.Namespace
|
|
// Only process metrics for specified namespaces
|
|
if !namespaceMap[nsKey] {
|
|
continue
|
|
}
|
|
|
|
if _, ok := podMetricsMap[nsKey]; !ok {
|
|
podMetricsMap[nsKey] = make(map[string]struct {
|
|
CPU int64
|
|
Memory int64
|
|
})
|
|
}
|
|
var totalCPU, totalMemory int64
|
|
for _, container := range metric.Containers {
|
|
totalCPU += container.Usage.Cpu().MilliValue()
|
|
totalMemory += container.Usage.Memory().Value()
|
|
}
|
|
podMetricsMap[nsKey][metric.Name] = struct {
|
|
CPU int64
|
|
Memory int64
|
|
}{
|
|
CPU: totalCPU,
|
|
Memory: totalMemory,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var result []PodInfo
|
|
for _, pod := range pods.Items {
|
|
readyCount := 0
|
|
restarts := 0
|
|
nsKey := pod.Namespace
|
|
// Only process pods for specified namespaces
|
|
if !namespaceMap[nsKey] {
|
|
continue
|
|
}
|
|
for _, status := range pod.Status.ContainerStatuses {
|
|
if status.Ready {
|
|
readyCount++
|
|
}
|
|
restarts += int(status.RestartCount)
|
|
}
|
|
podInfo := PodInfo{
|
|
Name: pod.Name,
|
|
Namespace: pod.Namespace,
|
|
Status: string(pod.Status.Phase),
|
|
ContainerCount: len(pod.Spec.Containers),
|
|
ReadyContainers: readyCount,
|
|
RestartCount: restarts,
|
|
Age: time.Since(pod.CreationTimestamp.Time),
|
|
//IP: pod.Status.PodIP,
|
|
IsStarting: time.Since(pod.CreationTimestamp.Time) < 1*time.Minute || isPodStartingOrRestarting(pod),
|
|
Node: pod.Spec.NodeName,
|
|
}
|
|
if metrics, ok := podMetricsMap[pod.Namespace][pod.Name]; ok {
|
|
podInfo.CPUUsage = fmt.Sprintf("%dm", metrics.CPU)
|
|
podInfo.MemoryUsage = humanize.Bytes(uint64(metrics.Memory))
|
|
} else {
|
|
podInfo.CPUUsage = "N/A"
|
|
podInfo.MemoryUsage = "N/A"
|
|
}
|
|
result = append(result, podInfo)
|
|
}
|
|
sort.Slice(result, func(i, j int) bool {
|
|
if result[i].Namespace == result[j].Namespace {
|
|
return result[i].Name < result[j].Name
|
|
}
|
|
return result[i].Namespace < result[j].Namespace
|
|
})
|
|
return result, nil
|
|
}
|
|
|
|
// Helper function to check if pod is starting or restarting
|
|
func isPodStartingOrRestarting(pod v1.Pod) bool {
|
|
// Check if pod is in Pending state
|
|
if pod.Status.Phase == corev1.PodPending {
|
|
return true
|
|
}
|
|
|
|
// Check if any container is in waiting state with specific reasons
|
|
for _, containerStatus := range pod.Status.ContainerStatuses {
|
|
if containerStatus.State.Waiting != nil {
|
|
reason := containerStatus.State.Waiting.Reason
|
|
if reason == "ContainerCreating" ||
|
|
reason == "PodInitializing" ||
|
|
reason == "CrashLoopBackOff" {
|
|
return true
|
|
}
|
|
}
|
|
// Check if container recently restarted (within last minute)
|
|
if containerStatus.LastTerminationState.Terminated != nil {
|
|
terminatedTime := containerStatus.LastTerminationState.Terminated.FinishedAt.Time
|
|
if time.Since(terminatedTime) < 60*time.Second {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|