建立每日自动异机备份基线(07-27-automatic-offhost-backup)

这个提交包含在:
2026-07-27 10:50:01 +08:00
父节点 325c592aba
当前提交 b78f2a11ce
共修改 19 个文件,包含 2070 行新增和 10 行删除
+90
查看文件
@@ -19,6 +19,16 @@ const timerScriptPath = path.resolve(
process.env.ENV_CONTRACT_TIMER_SCRIPT_PATH
|| path.join(rootDir, 'deploy/trialrun/install-warch3-ops-timer.sh'),
);
const automaticBackupTimerScriptPath = path.join(
rootDir,
'deploy/trialrun/install-automatic-backup-timer.sh',
);
const offhostReceiverInstallerPath = path.join(
rootDir,
'deploy/trialrun/install-offhost-backup-receiver.sh',
);
const automaticBackupRunnerPath = path.join(rootDir, 'scripts/run-automatic-backup.sh');
const offhostReceiverPath = path.join(rootDir, 'scripts/receive-offhost-backup.sh');
const nginxPaths = [
path.join(rootDir, 'deploy/trialrun/nginx-hyb-oa.conf'),
path.join(rootDir, 'deploy/trialrun/nginx.conf.example'),
@@ -143,6 +153,10 @@ const runbookContent = readFile(runbookPath);
const checklistContent = readFile(checklistPath);
const deployScriptContent = readFile(deployScriptPath);
const timerScriptContent = readFile(timerScriptPath);
const automaticBackupTimerScriptContent = readFile(automaticBackupTimerScriptPath);
const offhostReceiverInstallerContent = readFile(offhostReceiverInstallerPath);
const automaticBackupRunnerContent = readFile(automaticBackupRunnerPath);
const offhostReceiverContent = readFile(offhostReceiverPath);
const nginxContents = nginxPaths.map((filePath) => ({
filePath,
content: readFile(filePath),
@@ -274,6 +288,82 @@ for (const requiredFragment of [
}
}
for (const requiredFragment of [
'MODE="${1:-install}"',
'write_service daily run',
'write_service watchdog check-stale',
'write_service drill-reminder remind-drill',
'write_timer daily',
'write_timer watchdog',
'write_timer drill-reminder',
'Environment=COMPOSE_PROJECT_NAME=${COMPOSE_PROJECT_NAME}',
'Environment=POSTGRES_DATA_DIR=${POSTGRES_DATA_DIR}',
'Environment=STORAGE_HOST_DIR=${STORAGE_HOST_DIR}',
'Environment=BUILD_SHA=${BUILD_SHA}',
'Environment=VITE_BUILD_SHA=${VITE_BUILD_SHA}',
'Environment=AUTOMATIC_BACKUP_DIR=${AUTOMATIC_BACKUP_DIR}',
'Environment=AUTOMATIC_BACKUP_STATE_DIR=${AUTOMATIC_BACKUP_STATE_DIR}',
'Environment=OFFHOST_TARGET=${OFFHOST_TARGET}',
'Environment=OFFHOST_SSH_KEY=${OFFHOST_SSH_KEY}',
'Environment=OFFHOST_KNOWN_HOSTS=${OFFHOST_KNOWN_HOSTS}',
'install)\n install_units',
'enable)\n enable_units',
]) {
if (!automaticBackupTimerScriptContent.includes(requiredFragment)) {
errors.push(`install-automatic-backup-timer.sh 缺少自动备份安装合同: ${requiredFragment}`);
}
}
for (const requiredFragment of [
'COMPOSE_CMD=(docker compose)',
'-p "$COMPOSE_PROJECT_NAME"',
'flock -n 9',
'"$actual_storage" == "$STORAGE_HOST_DIR"',
'"$actual_pgdata" == "$POSTGRES_DATA_DIR"',
'--partial --chmod=Du=rwx,Dgo=,Fu=rw,Fgo=',
'StrictHostKeyChecking=yes',
'hyb-backup-finalize $run_id $manifest_sha',
'write_success_state "$run_id" "$manifest_sha"',
'prune_local_backups "$batch_name"',
'--summary-file=-',
]) {
if (!automaticBackupRunnerContent.includes(requiredFragment)) {
errors.push(`run-automatic-backup.sh 缺少运行时安全合同: ${requiredFragment}`);
}
}
for (const requiredFragment of [
'"$original_command" == rsync\\ --server\\ *',
'--sender([[:space:]]|$)',
'exec "$RRSYNC_BIN" -wo "$BACKUP_INBOX_DIR"',
'hyb-backup-finalize',
'batch must contain exactly three regular files',
'manifest artifact checksum mismatch',
'mv -- "$partial_dir" "$archive_dir"',
'prune_named_directories "$BACKUP_ARCHIVE_DIR"',
'prune_named_directories "$BACKUP_WEEKLY_DIR"',
]) {
if (!offhostReceiverContent.includes(requiredFragment)) {
errors.push(`receive-offhost-backup.sh 缺少受限接收合同: ${requiredFragment}`);
}
}
for (const requiredFragment of [
'MODE="${1:-install}"',
'passwd -l "$BACKUP_USER"',
'command="${RECEIVER_TARGET}",no-agent-forwarding,no-port-forwarding,no-X11-forwarding,no-pty,no-user-rc',
'BACKUP_INBOX_DIR=${inbox_dir}',
'BACKUP_ARCHIVE_DIR=${archive_dir}',
'BACKUP_WEEKLY_DIR=${weekly_dir}',
'archives retained',
'backup root contains an unmanaged top-level entry',
'backup root must not be inside a system-managed directory',
]) {
if (!offhostReceiverInstallerContent.includes(requiredFragment)) {
errors.push(`install-offhost-backup-receiver.sh 缺少受限身份安装合同: ${requiredFragment}`);
}
}
for (const requiredFragment of [
'validate_data_path POSTGRES_DATA_DIR "$POSTGRES_DATA_DIR"',
'validate_data_path STORAGE_HOST_DIR "$STORAGE_HOST_DIR"',