1
1
Fork 0
scripts/restic-backup.sh

237 lines
6.8 KiB
Bash
Executable File

#!/usr/bin/env bash
## ## ## ## ## ## ## ## ## ## ## ## ## ##
## ##
## restic-backup.sh ##
## ##
## Simple script to help setup and run ##
## periodic Restic backup jobs ##
## ##
## ## ## ## ## ## ## ## ## ## ## ## ## ##
##
## Information
##
NAME="restic-backup.sh"
VERSION="0.2.3"
AUTHOR="david@socialnerds.org"
LICENSE="MIT"
DESCRIPTION="A simple script to help setup and run periodic Restic backup jobs."
WEBSITE="https://git.socialnerds.org/david/scripts"
CHANGELOG=("[2023-10-28][v0.2.3] Fixed a bug when repo is locked, simplified healthchecks code"
"[2023-10-27][v0.2.2] Added secret export option"
"[2023-10-27][v0.2.1] Better Healthchecks integration"
"[2023-10-26][v0.2.0] Complete rewrite"
"[2021-02-10][v0.1.0] Initial version")
##
## Configuration
##
EXECUTABLE="$(basename $0)"
LIBRARIES="lib.sh"
DEPENDENCIES="basename dirname readlink tr head chmod cat curl restic"
REQUIRE_ROOT=0
# Files
PASSWORD="$HOME/.restic-password"
REPOSITORY="$HOME/.restic-repository"
HEALTHCHECKS="$HOME/.restic-healthchecks"
# Build the Restic command
RESTIC_BINARY="$(command -v restic)"
RESTIC_OPTIONS="-q"
RESTIC_COMMAND="$RESTIC_BINARY --password-file $PASSWORD --repository-file $REPOSITORY $RESTIC_OPTIONS"
# How many snapshots should be kept?
SNAPSHOTS=32
##
## Functions
##
# Print help information
function print_help() {
printf "%s\n\n%s\n%b\n\n%s\n %-15s %s\n %-15s %s\n %-15s %s\n %-15s %s\n %-15s %s\n %-15s %s\n" \
"$DESCRIPTION" "Usage:" "$LIB_BOLD$EXECUTABLE <options> <path or Restic keyword>$LIB_CLEAR" "Options:" \
"-c, --checks" "Enable Healthchecks" \
"-s, --secret" "Print secret(s) and exit" \
"-h, --help" "Print help screen and exit" \
"-i, --info" "Print script information and exit" \
"-v, --verbose" "More verbose output" \
"-q, --quiet" "No output except errors (overrides -v)"
}
# Print secret(s) to easily c/p it to your password manager
function print_secret() {
printf "%13s %b\n" "Password:" "$LIB_RED$LIB_RED_BG$(cat $PASSWORD)$LIB_CLEAR"
printf "%13s %b\n" "Repository:" "$LIB_RED$LIB_RED_BG$(cat $REPOSITORY)$LIB_CLEAR"
if [[ -s $HEALTHCHECKS ]]; then
printf "%13s %b\n" "Healthchecks:" "$LIB_RED$LIB_RED_BG$(cat $HEALTHCHECKS)$LIB_CLEAR"
fi
}
##
## Preflight
##
# Load BASH libraries
SCRIPT_PATH=$(readlink -f "$0")
SCRIPT_DIR=$(dirname "$SCRIPT_PATH")
for LIBRARY in $LIBRARIES; do
if [[ -r "$SCRIPT_DIR/$LIBRARY" ]]; then
#echo "Loading library file [$SCRIPT_DIR/$LIBRARY]"
source "$SCRIPT_DIR/$LIBRARY"
else
echo "Error: Cannot load library file [$SCRIPT_DIR/$LIBRARY]"
exit 1
fi
done
# Check for root privileges
if ! lib_amiroot && [[ $REQUIRE_ROOT -eq 1 ]]; then
lib_print "!You need to have root privileges"
exit 1
fi
# Check for dependencies
MISSING_COMMANDS=$(lib_missing_commands $DEPENDENCIES)
if [ -n "$MISSING_COMMANDS" ]; then
lib_print "!One or more commands missing [$MISSING_COMMANDS]"
lib_print "Try installing them with your package manager"
exit 1
fi
##
## Liftoff
##
while [[ "$1" =~ ^- && ! "$1" == "--" ]]; do
case $1 in
-c|--checks)
C=1
;;
-s|--secret)
S=1
;;
-h|--help)
H=1
;;
-i|--info)
I=1
;;
-v|--verbose)
V=1
;;
-q|--quiet)
Q=1
;;
*)
lib_print "!Unknown option [$1]"
lib_print "Try --help or -h for available options"
exit 1
;;
esac
shift
done
if [[ "$1" == '--' ]]; then
shift
fi
if [[ $I -eq 1 ]]; then
lib_print_info
exit 0
elif [[ $H -eq 1 ]]; then
print_help
exit 0
elif [[ $S -eq 1 ]]; then
print_secret
exit 0
fi
if [[ -z "$1" ]]; then
print_help
exit 0
else
# Generate a new password file if it is missing or empty
if [[ ! -s $PASSWORD ]]; then
lib_gen_string 32 > $PASSWORD
chmod 600 $PASSWORD
lib_print "New password file generated since it did not exist or was empty [$PASSWORD]"
fi
# Generate a new repo file if it is missing or empty
if [[ ! -s $REPOSITORY ]]; then
echo -n "Repository URL: "; read -r INPUT
echo $INPUT > $REPOSITORY
chmod 600 $REPOSITORY
lib_print "New repository file generated since it did not exist or was empty [$REPOSITORY]"
fi
# Generate a new healthchecks file if it is missing or empty
if [[ ! -s $HEALTHCHECKS ]] && [[ $C -eq 1 ]]; then
echo -n "Healthchecks URL: "; read -r INPUT
if [[ -n "$INPUT" ]]; then
echo "$INPUT" > $HEALTHCHECKS
chmod 600 $HEALTHCHECKS
lib_print "New healthchecks file generated [$HEALTHCHECKS]"
fi
fi
if [[ "$1" =~ ^/ && -r "$1" ]]; then
# Initialize a new repo if the URL has no config file
if ! $($RESTIC_COMMAND snapshots >/dev/null 2>&1); then
if ! $($RESTIC_COMMAND init >/dev/null 2>&1); then
lib_print "!A problem occured while initializing a new repository"
exit 1
fi
lib_print "New repository initialized"
fi
# Run the Restic repository check
if ! $($RESTIC_COMMAND check >/dev/null 2>&1); then
lib_print "!Repository check failed"
exit 1
fi
# Signal backup start to Healthchecks if enabled
[[ "$C" -eq 1 ]] && lib_healthchecks "$(cat $HEALTHCHECKS)/start" "Commencing backup [$1]"
# Run the actual backup
if ! $($RESTIC_COMMAND backup --one-file-system ${@:2} "$1" >/dev/null 2>&1); then
lib_print "!Something went wrong while running backup [$1]"
# Signal fail to Healthchecks if enabled
[[ "$C" -eq 1 ]] && lib_healthchecks "$(cat $HEALTHCHECKS)/fail" "Something went wrong while running backup [$1]"
exit 1
fi
# Delete old snapshots
if ! $($RESTIC_COMMAND forget --keep-last $SNAPSHOTS --path "$1" --prune >/dev/null 2>&1); then
lib_print "!Something went wrong while deleting old snapshots [$1]"
# Signal fail to Healthchecks if enabled
[[ "$C" -eq 1 ]] && lib_healthchecks "$(cat $HEALTHCHECKS)/fail" "Something went wrong while deleting old snapshots [$1]"
exit 1
fi
# Signal success to Healthchecks if enabled
[[ "$C" -eq 1 ]] && lib_healthchecks "$(cat $HEALTHCHECKS)" "Backup successfully done [$1]"
elif [[ "$1" =~ ^(backup|cache|cat|check|copy|diff|dump|find|forget|generate|help|init|key|list|ls|migrate|mount|prune|recover|repair|restore|rewrite|snapshots|stats|tag|unlock|version)$ ]]; then
# Wrapper mode
RESTIC_COMMAND="$RESTIC_BINARY --password-file $PASSWORD --repository-file $REPOSITORY"
$RESTIC_COMMAND $@
else
lib_print "!The given path must be absolute and readable [$1]"
lib_print "Or you can try a Restic keyword directly for wrapper mode"
exit 1
fi
fi
##
## Here be dragons
##
exit 0