Files
LokiStack/backend/services/cluster_info.go
T
opsx f3733f0bfc feat: 品牌统一 LokiStack + 全站中英 i18n + 用户批量删除 + 主题定时切换与浅色侧栏
- 品牌: Go 模块/数据文件/二进制/构建部署资产全部 opsx→lokistack;前端 localStorage 命名空间迁移(读旧写新,含按用户 id 前缀迁移);修复 nginx upstream 与 Makefile 引用已改名服务的断裂
- i18n: react-i18next 落地,common + 11 个页面域命名空间全站中英(菜单/顶栏/登录/列表页/表单/确认弹窗/状态徽章);设置页语言即时切换;修复词典自注册导致的循环导入白屏
- 用户管理: 批量删除(POST /users/batch-delete,不删自己/最后管理员,部分失败不阻断)+ 行复选/全选/批量工具条
- 主题: 浅色侧栏(中性灰激活态)、四态模式(浅色/深色/跟随系统/定时切换,按时区判定)、顶栏太阳月亮二态、设置页紧凑分段选择器
- UI: 登录记住用户名、CardActionButton 统一五页卡片操作钮、shadcn Badge 替换手写徽章、语义色修正
- 清理: 移除主机 Web 终端(前后端)、k8s-helm/Helm 遗留代码与字段、README 重写精简
- 修复: 侧栏与日志告警 order 重复、路由标题 i18n key 未翻译、词典双逗号等
2026-10-27 17:11:38 +08:00

106 lines
3.2 KiB
Go

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"
"lokistack/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
}