87 lines
2.9 KiB
Go
87 lines
2.9 KiB
Go
package deployment
|
||
|
||
import (
|
||
"context"
|
||
"errors"
|
||
"fmt"
|
||
)
|
||
|
||
// validateTransactionLocks 在任何文件变更或解锁之前核验完整锁集合。
|
||
// 已完成核验的收尾阶段允许锁已不存在,但存在的无归属锁绝不自动接管。
|
||
func validateTransactionLocks(ctx context.Context, client Client, record TransactionRecord, allowMissing bool) error {
|
||
if record.Evidence == nil || !validDigest(record.Evidence.LockToken) {
|
||
return errors.New("缺少可信部署锁证据")
|
||
}
|
||
for _, path := range record.LockPaths {
|
||
exists, err := client.Exists(ctx, path)
|
||
if err != nil {
|
||
return fmt.Errorf("检查部署锁失败:%w", err)
|
||
}
|
||
if !exists && allowMissing {
|
||
continue
|
||
}
|
||
if err := checkOwnedLock(ctx, client, path, record.Evidence.LockToken, false); err != nil {
|
||
return fmt.Errorf("部署锁归属预检失败 %s:%w", path, err)
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// validateRollbackFiles 先核验所有旧文件和目标文件,再允许逐个还原,避免部分覆盖现场。
|
||
func validateRollbackFiles(ctx context.Context, client Client, record TransactionRecord) error {
|
||
if err := validateTransactionLocks(ctx, client, record, false); err != nil {
|
||
return err
|
||
}
|
||
if err := validateTemporaryFiles(ctx, client, record); err != nil {
|
||
return err
|
||
}
|
||
for _, backup := range record.Backups {
|
||
backupExists, err := validateRecoveryBackup(ctx, client, backup)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
targetExists, err := client.Exists(ctx, backup.Original)
|
||
if err != nil {
|
||
return fmt.Errorf("检查待恢复目标失败:%w", err)
|
||
}
|
||
if backup.OriginalExists && !backupExists {
|
||
if !targetExists {
|
||
return fmt.Errorf("备份文件和目标文件均不存在:%s", backup.Original)
|
||
}
|
||
if err := checkFileDigest(ctx, client, backup.Original, backup.OriginalDigest); err != nil {
|
||
return fmt.Errorf("无法证明旧文件已恢复:%w", err)
|
||
}
|
||
continue
|
||
}
|
||
if !targetExists {
|
||
continue
|
||
}
|
||
if err := checkFileDigest(ctx, client, backup.Original, record.Evidence.Files[backup.Original]); err != nil {
|
||
if !backup.OriginalExists {
|
||
return fmt.Errorf("新增目标文件身份不一致:%w", err)
|
||
}
|
||
if oldErr := checkFileDigest(ctx, client, backup.Original, backup.OriginalDigest); oldErr != nil {
|
||
return fmt.Errorf("目标既不是本次部署文件也不是原始文件:%w", oldErr)
|
||
}
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func validateRecoveryBackup(ctx context.Context, client Client, backup TransactionBackup) (bool, error) {
|
||
exists, err := client.Exists(ctx, backup.Saved)
|
||
if err != nil {
|
||
return false, fmt.Errorf("检查恢复备份失败:%w", err)
|
||
}
|
||
if !exists {
|
||
return false, nil
|
||
}
|
||
if !backup.OriginalExists {
|
||
return true, fmt.Errorf("发现不属于本次事务的备份文件:%s", backup.Saved)
|
||
}
|
||
if err := checkFileDigest(ctx, client, backup.Saved, backup.OriginalDigest); err != nil {
|
||
return true, fmt.Errorf("恢复备份身份不一致:%w", err)
|
||
}
|
||
return true, nil
|
||
}
|