fix(db): 首次加载历史命名数据文件即迁移为 lokistack.json
旧回退链(lokistack.json → opsxloki.json → lokideploy.json)会永远沿用旧文件名, 旧品牌残留一直不消;现在首次从旧文件成功加载后立即原子写入 lokistack.json, 旧文件改名 .migrated-*.bak 留档(不删数据),新旧并存或写失败时退回旧路径安全运行。
This commit is contained in:
1 parent
4f74fe6c3f
commit
cddd60045c
2 files changed
+87
No files matched your search
@@ -539,6 +539,35 @@ func (d *JsonDB) migrateSchema() bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// migrateLegacyDataFile 首次从历史命名(opsxloki.json / lokideploy.json)加载后,
|
||||||
|
// 立即升级到 lokistack.json:数据原子写入新文件,旧文件改名 .migrated-*.bak 留档。
|
||||||
|
// 不迁移的话回退链会让旧文件名(旧品牌残留)一直被沿用下去。
|
||||||
|
func (d *JsonDB) migrateLegacyDataFile() {
|
||||||
|
const newName = "lokistack.json"
|
||||||
|
if filepath.Base(d.dataFile) == newName {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
legacyPath := d.dataFile
|
||||||
|
newPath := filepath.Join(filepath.Dir(legacyPath), newName)
|
||||||
|
if _, err := os.Stat(newPath); err == nil {
|
||||||
|
// 新旧并存属异常态:不覆盖任何一边,沿用旧路径并留日志交人工判断
|
||||||
|
log.Printf("[db] 检测到 %s 与 %s 并存,沿用 %s(请人工确认)", newPath, legacyPath, legacyPath)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.dataFile = newPath
|
||||||
|
if err := d.doSave(); err != nil {
|
||||||
|
d.dataFile = legacyPath // 写新文件失败:退回旧路径,下次启动再试
|
||||||
|
log.Printf("[ERROR] [db] 迁移数据文件到 %s 失败(%v),继续使用 %s", newPath, err, legacyPath)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
backup := fmt.Sprintf("%s.migrated-%s.bak", legacyPath, time.Now().Format("20060102-150405"))
|
||||||
|
if err := os.Rename(legacyPath, backup); err != nil {
|
||||||
|
log.Printf("[db] 数据已写入 %s,但旧文件 %s 改名留档失败(%v)", newPath, legacyPath, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Printf("[db] 数据文件已从 %s 升级为 %s(原文件留档 %s)", legacyPath, newPath, backup)
|
||||||
|
}
|
||||||
|
|
||||||
// loadFromDisk 读取并解析数据文件,重建 ID 序列;文件损坏时备份留档绝不覆盖。
|
// loadFromDisk 读取并解析数据文件,重建 ID 序列;文件损坏时备份留档绝不覆盖。
|
||||||
func (d *JsonDB) loadFromDisk() {
|
func (d *JsonDB) loadFromDisk() {
|
||||||
if data, err := os.ReadFile(d.dataFile); err == nil {
|
if data, err := os.ReadFile(d.dataFile); err == nil {
|
||||||
@@ -681,6 +710,7 @@ func InitDB() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
d.loadFromDisk()
|
d.loadFromDisk()
|
||||||
|
d.migrateLegacyDataFile()
|
||||||
migratedLogAccess := migrateLogAccessToGroups(d)
|
migratedLogAccess := migrateLogAccessToGroups(d)
|
||||||
// 结构版本迁移(审计表、采集项等新字段的兼容处理)
|
// 结构版本迁移(审计表、采集项等新字段的兼容处理)
|
||||||
migratedSchema := d.migrateSchema()
|
migratedSchema := d.migrateSchema()
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestMigrateLegacyDataFile 首次从历史命名(opsxloki.json)加载后必须升级到 lokistack.json:
|
||||||
|
// 新文件出现且内容完整,旧文件改名留档(不删数据)。
|
||||||
|
func TestMigrateLegacyDataFile(t *testing.T) {
|
||||||
|
dir := t.TempDir()
|
||||||
|
legacy := filepath.Join(dir, "opsxloki.json")
|
||||||
|
seed := `{"schemaVersion":10,"users":[{"id":1,"username":"admin"}],"clusters":[],"hosts":[],"storages":[],"deployments":[],"collectors":[],"logs":[],"settings":[],"logSources":[],"alertRules":[],"alertDeliveries":[],"notifyChannels":[],"logPolicyGroups":[],"auditLogs":[],"dailyVolumeSnapshots":[]}`
|
||||||
|
if err := os.WriteFile(legacy, []byte(seed), 0o600); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
d := &JsonDB{dataFile: legacy}
|
||||||
|
d.loadFromDisk()
|
||||||
|
d.migrateLegacyDataFile()
|
||||||
|
|
||||||
|
// 新文件存在且包含原有数据
|
||||||
|
content, err := os.ReadFile(filepath.Join(dir, "lokistack.json"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("未生成 lokistack.json: %v", err)
|
||||||
|
}
|
||||||
|
if !strings.Contains(string(content), `"admin"`) {
|
||||||
|
t.Fatalf("迁移后数据不完整: %s", content[:min(len(content), 120)])
|
||||||
|
}
|
||||||
|
// 旧文件被改名留档,原内容不丢
|
||||||
|
entries, _ := os.ReadDir(dir)
|
||||||
|
backedUp := false
|
||||||
|
for _, e := range entries {
|
||||||
|
if strings.HasPrefix(e.Name(), "opsxloki.json.migrated-") {
|
||||||
|
kept, _ := os.ReadFile(filepath.Join(dir, e.Name()))
|
||||||
|
if string(kept) == seed {
|
||||||
|
backedUp = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !backedUp {
|
||||||
|
t.Fatalf("旧文件未留档或内容被改动: %v", entries)
|
||||||
|
}
|
||||||
|
// dataFile 已切到新路径
|
||||||
|
if filepath.Base(d.dataFile) != "lokistack.json" {
|
||||||
|
t.Fatalf("dataFile 未切换: %s", d.dataFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func min(a, b int) int {
|
||||||
|
if a < b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
Reference in new issue
Block a user