package services import ( "context" "fmt" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/tools/clientcmd" "opsxloki/models" "sort" "strings" "time" ) type ClusterInfo struct { Version string `json:"version"` Nodes []NodeInfo `json:"nodes"` Namespaces []string `json:"namespaces"` Contexts []string `json:"contexts"` CurrentCtx string `json:"currentContext"` } type NodeInfo struct { Name string `json:"name"` Status string `json:"status"` Roles []string `json:"roles"` Version string `json:"version"` OS string `json:"os"` Arch string `json:"arch"` InternalIP string `json:"internalIP"` Labels map[string]string `json:"labels"` } // GetClusterInfo 从 API Server 读取集群信息,context 列表由 kubeconfig 本地解析。 func GetClusterInfo(ctx context.Context, cluster *models.Cluster) (*ClusterInfo, error) { client, err := clusterClient(cluster, 30*time.Second) if err != nil { return nil, err } info := &ClusterInfo{Nodes: []NodeInfo{}, Namespaces: []string{}, Contexts: []string{}} if version, err := client.Discovery().ServerVersion(); err == nil { info.Version = version.GitVersion } nodes, err := client.CoreV1().Nodes().List(ctx, metav1.ListOptions{}) if err != nil && !apierrors.IsForbidden(err) { return nil, fmt.Errorf("获取节点列表失败: %w", err) } // 命名空间级 Token 通常没有 nodes 权限:跳过节点信息,不影响集群可用性 var nodeItems []corev1.Node if nodes != nil { nodeItems = nodes.Items } for _, item := range nodeItems { status := "Unknown" for _, cond := range item.Status.Conditions { if cond.Type == "Ready" { if cond.Status == "True" { status = "Ready" } else { status = "NotReady" } break } } roles := []string{} for label := range item.Labels { if strings.HasPrefix(label, "node-role.kubernetes.io/") { roles = append(roles, strings.TrimPrefix(label, "node-role.kubernetes.io/")) } } if len(roles) == 0 { roles = []string{"worker"} } sort.Strings(roles) ip := "" for _, address := range item.Status.Addresses { if address.Type == "InternalIP" { ip = address.Address break } } info.Nodes = append(info.Nodes, NodeInfo{Name: item.Name, Status: status, Roles: roles, Version: item.Status.NodeInfo.KubeletVersion, OS: item.Status.NodeInfo.OperatingSystem, Arch: item.Status.NodeInfo.Architecture, InternalIP: ip, Labels: item.Labels}) } if namespaces, err := client.CoreV1().Namespaces().List(ctx, metav1.ListOptions{}); err == nil { for _, item := range namespaces.Items { info.Namespaces = append(info.Namespaces, item.Name) } sort.Strings(info.Namespaces) } source, err := resolveKubeconfig(cluster) if err == nil { if raw, err := clientcmd.Load([]byte(source)); err == nil { for name := range raw.Contexts { info.Contexts = append(info.Contexts, name) } sort.Strings(info.Contexts) info.CurrentCtx = raw.CurrentContext if cluster.Context != "" { info.CurrentCtx = cluster.Context } } } return info, nil }