fix(deploy): 在线更新备份 - 兼容 root-only 历史数据文件

This commit is contained in:
ddCat
2026-08-30 23:56:33 +08:00
parent 9820be4e8c
commit fb089b2d21
4 changed files with 45 additions and 2 deletions

View File

@@ -2,6 +2,10 @@
## Unreleased
## v1.2.2-preview.3
- 修复历史数据目录包含 root-only 备份文件时,在线更新无法创建数据目录 ZIP 的问题;仅备份命令以容器 root 身份读取挂载目录,业务 Backend 仍保持非 root 运行。
## v1.2.2-preview.2
- 在线更新页明确提示每次升级都会重新备份数据库,并校验镜像摘要、数据库迁移与服务健康状态。

View File

@@ -1 +1 @@
v1.2.2-preview.2
v1.2.2-preview.3

View File

@@ -301,7 +301,7 @@ func (m *Manager) createBackup(version string) (Backup, error) {
if err != nil {
return Backup{}, err
}
if err := m.compose(m.composePath(), version, m.config.StepTimeout, dataEntry, "exec", "-T", "backend", "tar", "-C", "/data", "-cf", "-", "."); err != nil {
if err := m.compose(m.composePath(), version, m.config.StepTimeout, dataEntry, "exec", "-T", "--user", "root", "backend", "tar", "-C", "/data", "-cf", "-", "."); err != nil {
return Backup{}, fmt.Errorf("备份数据目录:%w", err)
}
if err := archive.Close(); err != nil {

View File

@@ -2,6 +2,7 @@ package hostupdate
import (
"archive/zip"
"context"
"crypto/sha256"
"encoding/hex"
"io"
@@ -11,6 +12,18 @@ import (
"testing"
)
type recordingRunner struct {
calls [][]string
}
func (r *recordingRunner) Run(_ context.Context, _ string, args, _ []string, stdout, _ io.Writer) error {
r.calls = append(r.calls, append([]string(nil), args...))
if stdout != nil {
_, _ = io.WriteString(stdout, "backup-fixture")
}
return nil
}
func TestSetEnvValuePreservesOtherSettings(t *testing.T) {
directory := t.TempDir()
path := filepath.Join(directory, ".env")
@@ -90,3 +103,29 @@ func TestCurrentVersionRejectsLatest(t *testing.T) {
t.Fatal("latest tag was accepted")
}
}
func TestCreateBackupReadsBackendDataAsRoot(t *testing.T) {
installDir := t.TempDir()
backupDir := filepath.Join(installDir, "backups")
if err := os.MkdirAll(backupDir, 0o700); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(installDir, ".env"), []byte("POSTGRES_USER=canvas\nPOSTGRES_DB=canvas\n"), 0o600); err != nil {
t.Fatal(err)
}
runner := &recordingRunner{}
manager := &Manager{
config: Config{InstallDir: installDir, ComposeFile: "docker-compose.deploy.yml", EnvFile: ".env", BackupDir: backupDir},
runner: runner,
}
if _, err := manager.createBackup("v1.2.2-preview.2"); err != nil {
t.Fatal(err)
}
for _, call := range runner.calls {
joined := strings.Join(call, " ")
if strings.Contains(joined, "exec -T --user root backend tar -C /data -cf - .") {
return
}
}
t.Fatalf("backend data backup did not use root: %#v", runner.calls)
}