109 lines
3.6 KiB
Bash
Executable File
109 lines
3.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
umask 077
|
|
|
|
# 生成供后台只读展示的备份状态摘要。内容不包含密钥、备份载荷或下载地址。
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
# shellcheck source=backup-common.sh
|
|
source "${SCRIPT_DIR}/backup-common.sh"
|
|
|
|
json_escape() {
|
|
local value="$1"
|
|
value="${value//\\/\\\\}"
|
|
value="${value//\"/\\\"}"
|
|
value="${value//$'\n'/ }"
|
|
value="${value//$'\r'/ }"
|
|
printf '%s' "${value}"
|
|
}
|
|
|
|
config_value() {
|
|
local key="$1" fallback="$2" value
|
|
value="$(backup_mysql_root_query "SELECT value FROM system_configs WHERE \`key\` = '${key}' LIMIT 1;" 2>/dev/null || true)"
|
|
value="${value//$'\n'/}"
|
|
printf '%s' "${value:-${fallback}}"
|
|
}
|
|
|
|
write_status() {
|
|
backup_require_cmd docker
|
|
backup_require_cmd ossutil
|
|
|
|
local backup_dir oss_uri status_dir status_file temp_file enabled hour minute interval
|
|
local local_complete remote_complete binlog_state binlog_file binlog_remote job_type job_status job_at job_message health
|
|
backup_dir="$(backup_require_env BACKUP_DIR)"
|
|
oss_uri="$(backup_require_env BACKUP_OSS_URI)"
|
|
status_dir="${BACKUP_STATUS_DIR:-${backup_dir}/status}"
|
|
backup_validate_dir "${status_dir}"
|
|
mkdir -p "${status_dir}"
|
|
chmod 700 "${status_dir}"
|
|
status_file="${status_dir}/status.json"
|
|
temp_file="${status_dir}/.status-$$.json"
|
|
|
|
enabled="$(config_value 'backup.schedule_enabled' 'true')"
|
|
hour="$(config_value 'backup.full_hour' '4')"
|
|
minute="$(config_value 'backup.full_minute' '30')"
|
|
interval="$(config_value 'backup.binlog_interval_minutes' '1')"
|
|
local_complete="$(find "${backup_dir}/full/daily" -name complete.env -type f -print 2>/dev/null | sort | tail -1 || true)"
|
|
remote_complete="$(ossutil ls "${oss_uri}/mysql/full/daily/" -r 2>/dev/null | awk '$NF ~ /\/complete\.env$/ { latest = $NF } END { print latest }')"
|
|
binlog_state="$(find "${backup_dir}/binlog-state" -name last-archived.env -type f -print 2>/dev/null | sort | tail -1 || true)"
|
|
binlog_file=''
|
|
binlog_remote=''
|
|
if [[ -n "${binlog_state}" ]]; then
|
|
binlog_file="$(awk -F= '$1 == "filename" { print substr($0, index($0, "=") + 1); exit }' "${binlog_state}")"
|
|
binlog_remote="$(awk -F= '$1 == "remote_dir" { print substr($0, index($0, "=") + 1); exit }' "${binlog_state}")"
|
|
fi
|
|
|
|
job_type=''
|
|
job_status=''
|
|
job_at=''
|
|
job_message=''
|
|
if [[ -f "${status_dir}/last-job.env" ]]; then
|
|
# shellcheck disable=SC1090
|
|
source "${status_dir}/last-job.env"
|
|
fi
|
|
if [[ "${job_status}" == 'failed' ]]; then
|
|
health='warning'
|
|
elif [[ -n "${local_complete}" && -n "${remote_complete}" && -n "${binlog_file}" ]]; then
|
|
health='healthy'
|
|
else
|
|
health='warning'
|
|
fi
|
|
|
|
cat > "${temp_file}" <<EOF
|
|
{
|
|
"generated_at": "$(date -Iseconds)",
|
|
"health": "$(json_escape "${health}")",
|
|
"schedule": {
|
|
"enabled": "$(json_escape "${enabled}")",
|
|
"full_hour": "$(json_escape "${hour}")",
|
|
"full_minute": "$(json_escape "${minute}")",
|
|
"binlog_interval_minutes": "$(json_escape "${interval}")"
|
|
},
|
|
"last_full": {
|
|
"local_complete": "$(json_escape "${local_complete}")",
|
|
"remote_complete": "$(json_escape "${remote_complete}")"
|
|
},
|
|
"last_binlog": {
|
|
"file": "$(json_escape "${binlog_file}")",
|
|
"remote_dir": "$(json_escape "${binlog_remote}")"
|
|
},
|
|
"last_job": {
|
|
"type": "$(json_escape "${job_type}")",
|
|
"status": "$(json_escape "${job_status}")",
|
|
"at": "$(json_escape "${job_at}")",
|
|
"message": "$(json_escape "${job_message}")"
|
|
}
|
|
}
|
|
EOF
|
|
chmod 640 "${temp_file}"
|
|
mv "${temp_file}" "${status_file}"
|
|
}
|
|
|
|
case "${1:-write}" in
|
|
write) write_status ;;
|
|
*)
|
|
printf '用法: %s {write}\n' "$0" >&2
|
|
exit 1
|
|
;;
|
|
esac
|