From 0578729b92e2e6032aad39c78430f9b194ce0068 Mon Sep 17 00:00:00 2001 From: BallsOfSpaghetti <115404710+OpenSpaghettiSauce@users.noreply.github.com> Date: Wed, 2 Nov 2022 19:43:39 +0100 Subject: [PATCH 1/5] Add support for NODATE env This adds a THREADS like env var and it makes the script skip the creation of the ${DATE} folder. --- helper-scripts/backup_and_restore.sh | 29 ++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/helper-scripts/backup_and_restore.sh b/helper-scripts/backup_and_restore.sh index 1853f501..29fa241a 100755 --- a/helper-scripts/backup_and_restore.sh +++ b/helper-scripts/backup_and_restore.sh @@ -93,9 +93,18 @@ fi function backup() { DATE=$(date +"%Y-%m-%d-%H-%M-%S") - mkdir -p "${BACKUP_LOCATION}/mailcow-${DATE}" - chmod 755 "${BACKUP_LOCATION}/mailcow-${DATE}" - cp "${SCRIPT_DIR}/../mailcow.conf" "${BACKUP_LOCATION}/mailcow-${DATE}" + + if [[ -z "${NODATE}" ]]; then + # No NODATE defined so we backup normally + FINAL_BACKUP_LOCATION="${BACKUP_LOCATION}/mailcow-${DATE}" + else + # NODATE so we will not timestamp a backup folder + FINAL_BACKUP_LOCATION="${BACKUP_LOCATION}/" + fi + + mkdir -p "${FINAL_BACKUP_LOCATION}" + chmod 755 "${FINAL_BACKUP_LOCATION}" + cp "${SCRIPT_DIR}/../mailcow.conf" "${FINAL_BACKUP_LOCATION}" for bin in docker; do if [[ -z $(which ${bin}) ]]; then >&2 echo -e "\e[31mCannot find ${bin} in local PATH, exiting...\e[0m" @@ -106,32 +115,32 @@ function backup() { case "$1" in vmail|all) docker run --name mailcow-backup --rm \ - -v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup:z \ + -v ${FINAL_BACKUP_LOCATION}:/backup:z \ -v $(docker volume ls -qf name=^${CMPS_PRJ}_vmail-vol-1$):/vmail:ro,z \ ${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="pigz --rsyncable -p ${THREADS}" -Pcvpf /backup/backup_vmail.tar.gz /vmail ;;& crypt|all) docker run --name mailcow-backup --rm \ - -v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup:z \ + -v ${FINAL_BACKUP_LOCATION}:/backup:z \ -v $(docker volume ls -qf name=^${CMPS_PRJ}_crypt-vol-1$):/crypt:ro,z \ ${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="pigz --rsyncable -p ${THREADS}" -Pcvpf /backup/backup_crypt.tar.gz /crypt ;;& redis|all) docker exec $(docker ps -qf name=redis-mailcow) redis-cli save docker run --name mailcow-backup --rm \ - -v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup:z \ + -v ${FINAL_BACKUP_LOCATION}:/backup:z \ -v $(docker volume ls -qf name=^${CMPS_PRJ}_redis-vol-1$):/redis:ro,z \ ${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="pigz --rsyncable -p ${THREADS}" -Pcvpf /backup/backup_redis.tar.gz /redis ;;& rspamd|all) docker run --name mailcow-backup --rm \ - -v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup:z \ + -v ${FINAL_BACKUP_LOCATION}:/backup:z \ -v $(docker volume ls -qf name=^${CMPS_PRJ}_rspamd-vol-1$):/rspamd:ro,z \ ${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="pigz --rsyncable -p ${THREADS}" -Pcvpf /backup/backup_rspamd.tar.gz /rspamd ;;& postfix|all) docker run --name mailcow-backup --rm \ - -v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup:z \ + -v ${FINAL_BACKUP_LOCATION}:/backup:z \ -v $(docker volume ls -qf name=^${CMPS_PRJ}_postfix-vol-1$):/postfix:ro,z \ ${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="pigz --rsyncable -p ${THREADS}" -Pcvpf /backup/backup_postfix.tar.gz /postfix ;;& @@ -148,7 +157,7 @@ function backup() { -v $(docker volume ls -qf name=^${CMPS_PRJ}_mysql-vol-1$):/var/lib/mysql/:ro,z \ -t --entrypoint= \ --sysctl net.ipv6.conf.all.disable_ipv6=1 \ - -v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup:z \ + -v ${FINAL_BACKUP_LOCATION}:/backup:z \ ${SQLIMAGE} /bin/sh -c "mariabackup --host mysql --user root --password ${DBROOT} --backup --rsync --target-dir=/backup_mariadb ; \ mariabackup --prepare --target-dir=/backup_mariadb ; \ chown -R 999:999 /backup_mariadb ; \ @@ -380,4 +389,4 @@ elif [[ ${1} == "restore" ]]; then done echo "Restoring ${FILE_SELECTION[${input_sel}]} from ${RESTORE_POINT}..." restore "${RESTORE_POINT}" ${FILE_SELECTION[${input_sel}]} -fi \ No newline at end of file +fi From 567ff84a25c516c2737b5aea491268baa22070c0 Mon Sep 17 00:00:00 2001 From: BallsOfSpaghetti <115404710+OpenSpaghettiSauce@users.noreply.github.com> Date: Wed, 2 Nov 2022 19:46:57 +0100 Subject: [PATCH 2/5] Introduce 'help' This adds a "help" text, so the user knows their options. Calling just the script will now tell the user to either user 'backup', 'restore', or 'help'. --- helper-scripts/backup_and_restore.sh | 46 +++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/helper-scripts/backup_and_restore.sh b/helper-scripts/backup_and_restore.sh index 29fa241a..3b0d9a31 100755 --- a/helper-scripts/backup_and_restore.sh +++ b/helper-scripts/backup_and_restore.sh @@ -2,12 +2,50 @@ DEBIAN_DOCKER_IMAGE="mailcow/backup:1.0" +if [[ ${1} == "help" ]]; then + cat << END_OF_HELP + Script usage: + + - Specify 'backup' or 'restore' mode: + backup_and_restore.sh backup + backup_and_restore.sh restore + + - If you 'backup', you also have to specify what to backup. + Use 'backup' to see the backup options you can use. + + backup_and_restore.sh backup + + The script also comes with support for environment variables. + + Available environment variables: + THREADS: Allows you to specify how many threads to backup with. + example: THREADS=4 backup_and_restore.sh backup all + + NODATE: Allows you to omit the date stamped folder. + example: NODATE=1 backup_and_restore.sh backup all + + You can combine the variables as well. + example: THREADS=4 NODATE=1 backup_and_restore.sh backup all + + To do a full backup with the script, in an example "/backup/" folder: + echo "/backup/" | backup_and_restore.sh backup all + + To do a full backup with 4 threads: + echo "/backup/" | THREADS=4 NODATE=1 backup_and_restore.sh backup all + + To do a full backup with all available threads: + echo "/backup/" | THREADS=\`getconf _NPROCESSORS_ONLN\` NODATE=1 backup_and_restore.sh backup all + +END_OF_HELP + exit 0 +fi + if [[ ! -z ${MAILCOW_BACKUP_LOCATION} ]]; then BACKUP_LOCATION="${MAILCOW_BACKUP_LOCATION}" fi -if [[ ! ${1} =~ (backup|restore) ]]; then - echo "First parameter needs to be 'backup' or 'restore'" +if [[ ! ${1} =~ (backup|restore|help) ]]; then + echo "First parameter needs to be 'backup' or 'restore' or 'help'" exit 1 fi @@ -58,7 +96,7 @@ if ! [[ "${THREADS}" =~ ^[1-9]+$ ]] ; then echo "Thread input is not a number!" exit 1 elif [[ "${THREADS}" =~ ^[1-9]+$ ]] ; then - echo "Using ${THREADS} Thread(s) for this run." + echo "Using ${THREADS} Thread(s) for this run." echo "Notice: You can set the Thread count with the THREADS Variable before you run this script." fi @@ -190,7 +228,7 @@ function restore() { elif [ "${DOCKER_COMPOSE_VERSION}" == "standalone" ]; then COMPOSE_COMMAND="docker-compose" - + else echo -e "\e[31mCan not read DOCKER_COMPOSE_VERSION variable from mailcow.conf! Is your mailcow up to date? Exiting...\e[0m" exit 1 From d1754bb5a6d1190d1f26c105ed20480939d88117 Mon Sep 17 00:00:00 2001 From: BallsOfSpaghetti <115404710+OpenSpaghettiSauce@users.noreply.github.com> Date: Sat, 26 Nov 2022 13:52:42 +0100 Subject: [PATCH 3/5] Fix restore for NODATE I tested it in Virtualbox with my own live server backups and it seemed to restore fine like this. I'm not sure if the help/explanation is enough, feel free to add more, change wording, etc. --- helper-scripts/backup_and_restore.sh | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/helper-scripts/backup_and_restore.sh b/helper-scripts/backup_and_restore.sh index 3b0d9a31..6d81fc49 100755 --- a/helper-scripts/backup_and_restore.sh +++ b/helper-scripts/backup_and_restore.sh @@ -23,6 +23,10 @@ if [[ ${1} == "help" ]]; then NODATE: Allows you to omit the date stamped folder. example: NODATE=1 backup_and_restore.sh backup all + + IMPORTANT: If you'd like to restore a NODATE backup, you have to + use NODATE=1 for restoring as well! Otherwise, Mailcow will only + look for timestamped folders, which you will not have. You can combine the variables as well. example: THREADS=4 NODATE=1 backup_and_restore.sh backup all @@ -367,10 +371,20 @@ if [[ ${1} == "backup" ]]; then elif [[ ${1} == "restore" ]]; then i=1 declare -A FOLDER_SELECTION - if [[ $(find ${BACKUP_LOCATION}/mailcow-* -maxdepth 1 -type d 2> /dev/null| wc -l) -lt 1 ]]; then - echo "Selected backup location has no subfolders" - exit 1 + + if [[ -z "${NODATE}" ]]; then + # No NODATE defined so restore normally + if [[ $(find ${BACKUP_LOCATION}/mailcow-* -maxdepth 1 -type d 2> /dev/null| wc -l) -lt 1 ]]; then + echo "Selected backup location has no timestamped subfolders" + exit 1 + fi + else + if [[ $(find ${BACKUP_LOCATION}/* -maxdepth 1 -type d 2> /dev/null| wc -l) -lt 1 ]]; then + echo "Selected backup location has no subfolders" + exit 1 + fi fi + for folder in $(ls -d ${BACKUP_LOCATION}/mailcow-*/); do echo "[ ${i} ] - ${folder}" FOLDER_SELECTION[${i}]="${folder}" From 3442520e2efc0f0ff618bd68a0fb6ab1f8cb55a5 Mon Sep 17 00:00:00 2001 From: BallsOfSpaghetti <115404710+OpenSpaghettiSauce@users.noreply.github.com> Date: Thu, 1 Dec 2022 13:00:53 +0100 Subject: [PATCH 4/5] Fixed restore with NODATE Apparently I changed the problematic for loop on the VM, but I forgot to copy the script back to the git folder before committing. I put the for loop into the IF branches now. TESTING IS UNDERWAY - I will comment again once it is 100% verified working (for me, again, sorry.) --- helper-scripts/backup_and_restore.sh | 31 +++++++++++++++++----------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/helper-scripts/backup_and_restore.sh b/helper-scripts/backup_and_restore.sh index 6d81fc49..29b8857e 100755 --- a/helper-scripts/backup_and_restore.sh +++ b/helper-scripts/backup_and_restore.sh @@ -23,8 +23,8 @@ if [[ ${1} == "help" ]]; then NODATE: Allows you to omit the date stamped folder. example: NODATE=1 backup_and_restore.sh backup all - - IMPORTANT: If you'd like to restore a NODATE backup, you have to + + IMPORTANT: If you'd like to restore a NODATE=1 backup, you have to use NODATE=1 for restoring as well! Otherwise, Mailcow will only look for timestamped folders, which you will not have. @@ -46,6 +46,7 @@ fi if [[ ! -z ${MAILCOW_BACKUP_LOCATION} ]]; then BACKUP_LOCATION="${MAILCOW_BACKUP_LOCATION}" + echo $BACKUP_LOCATION fi if [[ ! ${1} =~ (backup|restore|help) ]]; then @@ -136,7 +137,7 @@ fi function backup() { DATE=$(date +"%Y-%m-%d-%H-%M-%S") - if [[ -z "${NODATE}" ]]; then + if [[ -z "$NODATE" ]]; then # No NODATE defined so we backup normally FINAL_BACKUP_LOCATION="${BACKUP_LOCATION}/mailcow-${DATE}" else @@ -371,25 +372,31 @@ if [[ ${1} == "backup" ]]; then elif [[ ${1} == "restore" ]]; then i=1 declare -A FOLDER_SELECTION - - if [[ -z "${NODATE}" ]]; then - # No NODATE defined so restore normally + + if [[ -z "$NODATE" ]]; then + # Normal backup mode, check if any timestamped folders exist at backup location if [[ $(find ${BACKUP_LOCATION}/mailcow-* -maxdepth 1 -type d 2> /dev/null| wc -l) -lt 1 ]]; then echo "Selected backup location has no timestamped subfolders" exit 1 fi + for folder in $(ls -d ${BACKUP_LOCATION}/mailcow-*/); do + echo "[ ${i} ] - ${folder}" + FOLDER_SELECTION[${i}]="${folder}" + ((i++)) + done else + # NODATE set, any sub-folder will be shown to user if [[ $(find ${BACKUP_LOCATION}/* -maxdepth 1 -type d 2> /dev/null| wc -l) -lt 1 ]]; then echo "Selected backup location has no subfolders" exit 1 fi + for folder in $(ls -d ${BACKUP_LOCATION}/*/); do + echo "[ ${i} ] - ${folder}" + FOLDER_SELECTION[${i}]="${folder}" + ((i++)) + done fi - - for folder in $(ls -d ${BACKUP_LOCATION}/mailcow-*/); do - echo "[ ${i} ] - ${folder}" - FOLDER_SELECTION[${i}]="${folder}" - ((i++)) - done + echo input_sel=0 while [[ ${input_sel} -lt 1 || ${input_sel} -gt ${i} ]]; do From ef6ea357a1dda3fba726daad723a575a2c6a4f30 Mon Sep 17 00:00:00 2001 From: BallsOfSpaghetti <115404710+OpenSpaghettiSauce@users.noreply.github.com> Date: Fri, 2 Dec 2022 00:33:31 +0100 Subject: [PATCH 5/5] Fix project name spelling mailcow is mailcow. --- helper-scripts/backup_and_restore.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helper-scripts/backup_and_restore.sh b/helper-scripts/backup_and_restore.sh index 29b8857e..bc7b060b 100755 --- a/helper-scripts/backup_and_restore.sh +++ b/helper-scripts/backup_and_restore.sh @@ -25,7 +25,7 @@ if [[ ${1} == "help" ]]; then example: NODATE=1 backup_and_restore.sh backup all IMPORTANT: If you'd like to restore a NODATE=1 backup, you have to - use NODATE=1 for restoring as well! Otherwise, Mailcow will only + use NODATE=1 for restoring as well! Otherwise, mailcow will only look for timestamped folders, which you will not have. You can combine the variables as well.