mb-backup-manager/scripts/backup-logic.sh

258 lines
11 KiB
Bash
Raw Normal View History

2024-03-13 13:25:45 +00:00
#!/bin/bash
BASE_URL=$2
BACKUP_TYPE=$3
BACKUP_LOG_FILE=$4
ENV_NAME=$5
BACKUP_COUNT=$6
APP_PATH=$7
USER_SESSION=$8
USER_EMAIL=$9
BACKUP_PATH=${10}
# Ensure restic is installed
if ! which restic &>/dev/null; then
if which dnf &>/dev/null; then
dnf install -y epel-release
dnf install -y restic
elif which yum &>/dev/null; then
yum-config-manager --add-repo https://copr.fedorainfracloud.org/coprs/copart/restic/repo/epel-7/copart-restic-epel-7.repo
yum-config-manager --enable copart-restic
yum -y install restic
yum-config-manager --disable copart-restic
fi
fi
# Function definitions remain the same, but adjustments are made to use $BACKUP_PATH
function update_restic(){
restic self-update 2>&1;
}
2024-11-12 17:13:53 +00:00
function check_backup_repo() {
2024-11-12 17:21:04 +00:00
local backup_repo_path="/mnt/backups/${ENV_NAME}"
2024-11-12 17:00:19 +00:00
local password_file="/etc/restic-password"
2024-11-12 17:21:04 +00:00
local backup_log_file="/var/log/backup_addon.log"
2024-11-12 17:00:19 +00:00
2024-11-12 17:18:35 +00:00
# Ensure the backup repository directory exists
2024-03-13 13:25:45 +00:00
[ -d "${backup_repo_path}" ] || mkdir -p "${backup_repo_path}"
2024-11-12 17:00:19 +00:00
2024-11-12 17:18:35 +00:00
# Generate a new 10-character random password if password file does not exist or is empty
if [ ! -f "$password_file" ] || [ -z "$(cat "$password_file")" ]; then
local new_password=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 10)
echo "$new_password" > "$password_file"
echo "$(date) Password file created with a new random password." | tee -a "${backup_log_file}"
2024-11-12 17:00:19 +00:00
fi
2024-11-12 17:18:35 +00:00
# Load the password from the file for use with Restic
export RESTIC_PASSWORD=$(cat "$password_file")
2024-11-12 17:00:19 +00:00
export RESTIC_REPOSITORY="$backup_repo_path"
2024-03-13 13:25:45 +00:00
export FILES_COUNT=$(find "${backup_repo_path}" -mindepth 1 | wc -l)
2024-11-12 17:13:53 +00:00
2024-11-12 17:21:04 +00:00
# Check if the repository is accessible with the current password
2024-03-13 13:25:45 +00:00
if [ "${FILES_COUNT}" -gt 0 ]; then
2024-11-12 17:18:35 +00:00
echo "$(date) ${ENV_NAME} Checking the backup repository integrity and consistency" | tee -a "${backup_log_file}"
2024-11-12 17:21:04 +00:00
if ! GOGC=20 restic -r "${backup_repo_path}" snapshots > /dev/null 2>&1; then
echo "$(date) ${ENV_NAME} ERROR: Repository exists but password does not match." | tee -a "${backup_log_file}"
echo "Please reinitialize the repository manually or adjust the password file." | tee -a "${backup_log_file}"
exit 1
fi
2024-11-12 17:18:35 +00:00
# Remove stale lock if exists
2024-03-13 13:25:45 +00:00
if [[ $(ls -A "${backup_repo_path}/locks") ]]; then
2024-11-12 17:18:35 +00:00
echo "$(date) ${ENV_NAME} Backup repository has a stale lock, removing" | tee -a "${backup_log_file}"
2024-11-12 17:00:19 +00:00
GOGC=20 restic -r "${backup_repo_path}" unlock
2024-03-13 13:25:45 +00:00
sendEmailNotification
fi
2024-11-12 17:18:35 +00:00
# Run the repository check command
2024-11-12 17:00:19 +00:00
if ! GOGC=20 restic -q -r "${backup_repo_path}" check --read-data-subset=5%; then
2024-11-12 17:18:35 +00:00
echo "Backup repository integrity check failed." | tee -a "${backup_log_file}"
2024-03-13 13:25:45 +00:00
exit 1
fi
else
2024-11-12 17:18:35 +00:00
echo "$(date) ${ENV_NAME} Initializing new backup repository" | tee -a "${backup_log_file}"
2024-11-12 17:00:19 +00:00
GOGC=20 restic init -r "${backup_repo_path}"
2024-03-13 13:25:45 +00:00
fi
}
function sendEmailNotification() {
if [ -e "/usr/lib/jelastic/modules/api.module" ]; then
[ -e "/var/run/jem.pid" ] && return 0;
CURRENT_PLATFORM_MAJOR_VERSION=$(jem api apicall -s --connect-timeout 3 --max-time 15 [API_DOMAIN]/1.0/statistic/system/rest/getversion 2>/dev/null | jq .version | grep -o [0-9.]* | awk -F . '{print $1}')
if [ "${CURRENT_PLATFORM_MAJOR_VERSION}" -ge "7" ]; then
echo "$(date) ${ENV_NAME} Sending e-mail notification about removing the stale lock" | tee -a "$BACKUP_LOG_FILE"
SUBJECT="Stale lock is removed on ${BACKUP_PATH}/${ENV_NAME} backup repo"
BODY="Please pay attention to ${BACKUP_PATH}/${ENV_NAME} backup repo because the stale lock left from previous operation is removed during the integrity check and backup rotation. Manual check of backup repo integrity and consistency is highly desired."
jem api apicall -s --connect-timeout 3 --max-time 15 [API_DOMAIN]/1.0/message/email/rest/send --data-urlencode "session=$USER_SESSION" --data-urlencode "to=$USER_EMAIL" --data-urlencode "subject=$SUBJECT" --data-urlencode "body=$BODY"
if [[ $? != 0 ]]; then
echo "$(date) ${ENV_NAME} Sending of e-mail notification failed" | tee -a "$BACKUP_LOG_FILE"
else
echo "$(date) ${ENV_NAME} E-mail notification is sent successfully" | tee -a "$BACKUP_LOG_FILE"
fi
elif [ -z "${CURRENT_PLATFORM_MAJOR_VERSION}" ]; then
echo "$(date) ${ENV_NAME} Error when checking the platform version" | tee -a "$BACKUP_LOG_FILE"
else
echo "$(date) ${ENV_NAME} Email notification is not sent because this functionality is unavailable for current platform version." | tee -a "$BACKUP_LOG_FILE"
fi
else
echo "$(date) ${ENV_NAME} Email notification is not sent because this functionality is unavailable for current platform version." | tee -a "$BACKUP_LOG_FILE"
fi
}
function rotate_snapshots(){
local backup_repo_path="/mnt/backups/${ENV_NAME}"
echo "$(date) ${ENV_NAME} Rotating snapshots by keeping the last ${BACKUP_COUNT}" | tee -a "${BACKUP_LOG_FILE}"
if [[ $(ls -A "${backup_repo_path}/locks") ]]; then
echo "$(date) ${ENV_NAME} Backup repository has a stale lock, removing" | tee -a "${BACKUP_LOG_FILE}"
GOGC=20 RESTIC_PASSWORD="${ENV_NAME}" restic -r "${backup_repo_path}" unlock
sendEmailNotification
fi
if ! GOGC=20 RESTIC_COMPRESSION=off RESTIC_PACK_SIZE=8 RESTIC_PASSWORD="${ENV_NAME}" restic forget -q -r "${backup_repo_path}" --keep-last "${BACKUP_COUNT}" --prune | tee -a "${BACKUP_LOG_FILE}"; then
echo "Backup rotation failed." | tee -a "${BACKUP_LOG_FILE}"
exit 1
fi
}
function create_snapshot(){
local backup_repo_path="/mnt/backups/${ENV_NAME}"
DUMP_NAME=$(date "+%F_%H%M%S_%Z")
echo "$(date) ${ENV_NAME} Begin uploading the ${DUMP_NAME} snapshot to backup storage" | tee -a "${BACKUP_LOG_FILE}"
if ! GOGC=20 RESTIC_COMPRESSION=off RESTIC_PACK_SIZE=8 RESTIC_READ_CONCURRENCY=8 RESTIC_PASSWORD="${ENV_NAME}" restic backup -q -r "${backup_repo_path}" --tag "${DUMP_NAME} ${BACKUP_ADDON_COMMIT_ID} ${BACKUP_TYPE}" "${APP_PATH}" ~/wp_db_backup.sql | tee -a "${BACKUP_LOG_FILE}"; then
echo "Backup snapshot creation failed." | tee -a "${BACKUP_LOG_FILE}"
exit 1
fi
echo "$(date) ${ENV_NAME} End uploading the ${DUMP_NAME} snapshot to backup storage" | tee -a "${BACKUP_LOG_FILE}"
}
2024-03-13 18:03:04 +00:00
function backup_database() {
local backup_repo_path="/mnt/backups/${ENV_NAME}/db"
local log_file="/home/litespeed/mbmanager/logs/${ENV_NAME}_db_backup.log"
# Ensure the backup and log directories exist
2024-03-13 17:45:48 +00:00
mkdir -p "${backup_repo_path}"
2024-03-13 18:03:04 +00:00
mkdir -p "$(dirname "${log_file}")"
2024-03-13 17:45:48 +00:00
2024-03-13 18:03:04 +00:00
echo "$(date) ${ENV_NAME} Starting database backup..." | tee -a "${log_file}"
2024-03-13 17:45:48 +00:00
2024-03-13 18:03:04 +00:00
# Extract database credentials from wp-config.php
2024-03-13 17:45:48 +00:00
DB_NAME=$(grep DB_NAME /var/www/webroot/ROOT/wp-config.php | cut -d "'" -f 4)
DB_USER=$(grep DB_USER /var/www/webroot/ROOT/wp-config.php | cut -d "'" -f 4)
DB_PASSWORD=$(grep DB_PASSWORD /var/www/webroot/ROOT/wp-config.php | cut -d "'" -f 4)
DB_HOST=$(grep DB_HOST /var/www/webroot/ROOT/wp-config.php | cut -d "'" -f 4 | awk -F':' '{print $1}')
2024-03-13 18:03:04 +00:00
DB_PORT=$(grep DB_HOST /var/www/webroot/ROOT/wp-config.php | cut -d "'" -f 4 | awk -F':' '{print $2}' | sed 's/)//')
2024-03-13 17:45:48 +00:00
DB_PORT=${DB_PORT:-3306}
2024-03-13 13:25:45 +00:00
2024-03-13 18:03:04 +00:00
# Determine the correct --column-statistics option based on MySQL or MariaDB version
local column_statistics_option=""
SERVER_VERSION=$(mysql -h "${DB_HOST}" -u "${DB_USER}" -p"${DB_PASSWORD}" -e 'SELECT VERSION();' -s -N)
if [[ "${SERVER_VERSION}" =~ ^5\.7|^8\.0|MariaDB ]]; then
column_statistics_option="--column-statistics=0"
fi
# Create a database dump
local dump_file="${backup_repo_path}/${ENV_NAME}_$(date "+%Y-%m-%d_%H-%M-%S").sql"
mysqldump -h "${DB_HOST}" -P "${DB_PORT}" -u "${DB_USER}" -p"${DB_PASSWORD}" ${column_statistics_option} --force --single-transaction --quote-names --opt "${DB_NAME}" > "${dump_file}"
2024-03-13 17:45:48 +00:00
if [ $? -ne 0 ]; then
2024-03-13 18:03:04 +00:00
echo "$(date) ${ENV_NAME} Database backup process failed." | tee -a "${log_file}"
2024-03-13 13:25:45 +00:00
exit 1
fi
2024-03-13 18:03:04 +00:00
# Backup the dump file using Restic
restic -r "${backup_repo_path}" backup "${dump_file}" --tag "db" --host "${ENV_NAME}"
2024-03-13 17:45:48 +00:00
if [ $? -ne 0 ]; then
2024-03-13 18:03:04 +00:00
echo "$(date) ${ENV_NAME} Restic backup process failed." | tee -a "${log_file}"
2024-03-13 13:25:45 +00:00
exit 1
fi
2024-03-13 18:03:04 +00:00
echo "$(date) ${ENV_NAME} Database backup completed successfully." | tee -a "${log_file}"
# Optionally, remove the dump file after successful backup
rm -f "${dump_file}"
2024-03-13 13:25:45 +00:00
}
function backup_wp_core() {
echo "Starting backup of WordPress core files excluding uploads directory..."
# Define the path to the WordPress installation and the backup tag
local wp_path="$APP_PATH"
local backup_tag="wp-core-$(date "+%F_%H%M%S_%Z")"
# Exclude the uploads directory using the --exclude option
GOGC=20 RESTIC_PASSWORD="$RESTIC_PASSWORD" restic -r "$backupPath" backup \
--tag "$backup_tag" \
--exclude="$wp_path/wp-content/uploads" \
"$wp_path"
echo "WordPress core files backup completed."
}
function backup_uploads() {
echo "Starting backup of WordPress uploads directory..."
# Define the path to the uploads directory and the backup tag
local uploads_path="$APP_PATH/wp-content/uploads"
local backup_tag="wp-uploads-$(date "+%F_%H%M%S_%Z")"
# Perform the backup
GOGC=20 RESTIC_PASSWORD="$RESTIC_PASSWORD" restic -r "$backupPath" backup \
--tag "$backup_tag" \
"$uploads_path"
echo "WordPress uploads directory backup completed."
}
function backup_database() {
echo "Starting backup of WordPress database..."
# Define the backup tag and the path for the temporary SQL dump
local backup_tag="wp-db-$(date "+%F_%H%M%S_%Z")"
local sql_dump_path="/tmp/wp_db_backup.sql"
# Create a database dump
mysqldump -h "$DB_HOST" -u "$DB_USER" -p"$DB_PASSWORD" "$DB_NAME" > "$sql_dump_path"
# Perform the backup
GOGC=20 RESTIC_PASSWORD="$RESTIC_PASSWORD" restic -r "$backupPath" backup \
--tag "$backup_tag" \
"$sql_dump_path"
# Remove the temporary SQL dump file
rm -f "$sql_dump_path"
echo "WordPress database backup completed."
}
case "$1" in
backup)
backup_wp_core
backup_uploads
backup_database
;;
backup_wp_core)
backup_wp_core
;;
backup_uploads)
backup_uploads
;;
backup_database)
backup_database
;;
check_backup_repo)
check_backup_repo
;;
rotate_snapshots)
rotate_snapshots
;;
create_snapshot)
create_snapshot
;;
update_restic)
update_restic
;;
*)
echo "Usage: $0 {backup|backup_wp_core|backup_uploads|backup_database|check_backup_repo|rotate_snapshots|create_snapshot|update_restic}"
exit 2
esac
exit $?