- 品牌: 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 未翻译、词典双逗号等
125 lines
3.5 KiB
Go
125 lines
3.5 KiB
Go
package services
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"time"
|
|
)
|
|
|
|
type LiveLokiConfig struct {
|
|
Location string `json:"location"`
|
|
YAML string `json:"yaml"`
|
|
}
|
|
|
|
// GetLiveConfig 读取实际运行的 Loki 配置。仅返回 loki.yaml,不返回整个 ConfigMap 元数据。
|
|
func (s *DeploymentService) GetLiveConfig(ctx context.Context, id uint) (*LiveLokiConfig, error) {
|
|
d, err := s.Get(id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var raw, location string
|
|
switch d.Mode {
|
|
case "k8s-yaml":
|
|
if d.Cluster == nil {
|
|
return nil, fmt.Errorf("部署未关联 Kubernetes 集群")
|
|
}
|
|
client, err := clusterClient(d.Cluster, 30*time.Second)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ns := d.Namespace
|
|
if ns == "" {
|
|
ns = "log"
|
|
}
|
|
configMap, err := client.CoreV1().ConfigMaps(ns).Get(ctx, "loki-config", metav1.GetOptions{})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("无法读取 %s/loki-config: %w", ns, err)
|
|
}
|
|
raw = configMap.Data["loki.yaml"]
|
|
location = fmt.Sprintf("ConfigMap %s/loki-config → loki.yaml", ns)
|
|
case "host-binary":
|
|
if d.Host == nil {
|
|
return nil, fmt.Errorf("部署未关联主机")
|
|
}
|
|
client, err := NewSSHClientWithAuth(HostSSHAuth(d.Host))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("连接部署主机失败")
|
|
}
|
|
defer client.Close()
|
|
output, _, err := client.Execute(ctx, "cat /etc/loki/loki.yaml")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("无法读取主机上的 /etc/loki/loki.yaml")
|
|
}
|
|
raw = output
|
|
location = "/etc/loki/loki.yaml"
|
|
case "host-docker":
|
|
if d.Host == nil {
|
|
return nil, fmt.Errorf("部署未关联主机")
|
|
}
|
|
client, err := NewSSHClientWithAuth(HostSSHAuth(d.Host))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("连接部署主机失败")
|
|
}
|
|
defer client.Close()
|
|
location = dockerConfigPath(d.ID)
|
|
output, _, err := client.Execute(ctx, "cat "+location)
|
|
if err != nil {
|
|
// 历史版本路径兜底(重部署一次后目录即迁移到新命名)
|
|
location = legacyDockerConfigDir(d.ID) + "/loki.yaml"
|
|
if output, _, err = client.Execute(ctx, "cat "+location); err != nil {
|
|
return nil, fmt.Errorf("无法读取主机上的 Loki 配置")
|
|
}
|
|
}
|
|
raw = output
|
|
default:
|
|
return nil, fmt.Errorf("当前部署模式暂不支持查看运行配置")
|
|
}
|
|
if strings.TrimSpace(raw) == "" {
|
|
return nil, fmt.Errorf("运行配置为空或尚未部署")
|
|
}
|
|
redacted, err := redactLokiYAML(raw)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("运行配置无法安全展示:YAML 格式无效")
|
|
}
|
|
return &LiveLokiConfig{Location: location, YAML: redacted}, nil
|
|
}
|
|
|
|
func redactLokiYAML(source string) (string, error) {
|
|
var document yaml.Node
|
|
if err := yaml.Unmarshal([]byte(source), &document); err != nil {
|
|
return "", err
|
|
}
|
|
redactYAMLNode(&document)
|
|
output, err := yaml.Marshal(&document)
|
|
return string(output), err
|
|
}
|
|
|
|
func redactYAMLNode(node *yaml.Node) {
|
|
if node.Kind == yaml.ScalarNode && strings.HasPrefix(node.Value, "ENC:") {
|
|
node.Value = "********"
|
|
return
|
|
}
|
|
if node.Kind == yaml.MappingNode {
|
|
for i := 0; i+1 < len(node.Content); i += 2 {
|
|
key, value := node.Content[i], node.Content[i+1]
|
|
switch strings.ToLower(key.Value) {
|
|
case "access_key_id", "secret_access_key", "account_key", "password", "token", "client_secret":
|
|
value.Kind = yaml.ScalarNode
|
|
value.Tag = "!!str"
|
|
value.Value = "********"
|
|
value.Content = nil
|
|
continue
|
|
}
|
|
redactYAMLNode(value)
|
|
}
|
|
return
|
|
}
|
|
for _, child := range node.Content {
|
|
redactYAMLNode(child)
|
|
}
|
|
}
|