建立每日自动异机备份基线(07-27-automatic-offhost-backup)
这个提交包含在:
@@ -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"',
|
||||
|
||||
在新工单中引用
屏蔽一个用户