package handlers import ( "time" "ezops/internal/models" "github.com/gin-gonic/gin" ) func (h *KubernetesHandler) GetMultiClusterComparison(c *gin.Context) { // 1. 获取所有集群 var clusters []models.KubernetesCluster h.db.Find(&clusters) type ClusterComparison struct { ID uint `json:"id"` Name string `json:"name"` Code string `json:"code"` Status string `json:"status"` Version string `json:"version"` NodeCount int `json:"node_count"` PodCount int `json:"pod_count"` DeploymentCount int `json:"deployment_count"` ServiceCount int `json:"service_count"` CPUUsage float64 `json:"cpu_usage"` MemoryUsage float64 `json:"memory_usage"` APIServer string `json:"api_server"` LastSyncAt *time.Time `json:"last_sync_at"` // Node-level aggregated data from kubernetes_nodes table TotalCPUCores int `json:"total_cpu_cores"` TotalMemoryMB int64 `json:"total_memory_mb"` MasterCount int `json:"master_count"` WorkerCount int `json:"worker_count"` HealthyNodes int `json:"healthy_nodes"` UnhealthyNodes int `json:"unhealthy_nodes"` } // 2. 获取所有节点的聚合数据(按 cluster_id 分组) type NodeAgg struct { ClusterID uint NodeCount int MasterCount int WorkerCount int HealthyNodes int UnhealthyNodes int TotalCPU int TotalMemory int64 AvgCPUUsage float64 AvgMemUsage float64 } nodeAggMap := map[uint]*NodeAgg{} { var nodes []models.KubernetesNode h.db.Find(&nodes) for _, n := range nodes { agg, ok := nodeAggMap[n.ClusterID] if !ok { agg = &NodeAgg{ClusterID: n.ClusterID} nodeAggMap[n.ClusterID] = agg } agg.NodeCount++ agg.TotalCPU += n.CPU agg.TotalMemory += n.Memory if n.CPUUsage > 0 || n.MemoryUsage > 0 { if agg.AvgCPUUsage == 0 { agg.AvgCPUUsage = n.CPUUsage } else { agg.AvgCPUUsage = (agg.AvgCPUUsage*float64(agg.NodeCount-1) + n.CPUUsage) / float64(agg.NodeCount) } if agg.AvgMemUsage == 0 { agg.AvgMemUsage = n.MemoryUsage } else { agg.AvgMemUsage = (agg.AvgMemUsage*float64(agg.NodeCount-1) + n.MemoryUsage) / float64(agg.NodeCount) } } if n.Role == "master" { agg.MasterCount++ } else { agg.WorkerCount++ } if n.Status == "Ready" { agg.HealthyNodes++ } else { agg.UnhealthyNodes++ } } } // 3. 构建对比数据 comparisons := []ClusterComparison{} totalNodes, totalPods, totalDeployments, totalServices := 0, 0, 0, 0 for _, cl := range clusters { cc := ClusterComparison{ ID: cl.ID, Name: cl.Name, Code: cl.Code, Status: cl.Status, Version: cl.Version, NodeCount: cl.NodeCount, PodCount: cl.PodCount, DeploymentCount: cl.DeploymentCount, ServiceCount: cl.ServiceCount, CPUUsage: cl.CPUUsage, MemoryUsage: cl.MemoryUsage, APIServer: cl.APIServer, LastSyncAt: cl.LastSyncAt, } // 优先用 node 级别的实时聚合数据 if agg, ok := nodeAggMap[cl.ID]; ok && agg.NodeCount > 0 { cc.TotalCPUCores = agg.TotalCPU cc.TotalMemoryMB = agg.TotalMemory cc.MasterCount = agg.MasterCount cc.WorkerCount = agg.WorkerCount cc.HealthyNodes = agg.HealthyNodes cc.UnhealthyNodes = agg.UnhealthyNodes // 如果集群级别的 cpu_usage 为 0,使用节点平均值 if cc.CPUUsage == 0 && agg.AvgCPUUsage > 0 { cc.CPUUsage = agg.AvgCPUUsage } if cc.MemoryUsage == 0 && agg.AvgMemUsage > 0 { cc.MemoryUsage = agg.AvgMemUsage } } totalNodes += cc.NodeCount totalPods += cc.PodCount totalDeployments += cc.DeploymentCount totalServices += cc.ServiceCount comparisons = append(comparisons, cc) } // 4. 返回结果 c.JSON(200, models.Response{ Code: 200, Message: "success", Data: gin.H{ "clusters": comparisons, "totals": gin.H{ "cluster_count": len(clusters), "node_count": totalNodes, "pod_count": totalPods, "deployment_count": totalDeployments, "service_count": totalServices, }, }, }) } // ============================================================================ // RBAC — ServiceAccount / Role / RoleBinding (code-based) // ============================================================================ // ListServiceAccountsNS lists ServiceAccounts in a namespace