fix: 修复备份工具的多处问题
- go.mod: 将非法版本号 go 1.26.4 改为 go 1.26 - engine: 压缩失败时删除残留的半截 tar.gz 文件 - config: 支持仅按天数保留(count 和 days 均未设时才用默认 count=7) - engine: docker compose stop 失败时也尝试重启,避免容器停摆 - list: 仅显示真正的备份归档文件(新增导出 IsBackupArchive) - systemd: 移除 PrivateTmp,暂存目录改到安装目录内,避免私有 /tmp 被大目录撑爆
This commit is contained in:
@@ -83,9 +83,12 @@ func (e *Engine) backupOne(ctx context.Context, proj config.ProjectConfig) Resul
|
||||
|
||||
executor := NewComposeExecutor(proj.Path, proj.ComposeFile)
|
||||
|
||||
// 1. Stop the compose project.
|
||||
// 1. Stop the compose project. If the stop is interrupted midway
|
||||
// (e.g. the context is cancelled), containers may be left stopped, so
|
||||
// always attempt a restart before returning.
|
||||
stopStart := time.Now()
|
||||
if err := executor.Stop(ctx); err != nil {
|
||||
_ = restartCompose(executor)
|
||||
result.Error = fmt.Errorf("stop: %w", err)
|
||||
result.Duration = time.Since(start)
|
||||
return result
|
||||
@@ -226,9 +229,14 @@ func createTarGz(ctx context.Context, srcDir, dstPath string) error {
|
||||
|
||||
closeErr := closeTarGz(tw, gw, f)
|
||||
if walkErr != nil {
|
||||
_ = os.Remove(dstPath)
|
||||
return walkErr
|
||||
}
|
||||
return closeErr
|
||||
if closeErr != nil {
|
||||
_ = os.Remove(dstPath)
|
||||
return closeErr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func closeTarGz(tw *tar.Writer, gw *gzip.Writer, f *os.File) error {
|
||||
|
||||
@@ -87,5 +87,10 @@ func ApplyRetention(backupDir, projectName string, policy RetentionPolicy) (int,
|
||||
}
|
||||
|
||||
func isBackupArchive(projectName, name string) bool {
|
||||
return IsBackupArchive(projectName, name)
|
||||
}
|
||||
|
||||
// IsBackupArchive reports whether name is a backup archive for the project.
|
||||
func IsBackupArchive(projectName, name string) bool {
|
||||
return strings.HasPrefix(name, projectName+"-") && strings.HasSuffix(name, ".tar.gz")
|
||||
}
|
||||
@@ -85,7 +85,9 @@ func (c *Config) Validate() error {
|
||||
if p.ComposeFile == "" {
|
||||
p.ComposeFile = "docker-compose.yml"
|
||||
}
|
||||
if p.Retention.Count <= 0 {
|
||||
// Apply the default count only when no retention policy is set at all.
|
||||
// This lets a user configure days-only retention (count omitted).
|
||||
if p.Retention.Count <= 0 && p.Retention.Days <= 0 {
|
||||
p.Retention.Count = DefaultRetentionCount
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,34 @@ projects:
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadDaysOnlyRetention(t *testing.T) {
|
||||
yaml := `
|
||||
global:
|
||||
backup_dir: /tmp/backups
|
||||
projects:
|
||||
- name: testapp
|
||||
path: /opt/testapp
|
||||
retention:
|
||||
days: 30
|
||||
`
|
||||
dir := t.TempDir()
|
||||
path := filepath.Join(dir, "config.yaml")
|
||||
if err := os.WriteFile(path, []byte(yaml), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cfg, err := Load(path)
|
||||
if err != nil {
|
||||
t.Fatalf("Load: %v", err)
|
||||
}
|
||||
p := cfg.Projects[0]
|
||||
if p.Retention.Count != 0 {
|
||||
t.Errorf("count should stay 0 for days-only retention, got %d", p.Retention.Count)
|
||||
}
|
||||
if p.Retention.Days != 30 {
|
||||
t.Errorf("days = %d", p.Retention.Days)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadMissingBackupDir(t *testing.T) {
|
||||
yaml := `
|
||||
projects:
|
||||
|
||||
Reference in New Issue
Block a user