Files
LokiStack/backend/services/deployment_config.go
T
logan 2d73661dd1 feat: complete Loki query, insights, settings and deployment workflows
Add the Loki query UI, access policies, ingress insights, alert and registry settings, and their backend APIs. Include the related Kubernetes deployment and collector updates required by the integrated application.

Validated with go test ./..., npm run build, and npm run lint.
2026-10-25 21:28:32 +08:00

121 lines
3.3 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 {
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)
}
}