Files
LokiStack/backend/services/compactor_workers_test.go
T
opsx bad2a1e624 fix(hosts,collectors,usage): 主机验证结果失真 + 主机模式系统日志解析 + metrics 单位
=== 一、主机「测试连接」永远显示旧错误(真实 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
2026-10-28 17:58:30 +00:00

72 lines
2.5 KiB
Go

package services
import (
"strings"
"testing"
"lokistack/models"
)
// 删除并发必须按对象存储类型区分:各云 DELETE 限流差别很大,
// 统一一个值要么被限流(COS 503 SlowDown),要么白等(S3 能扛更多)。
func TestCompactorDeleteWorkersPerStorageType(t *testing.T) {
cases := map[string]int{
"cos": 16, // 实测 150 触发 503 SlowDown
"obs": 32, // 华为 OBS 保守值
"minio": 32, // 自建,取决于磁盘
"azure": 32, // 存储账户限流较严
"oss": 64, // 阿里云 OSS QPS 较高
"gcs": 64,
"s3": 96, // AWS S3 限流宽松
"": defaultCompactorDeleteWorkers,
"unknown": defaultCompactorDeleteWorkers,
}
for storageType, want := range cases {
got := compactorDeleteWorkers(&models.StorageConfig{StorageType: storageType})
if got != want {
t.Errorf("storageType=%q: got %d, want %d", storageType, got, want)
}
}
// nil 存储配置不能 panic
if got := compactorDeleteWorkers(nil); got != defaultCompactorDeleteWorkers {
t.Errorf("nil: got %d, want %d", got, defaultCompactorDeleteWorkers)
}
}
// 运维应能用环境变量覆盖,不必改代码发版
func TestCompactorDeleteWorkersEnvOverride(t *testing.T) {
t.Setenv("LOKI_COMPACTOR_DELETE_WORKERS", "48")
if got := compactorDeleteWorkers(&models.StorageConfig{StorageType: "cos"}); got != 48 {
t.Errorf("env override: got %d, want 48", got)
}
// 非法值回落到 provider 默认,不能让 Loki 起不来
t.Setenv("LOKI_COMPACTOR_DELETE_WORKERS", "abc")
if got := compactorDeleteWorkers(&models.StorageConfig{StorageType: "cos"}); got != 16 {
t.Errorf("invalid env: got %d, want 16", got)
}
}
// 生成的配置里必须出现对应 provider 的并发值(防止模板变量漏渲染)
func TestGeneratedConfigCarriesProviderDeleteWorkers(t *testing.T) {
for storageType, want := range map[string]string{
"cos": "retention_delete_worker_count: 16",
"s3": "retention_delete_worker_count: 96",
} {
d := NewLokiDeployer(&models.LokiDeployment{
RetentionDays: 7, Replicas: 1,
StorageConfig: &models.StorageConfig{
StorageType: storageType, Bucket: "b", Region: "r",
Endpoint: "e", AccessKey: "ak", SecretKey: "sk",
},
}, func(level, message, detail string) {})
cfg := d.generateLokiConfig()
if !strings.Contains(cfg, want) {
t.Errorf("storageType=%s: generated config missing %q", storageType, want)
}
if !strings.Contains(cfg, "retention_delete_delay: 10m") {
t.Errorf("storageType=%s: missing retention_delete_delay", storageType)
}
}
}