diff --git a/backend/main.go b/backend/main.go index a4e15dc..6b115be 100644 --- a/backend/main.go +++ b/backend/main.go @@ -81,6 +81,9 @@ func main() { log.Println("Seeding default data...") seed.Seed() + + // 清理旧版本删除部署后残留的托管数据源(幂等) + services.CleanupOrphanManagedLogSources() // 补齐/升级内置告警规则(幂等):启动即执行,存量环境也能拿到「默认启用+绑默认通道」 seed.SeedAlertRules() diff --git a/backend/services/log_source.go b/backend/services/log_source.go index 182cb00..d724096 100644 --- a/backend/services/log_source.go +++ b/backend/services/log_source.go @@ -433,6 +433,58 @@ func CleanupManagedLogSource(name string) { } } +// CleanupOrphanManagedLogSources 启动时清理孤儿托管数据源: +// 部署记录已删除、或所属集群已删除的 managed 源。 +// +// 为什么需要:CleanupManagedLogSource 只在「删除部署」的代码路径里执行; +// 如果部署是在旧版本(没有清理逻辑)删掉的,或删除过程中进程退出, +// 托管源就会永久残留,在数据源列表里指向一个不存在的实例。 +func CleanupOrphanManagedLogSources() { + db := repository.GetDB() + if db == nil { + return + } + db.Lock() + names := make(map[string]bool, len(db.Deployments)) + for _, d := range db.Deployments { + names[d.Name] = true + } + clusters := make(map[uint]bool, len(db.Clusters)) + for _, c := range db.Clusters { + clusters[c.ID] = true + } + kept := make([]models.LogSource, 0, len(db.LogSources)) + removed, removedDefault := 0, false + for _, s := range db.LogSources { + orphan := false + if s.Managed { + if !names[s.Name] { + orphan = true + } + if s.ClusterID != nil && !clusters[*s.ClusterID] { + orphan = true + } + } + if orphan { + removed++ + if s.IsDefault { + removedDefault = true + } + continue + } + kept = append(kept, s) + } + if removed > 0 { + if removedDefault && len(kept) > 0 { + kept[0].IsDefault = true + } + db.LogSources = kept + db.RequestSave() + log.Printf("[startup] 清理 %d 个孤儿托管数据源", removed) + } + db.Unlock() +} + // EnsureLogSourceForDeployment 部署成功后自动注册查询数据源(host→url;k8s→cluster-svc)。 // 已存在同名数据源时:平台托管的自动刷新连接信息,用户手填的不覆盖。 func EnsureLogSourceForDeployment(d *models.LokiDeployment) {