Last active
January 11, 2020 22:53
-
-
Save jamchamb/7c404a992f01142bf293 to your computer and use it in GitHub Desktop.
Phabricator automatic backup script
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# Backup Phabricator database, storage engine, and config files | |
# NOTE: This script is to be run by cron. See /etc/cron.d/phabricator | |
# Don't use unset variables | |
set -u | |
# Exit if any commands fail (unless they begin a pipe, etc.) | |
set -e | |
# Output an error message and set the errors flag | |
function error() { | |
echo "ERROR: $1" | |
errors=true | |
} | |
# Directory paths | |
# Note that the backup, temporary, and storage directories should be on the same filesystem to ensure atomic operations. | |
BACKUP_DIR="/nfsmount/phabricator/backups" | |
TMP_ROOT_DIR="/nfsmount/phabricator/tmp" | |
STORAGE_DIR="/nfsmount/phabricator/storage" | |
INSTALL_DIR="/opt/phabricator" | |
# Binary paths | |
DATE="/bin/date" | |
MV="/bin/mv" | |
MKDIR="/bin/mkdir" | |
GZIP="/bin/gzip" | |
RM="/bin/rm" | |
TAR="/bin/tar" | |
MKTEMP="/bin/mktemp" | |
DIRNAME="/usr/bin/dirname" | |
BASENAME="/bin/basename" | |
STORAGE="$INSTALL_DIR/bin/storage" | |
##################################### | |
# VALIDATE BINARIES AND DIRECTORIES # | |
##################################### | |
errors=false | |
# Validate binaries | |
for binvar in "DATE" "MV" "MKDIR" "GZIP" "RM" "TAR" "MKTEMP" "DIRNAME" "BASENAME" "STORAGE"; do | |
eval binpath="\$$binvar" | |
if [ ! -f "$binpath" ]; then | |
error "Bad path for $binvar: \"$binpath\" is not a file" | |
elif [ ! -x "$binpath" ]; then | |
error "Don't have execute permissions for $binvar binary at \"$binpath\'" | |
fi | |
done | |
# Make sure directories exist and are readable | |
for dirvar in "BACKUP_DIR" "STORAGE_DIR" "INSTALL_DIR" "TMP_ROOT_DIR"; do | |
eval dirpath="\$$dirvar" | |
if [ ! -d "$dirpath" ]; then | |
error "$dirvar path is invalid: \"$dirpath\" is not a directory" | |
elif [ ! -x "$dirpath" ] || [ ! -r "$dirpath" ]; then | |
error "Don't have read and/or execute permissions for $dirvar path at \"$dirpath\"" | |
fi | |
done | |
# Check for write permissions where necessary | |
for dirvar in "BACKUP_DIR" "TMP_ROOT_DIR"; do | |
eval dirpath="\$$dirvar" | |
if [ ! -w "$dirpath" ]; then | |
error "Don't have write permissions for $dirvar path at \"$dirpath\"" | |
fi | |
done | |
# Exit if there were environment problems | |
$errors && exit 1 | |
# Create the backup directories if they don't exist | |
for i in {0..4}; do | |
cur_dir="$BACKUP_DIR/backup.$i" | |
if [ -d "$cur_dir" ] && [ ! -L "$cur_dir" ]; then | |
# Backup directory exists, check permissions | |
if [ ! -r "$cur_dir" ] || [ ! -w "$cur_dir" ] || [ ! -x "$cur_dir" ]; then | |
error "Backup directory \"$cur_dir\" exists but is not readable, writable, and executable" | |
fi | |
elif [ -e "$cur_dir" ]; then | |
error "Backup directory path \"$cur_dir\" already in use by a symlink or file" | |
else | |
$MKDIR -- "$cur_dir" | |
fi | |
done | |
# Exit if backup directories couldn't be set up | |
$errors && exit 2 | |
############################## | |
# PERFORM PHABRICATOR BACKUP # | |
############################## | |
# Create the SQL dump filename | |
timestamp=$($DATE +'%Y%m%dT%H%M%S%z') | |
sql_file="phabricator-db-$timestamp.sql" | |
# Make temporary directory for new backup files | |
cur_tmp_dir=$($MKTEMP -d --tmpdir="$TMP_ROOT_DIR" "phabbackup.TEMP.XXXXXXXX") | |
# Delete the temporary directory if script gets killed here | |
trap "error 'Phabricator backup interrupted. Cleaning up temporary directory \"$cur_tmp_dir\"'; \"$RM\" -rf -- $cur_tmp_dir; exit" INT TERM EXIT | |
# Dump all the MySQL databases | |
$STORAGE dump > "$cur_tmp_dir/$sql_file" | |
if [ ! -s "$cur_tmp_dir/$sql_file" ]; then | |
error "Phabricator database dump is empty" | |
exit 3 | |
fi | |
$GZIP -- "$cur_tmp_dir/$sql_file" | |
# Create the local storage engine backup. | |
# The -C option prevents cron from emailing us "Removing leading `/' from member names" | |
# every time the backup runs. | |
$TAR -zcf "$cur_tmp_dir/phabricator-storage-$timestamp.tar.gz" -C "$($DIRNAME "$STORAGE_DIR")" -- "$($BASENAME "$STORAGE_DIR")" | |
# Copy the configuration JSON | |
$GZIP -c -- "$INSTALL_DIR/conf/local/local.json" > "$cur_tmp_dir/phabricator-local-$timestamp.json.gz" | |
# Finished creating a consistent set of backup files. | |
# Now we change the trap to indicate backup directory rotation failed. | |
trap "error 'Backup directory rotation failed. Please check \"$TMP_ROOT_DIR\" and \"$BACKUP_DIR\"'; exit" INT TERM EXIT | |
# Set aside oldest backup for deletion if new backup is successful | |
old_backup_dir=$($MKTEMP -d --tmpdir="$TMP_ROOT_DIR" "phabbackup.OLD.XXXXXXXX") | |
$MV -- "$BACKUP_DIR/backup.4/" "$old_backup_dir" | |
# Go from oldest to newest backup, changing the directory names by 1 | |
for i in {4..1}; do | |
$MV -- "$BACKUP_DIR/backup.$[${i}-1]" "$BACKUP_DIR/backup.${i}" | |
done | |
# Move the new files to the new backup directory | |
$MV -- "$cur_tmp_dir" "$BACKUP_DIR/backup.0" | |
# Finally delete oldest backup | |
$RM -rf -- "$old_backup_dir" | |
# Disable trap | |
trap - INT TERM EXIT |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Nice shell code. But it looks incomplete to me. Did you ever need to do a restore?
What is missing: These are where you resolve the phabricator "setup issues" after a fresh install
Your web server setup, e.g.
The repositories, if you have them.
And I am not sure that your mysql dump also includes the phabricator database user.