=== 一、主机「测试连接」永远显示旧错误(真实 bug)===
HostService.Validate 的失败分支也写 status=active,且从不写 statusMessage:
if info, err := client.GetHostInfo(ctx); err == nil { 更新 os/arch }
else { s.Update(id, {status:"active"}) } // ← 失败也标记成功
结果:连不上也显示正常,页面永远停在几分钟前的旧错误文案,
用户改了 IP 再点测试,还以为"改了还是不行"。
改为区分成功/失败并写清原因:
成功 → status=active + os/arch + statusMessage="连接成功"
失败 → status=error + statusMessage=FormatSSHError(err)
=== 二、回环地址在平台里必然连不上 ===
平台跑在容器/Pod 内,127.0.0.1 指向平台自身。
- 新建主机时拦截:hostIP 为 127.0.0.1/localhost/::1 直接报错并解释原因
- 前端表单同步校验 + 文案
- SSH 错误映射补充:连接被拒绝时提示可能是地址填了回环
=== 三、主机模式采集系统日志(两处会导致采集失败)===
1. pipeline 一律套 cri 阶段:cri 只认容器运行时格式,
套在 /var/log/messages 这类 syslog 上会取不到时间戳、整行被丢。
→ appPipelineStages() 按路径判断:容器日志用 cri,普通文本日志只做限流。
2. promtailExternalLabels 写死 8 空格缩进:主机模板 external_labels 只有 4 空格,
一旦给采集器配置了标签就生成非法 YAML,Promtail 装不上。
→ 增加 indent 参数,主机 6 空格 / DaemonSet 8 空格。
3. 新增 system 采集任务:
/var/log/{messages,secure,cron,maillog,syslog,dmesg}
正则解析 syslog 时间戳(Jan _2 15:04:05,Asia/Shanghai)+ 限流
测试:TestHostPromtailConfigValid / CRIPathKeepsCRIStage / PlainFileHasNoCRIStage
TestPromtailManifestParses 全部通过
=== 四、资源用量 CPU 解析(nanocores)===
metrics-server 返回的 CPU 常是纳核(如 "194533645n"=194.5m)。
原解析只处理 "m",导致 194 亿核、页面出现"CPU 已达 limit 的 4863341125%"。
→ parseQuantityMilli 支持 n/u/m/整核
实测:CPU 当前=178m,峰值=3300m,建议"峰值占 limit 82%,可考虑提前扩容"
=== 五、其他 ===
- 添加集群默认改用 Token 认证(最小权限、可随时吊销)
- RBAC 片段补 metrics.k8s.io(资源面板需要,独立 API 组常被漏授)
- 文案去掉 Grafana 需求描述(平台自身不部署 Grafana)
镜像:lghost/lokistack:latest + v0.1 = sha256:7bddd0f4d1f1
58 lines
1.9 KiB
Go
58 lines
1.9 KiB
Go
package services
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
// 主机模式(PromtailConfig)生成的 YAML 必须能被解析,
|
|
// 且系统日志任务不能用 cri 阶段(否则 syslog 格式解析失败丢行)。
|
|
func TestHostPromtailConfigValid(t *testing.T) {
|
|
cfg := PromtailConfig("http://loki.log.svc:3100", "/var/log/myapp/*.log", "pre", `{"env":"pre"}`)
|
|
|
|
var out map[string]interface{}
|
|
if err := yaml.Unmarshal([]byte(cfg), &out); err != nil {
|
|
t.Fatalf("生成的 promtail 配置不是合法 YAML: %v\n%s", err, cfg)
|
|
}
|
|
if _, ok := out["scrape_configs"]; !ok {
|
|
t.Fatal("缺少 scrape_configs")
|
|
}
|
|
// 必须包含系统日志任务
|
|
if !strings.Contains(cfg, "job_name: system") {
|
|
t.Fatal("缺少 system 日志采集任务")
|
|
}
|
|
if !strings.Contains(cfg, "/var/log/{messages,secure,cron,maillog,syslog,dmesg}") {
|
|
t.Fatal("system 任务没有采集系统日志路径")
|
|
}
|
|
// 系统任务段内不能出现 cri 阶段
|
|
sysIdx := strings.Index(cfg, "job_name: system")
|
|
if sysIdx < 0 {
|
|
t.Fatal("找不到 system 任务")
|
|
}
|
|
if strings.Contains(cfg[sysIdx:], "- cri: {}") {
|
|
t.Fatal("system 任务里错误地使用了 cri 阶段")
|
|
}
|
|
}
|
|
|
|
// 容器/Cri 路径仍应保留 cri 解析与时间戳提取
|
|
func TestHostPromtailCRIPathKeepsCRIStage(t *testing.T) {
|
|
cfg := PromtailConfig("http://loki:3100", "/var/log/pods/*/*/*.log", "pre", "")
|
|
if !strings.Contains(cfg, "- cri: {}") {
|
|
t.Fatal("CRI 路径应保留 cri 阶段")
|
|
}
|
|
var out map[string]interface{}
|
|
if err := yaml.Unmarshal([]byte(cfg), &out); err != nil {
|
|
t.Fatalf("YAML 非法: %v", err)
|
|
}
|
|
}
|
|
|
|
// 普通应用文件不应套 cri,否则非 CRI 行会被丢弃
|
|
func TestHostPromtailPlainFileHasNoCRIStage(t *testing.T) {
|
|
cfg := PromtailConfig("http://loki:3100", "/data/app/logs/*.log", "pre", "")
|
|
if strings.Contains(cfg, "- cri: {}") {
|
|
t.Fatal("普通应用日志不应使用 cri 阶段")
|
|
}
|
|
}
|