Files
LokiStack/backend/services/cluster_info.go
T
Logan.LiandClaude 2a93de600d feat: LokiDeploy 初始提交 - 轻量级 K8s Loki 部署平台
Go(Gin) + React(Vite + shadcn/ui + Tailwind) 前后端分离,全中文 UI

- 集群管理:Kubeconfig / Token 双接入,kubectl + helm 部署
- 主机管理:SSH 密钥 / 密码双认证,CPU/内存/磁盘使用率监控,SSH 错误中文友好提示
- 存储配置、部署管理、系统设置
- Docker 镜像内置 kubectl + helm,开箱即用
- run.sh 一键启停(start/stop/restart/status)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-29 17:03:57 +08:00

165 lines
4.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package services
import (
"context"
"encoding/json"
"fmt"
"os"
"os/exec"
"strings"
"lokideploy/models"
)
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 通过 kubectl 查询集群信息。
// Token 接入的集群在创建时已构造等价 kubeconfig,因此 kubectl 同样适用。
func GetClusterInfo(ctx context.Context, cluster *models.Cluster) (*ClusterInfo, error) {
kubeconfig, err := resolveKubeconfig(cluster)
if err != nil {
return nil, err
}
kubeFile, err := os.CreateTemp("", "kube-*.yaml")
if err != nil {
return nil, err
}
defer os.Remove(kubeFile.Name())
kubeFile.WriteString(kubeconfig)
kubeFile.Close()
env := append(os.Environ(), fmt.Sprintf("KUBECONFIG=%s", kubeFile.Name()))
info := &ClusterInfo{
Nodes: []NodeInfo{},
Namespaces: []string{},
Contexts: []string{},
}
// Version(best-effort)
if versionOut, err := runCmd(ctx, env, "kubectl", "version", "-o", "json"); err == nil {
var versionData struct {
ServerVersion struct {
GitVersion string `json:"gitVersion"`
} `json:"serverVersion"`
}
json.Unmarshal(versionOut, &versionData)
info.Version = versionData.ServerVersion.GitVersion
}
// Nodes
nodesOut, err := runCmd(ctx, env, "kubectl", "get", "nodes", "-o", "json")
if err != nil {
return nil, fmt.Errorf("获取节点列表失败: %v", err)
}
var nodesData struct {
Items []struct {
Metadata struct {
Name string `json:"name"`
Labels map[string]string `json:"labels"`
} `json:"metadata"`
Status struct {
Conditions []struct {
Type string `json:"type"`
Status string `json:"status"`
} `json:"conditions"`
NodeInfo struct {
KubeletVersion string `json:"kubeletVersion"`
OperatingSystem string `json:"operatingSystem"`
Architecture string `json:"architecture"`
} `json:"nodeInfo"`
Addresses []struct {
Type string `json:"type"`
Address string `json:"address"`
} `json:"addresses"`
} `json:"status"`
} `json:"items"`
}
json.Unmarshal(nodesOut, &nodesData)
for _, item := range nodesData.Items {
status := "Unknown"
for _, cond := range item.Status.Conditions {
if cond.Type == "Ready" {
if cond.Status == "True" {
status = "Ready"
} else {
status = "NotReady"
}
break
}
}
var roles []string
for label := range item.Metadata.Labels {
if strings.HasPrefix(label, "node-role.kubernetes.io/") {
roles = append(roles, label[len("node-role.kubernetes.io/"):])
}
}
if len(roles) == 0 {
roles = []string{"worker"}
}
internalIP := ""
for _, addr := range item.Status.Addresses {
if addr.Type == "InternalIP" {
internalIP = addr.Address
break
}
}
info.Nodes = append(info.Nodes, NodeInfo{
Name: item.Metadata.Name,
Status: status,
Roles: roles,
Version: item.Status.NodeInfo.KubeletVersion,
OS: item.Status.NodeInfo.OperatingSystem,
Arch: item.Status.NodeInfo.Architecture,
InternalIP: internalIP,
Labels: item.Metadata.Labels,
})
}
// Namespaces(best-effort)
if nsOut, err := runCmd(ctx, env, "kubectl", "get", "ns", "-o", "jsonpath={.items[*].metadata.name}"); err == nil {
info.Namespaces = strings.Fields(string(nsOut))
}
// Contexts(best-effort)
if ctxOut, err := runCmd(ctx, env, "kubectl", "config", "get-contexts", "-o", "name"); err == nil {
for _, line := range strings.Split(strings.TrimSpace(string(ctxOut)), "\n") {
if line = strings.TrimSpace(line); line != "" {
info.Contexts = append(info.Contexts, line)
}
}
}
// Current context(best-effort)
if currentCtxOut, err := runCmd(ctx, env, "kubectl", "config", "current-context"); err == nil {
info.CurrentCtx = strings.TrimSpace(string(currentCtxOut))
}
return info, nil
}
func runCmd(ctx context.Context, env []string, name string, args ...string) ([]byte, error) {
cmd := exec.CommandContext(ctx, name, args...)
cmd.Env = env
return cmd.CombinedOutput()
}