15 Commits

Author SHA1 Message Date
Asif Bacchus a26bb36f17 define containerName once 2019-09-10 17:22:43 -06:00
Asif Bacchus bbbdf38dcc change mailcowConfigFilePath to explicit param 2019-09-10 16:58:30 -06:00
Asif Bacchus 43f1bb0021 update gitignore, add gitattributes 2019-09-10 16:34:53 -06:00
Asif Bacchus b371e18ab0 'containerName' instead of 'rname' for consistency 2019-09-10 15:23:45 -06:00
Asif Bacchus fb8091ecce Merge pull request #1 from hockeymikey/master
Ensure non-exactly named services are found
2019-09-09 16:45:55 -06:00
hockeymikey 25c6b407a6 Ensure non-exactly named services are found
My naming scheme for my mailcown was like -mailcow_1_k8bdj9op so the script never found them. This ensures they do.
2019-09-07 12:33:54 -05:00
Asif Bacchus 3ddc2c5d4a quoted path-related vars to prevent issues with spaces in path 2019-01-30 12:27:54 -07:00
Asif Bacchus 82f80d0f83 made explanations clearer regarding remote server and keys 2019-01-30 12:22:02 -07:00
Asif Bacchus 0597127cbf clear up confusion between ssh and borg key passwords 2019-01-30 12:19:24 -07:00
Asif Bacchus e23feddb1b added comment for stamp function 2018-10-28 05:25:59 -06:00
Asif Bacchus 12bc49a59e replaced all ${stamp} with [$(stamp)] 2018-10-28 05:12:50 -06:00
Asif Bacchus a14d0ba4a5 removed fixed 'stamp' var and replaced it with a function so it updates 2018-10-28 05:10:58 -06:00
Asif Bacchus ed9db22534 execute docker-compose exec ops w/o TTY (-T) 2018-10-25 13:47:37 -06:00
Asif Bacchus 7e92495c94 Check exitcode from within redis container after cli-save 2018-10-25 13:26:22 -06:00
Asif Bacchus 0808888030 Check dump exitcode from within mysql container 2018-10-25 13:18:31 -06:00
6 changed files with 352 additions and 190 deletions
+75
View File
@@ -0,0 +1,75 @@
# Common settings that generally should always be used with your language specific settings
# Auto detect text files and perform LF normalization
# https://www.davidlaing.com/2012/09/19/customise-your-gitattributes-to-become-a-git-ninja/
* text=auto
#
# The above will handle all files NOT found below
#
# Documents
*.bibtex text diff=bibtex
*.doc diff=astextplain
*.DOC diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot diff=astextplain
*.DOT diff=astextplain
*.pdf diff=astextplain
*.PDF diff=astextplain
*.rtf diff=astextplain
*.RTF diff=astextplain
*.md text
*.tex text diff=tex
*.adoc text
*.textile text
*.mustache text
*.csv text
*.tab text
*.tsv text
*.txt text
*.sql text
# Graphics
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.tif binary
*.tiff binary
*.ico binary
# SVG treated as an asset (binary) by default.
*.svg text
# If you want to treat it as binary,
# use the following line instead.
# *.svg binary
*.eps binary
# Scripts
*.bash text eol=lf
*.sh text eol=lf
# These are explicitly windows files and should use crlf
*.bat text eol=crlf
*.cmd text eol=crlf
*.ps1 text eol=crlf
# Serialisation
*.json text
*.toml text
*.xml text
*.yaml text
*.yml text
# Archives
*.7z binary
*.gz binary
*.tar binary
*.zip binary
#
# Exclude files from exporting
#
.gitattributes export-ignore
.gitignore export-ignore
+6 -1
View File
@@ -1,5 +1,10 @@
# ignore all vscode config files # ignore all vscode config files
.vscode/ .vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/numbered-bookmarks.json
# ignore all generated logfiles # ignore all generated logfiles
*.log *.log
+3
View File
@@ -0,0 +1,3 @@
{
"bookmarks": []
}
+67 -14
View File
@@ -26,18 +26,22 @@ This script automates the following tasks:
- [Why this script must be run as root](#why-this-script-must-be-run-as-root) - [Why this script must be run as root](#why-this-script-must-be-run-as-root)
- [Script parameters](#script-parameters) - [Script parameters](#script-parameters)
- [Optional parameters](#optional-parameters) - [Optional parameters](#optional-parameters)
- [Docker container STOP timeout before error: -1 _number_](#docker-container-stop-timeout-before-error--1-_number_) - [Docker container STOP timeout before error: -1 _number_](#docker-container-stop-timeout-before-error--1-number)
- [Docker container START timeout before error: -2 _number_](#docker-container-start-timeout-before-error--2-_number_) - [Docker container START timeout before error: -2 _number_](#docker-container-start-timeout-before-error--2-number)
- [Path to 503 error page: -5 _/path/to/filename.html_](#path-to-503-error-page--5-_pathtofilenamehtml_) - [Path to 503 error page: -5 _/path/to/filename.html_](#path-to-503-error-page--5-pathtofilenamehtml)
- [Path to borg details file: -b _/path/to/filename.file_](#path-to-borg-details-file--b-_pathtofilenamefile_) - [Path to borg details file: -b _/path/to/filename.file_](#path-to-borg-details-file--b-pathtofilenamefile)
- [File name of docker-compose configuration file: -d _filename.file_](#file-name-of-docker-compose-configuration-file--d-_filenamefile_) - [File name of docker-compose configuration file: -d _filename.file_](#file-name-of-docker-compose-configuration-file--d-filenamefile)
- [Log file location: -l _/path/to/filename.file_](#log-file-location--l-_pathtofilenamefile_) - [Log file location: -l _/path/to/filename.file_](#log-file-location--l-pathtofilenamefile)
- [File name of Mailcow master configuration file: -m _filename.file_](#file-name-of-mailcow-master-configuration-file--m-_filenamefile_) - [File name of Mailcow master configuration file: -m _filename.file_](#file-name-of-mailcow-master-configuration-file--m-filenamefile)
- [Verbose output from borg: -v (no arguments)](#verbose-output-from-borg--v-no-arguments) - [Verbose output from borg: -v (no arguments)](#verbose-output-from-borg--v-no-arguments)
- [Path to webroot: -w _/path/to/webroot/_](#path-to-webroot--w-_pathtowebroot_) - [Path to webroot: -w _/path/to/webroot/_](#path-to-webroot--w-pathtowebroot)
- [Borg details file](#borg-details-file) - [Borg details file](#borg-details-file)
- [Protect your borg details file](#protect-your-borg-details-file) - [Protect your borg details file](#protect-your-borg-details-file)
- [borg specific entries (lines 1-4)](#borg-specific-entries-lines-1-4) - [borg specific entries (lines 1-4)](#borg-specific-entries-lines-1-4)
- [Line 1: Path to borg base directory](#line-1-path-to-borg-base-directory)
- [Line 2: Path to SSH key for remote server](#line-2-path-to-ssh-key-for-remote-server)
- [Line 3: Connection string to remote repo](#line-3-connection-string-to-remote-repo)
- [Line 4: Password for borg repo/repo key](#line-4-password-for-borg-reporepo-key)
- [additional files/directories to backup](#additional-filesdirectories-to-backup) - [additional files/directories to backup](#additional-filesdirectories-to-backup)
- [exclusion patterns](#exclusion-patterns) - [exclusion patterns](#exclusion-patterns)
- [prune timeframe options](#prune-timeframe-options) - [prune timeframe options](#prune-timeframe-options)
@@ -209,9 +213,9 @@ example entries. The file must have the following information in the following
order: order:
1. path to borg base directory **(required)** 1. path to borg base directory **(required)**
2. path to ssh private key for repo **(required)** 2. path to ssh private key for remote server **(required)**
3. connection string to remote repo **(required)** 3. connection string to remote repo **(required)**
4. password for ssh key/repo **(required)** 4. password for borg repo/repo key **(required)**
5. path to file listing additional files/directories to backup 5. path to file listing additional files/directories to backup
6. path to file containing borg-specific exclusion patterns 6. path to file containing borg-specific exclusion patterns
7. prune timeframe options 7. prune timeframe options
@@ -234,10 +238,59 @@ chmod 600 mc_borg.details # grant access to root user only (read/write)
If you need help with these options, then you should consult the borg If you need help with these options, then you should consult the borg
documentation or search my blog at documentation or search my blog at
[https://mytechiethoughts.com](https://mytechiethoughts.com) for borg. This is [https://mytechiethoughts.com](https://mytechiethoughts.com) for borg. Here's a
especially true if you want to understand why an SSH key and passphrase are very brief overview:
preferred and why just a passphrase on it's own presents problems automating
borg backups. #### Line 1: Path to borg base directory
This is primary directory on your local system where your borg configuration is
located, **NOT* the path to your borg binary. The base directory contains the
borg configuration, cache, security files and keys.
#### Line 2: Path to SSH key for remote server
This is the SSH key used to connect to your remote (backup) server where your
borg repo is located. **This is NOT your borg repo key!**
> Please note: If you are planning on executing this script via cron or some
> other form of automation, it is *highly recommended* that you use an SSH key
> **without** a password! SSH is designed such that passwords cannot simply be
> passed to it via environment variables, etc. so this is something not easily
> automated by a script such as this for security reasons. As such, your
> computer will sit and wait for you to enter the password and will NOT execute
> the actual backup portion of the script until the SSH key password is provided.
>
> If you really want/need to use an SSH key password, you will have to look into
> somethign like GNOME keyring or SSH-agent to provide a secure automated way to
> provide that password to SSH and allow this script to continue.
>
> In practice, SSH keys without passwords are still quite safe since the key
> must still be known in order to connect and most keys are quite long. In
> addition, they key only connects to the remote server, your actual information
> within the borg repository is still encrypted and secured with both a key and
> password.
#### Line 3: Connection string to remote repo
This is the full server and path required to connect to your borg repo on the
remote server. Very often it is the in the form of:
```
user@servername.tld:repo-name/
```
for rsync.net it is in the following form:
```
username@server-number.rsync.net:repo-name/
```
#### Line 4: Password for borg repo/repo key
This is the password needed to access and decrypt your *borg repo*. Assuming
you set up your borg repo using recommended practices, this will actually be the
password for your *borg repo private key*. **This is NOT your SSH key
password!**
### additional files/directories to backup ### additional files/directories to backup
Regular → Executable
+166 -140
View File
@@ -12,7 +12,6 @@ lit="\e[93m"
op="\e[39m" op="\e[39m"
info="\e[96m" info="\e[96m"
note="\e[95m" note="\e[95m"
stamp="[`date +%Y-%m-%d` `date +%H:%M:%S`]"
### Functions ### ### Functions ###
@@ -34,10 +33,15 @@ function scriptHelp {
echo -e "\tMailcow to operational status." echo -e "\tMailcow to operational status."
echo -e "\nThe readme file included in this script's git contains detailed" echo -e "\nThe readme file included in this script's git contains detailed"
echo -e "usage information. The following is a brief summary:\n" echo -e "usage information. The following is a brief summary:\n"
echo -e "${bold}***This script scans for your mailcow configuration file" echo -e "${bold}***You MUST provide the full path to your mailcow"
echo -e "either with the default name or with your provided filename. If" echo -e "configuration file using the '-m' parameter***${normal}${default}"
echo -e "multiple files with this name are on your system, the script" echo -e "${bold}${note}\nMandatory parameters:${normal}${default}"
echo -e "WILL get confused and exit with errors***${normal}${default}" echo -e "${lit}\n-m, File name of the Mailcow build configuration file${default}"
echo -e "FULL PATH to your Mailcow master build configuration file containing"
echo -e "all variables and configuration info unique to your Mailcow setup."
echo -e "The path specified here is also used for all docker-related"
echo -e "operations in this script."
echo -e "${info}Default: <none>${default}"
echo -e "${bold}${note}\nOptional parameters:${normal}${default}" echo -e "${bold}${note}\nOptional parameters:${normal}${default}"
echo -e "${lit}\n-1, Timeout for containers to STOP before error${default}" echo -e "${lit}\n-1, Timeout for containers to STOP before error${default}"
echo -e "The number of seconds to wait for a docker container to STOP" echo -e "The number of seconds to wait for a docker container to STOP"
@@ -64,20 +68,12 @@ function scriptHelp {
echo -e "${lit}\n-d, File name of the docker-compose configuration file${default}" echo -e "${lit}\n-d, File name of the docker-compose configuration file${default}"
echo -e "Name of the docker-compose configuration file that Mailcow uses" echo -e "Name of the docker-compose configuration file that Mailcow uses"
echo -e "to build/start/stop all containers. This will only be searched" echo -e "to build/start/stop all containers. This will only be searched"
echo -e "for in the path found to contain your mailcow configuration file." echo -e "for in the path provided for your mailcow configuration file."
echo -e "${info}Default: docker-compose.yml${default}" echo -e "${info}Default: docker-compose.yml${default}"
echo -e "${lit}\n-l, Location to save log file${default}" echo -e "${lit}\n-l, Location to save log file${default}"
echo -e "This script writes a detailed log file of all activities. It is" echo -e "This script writes a detailed log file of all activities. It is"
echo -e "structured in an way easy for log parsers (like Logwatch) to read." echo -e "structured in an way easy for log parsers (like Logwatch) to read."
echo -e "${info}Default: ScriptPath/ScriptName.log${default}" echo -e "${info}Default: ScriptPath/ScriptName.log${default}"
echo -e "${lit}\n-m, File name of the Mailcow build configuration file${default}"
echo -e "Name of the Mailcow master build configuration file that has all"
echo -e "variables and configuration info unique to your Mailcow setup."
echo -e "This script will search for any file matching what you specify"
echo -e "so please ensure you don't have multiple files laying around with"
echo -e "the same name! The path where this file is found is used for all"
echo -e "docker-based operations in this script."
echo -e "${info}Default: mailcow.conf${default}"
echo -e "${lit}\n-v, Verbose output from borgbackup${default}" echo -e "${lit}\n-v, Verbose output from borgbackup${default}"
echo -e "By default, this script will only log summary data from borg." echo -e "By default, this script will only log summary data from borg."
echo -e "If you need/want more detailed information, the verbose setting" echo -e "If you need/want more detailed information, the verbose setting"
@@ -101,6 +97,11 @@ function scriptHelp {
exit 1 exit 1
} }
### Generate dynamic timestamp
function stamp {
echo `date +%F" "%T`
}
### quit -- exit the script after logging any errors, warnings, etc. ### quit -- exit the script after logging any errors, warnings, etc.
function quit { function quit {
# list generated warnings, if any # list generated warnings, if any
@@ -117,7 +118,7 @@ function quit {
fi fi
if [ -z "${exitError}" ]; then if [ -z "${exitError}" ]; then
# exit cleanly # exit cleanly
echo -e "${note}${stamp} -- ${scriptName} completed" \ echo -e "${note}[$(stamp)] -- ${scriptName} completed" \
"--${normal}" >> "$logFile" "--${normal}" >> "$logFile"
exit 0 exit 0
else else
@@ -174,10 +175,10 @@ function cleanup {
checkResult="$?" checkResult="$?"
if [ "$checkResult" = "0" ]; then if [ "$checkResult" = "0" ]; then
# directory still exists # directory still exists
exitWarn+=("${stamp}_111") exitWarn+=("[$(stamp)]_111")
else else
# directory removed # directory removed
echo -e "${op}${stamp} Removed SQL temp directory${normal}" \ echo -e "${op}[$(stamp)] Removed SQL temp directory${normal}" \
>> "$logFile" >> "$logFile"
fi fi
@@ -185,21 +186,21 @@ function cleanup {
# check value of 'clean503' to see if this is necessary (=1) otherwise, skip # check value of 'clean503' to see if this is necessary (=1) otherwise, skip
if [ "$clean503" = "1" ]; then if [ "$clean503" = "1" ]; then
# proceed with cleanup # proceed with cleanup
echo -e "${op}${stamp} Removing 503 error page..." >> "$logFile" echo -e "${op}[$(stamp)] Removing 503 error page..." >> "$logFile"
rm -f "$webroot/$err503File" >> "$logFile" 2>&1 rm -f "$webroot/$err503File" >> "$logFile" 2>&1
# verify file is actually gone # verify file is actually gone
checkExist ff "$webroot/$err503File" checkExist ff "$webroot/$err503File"
checkResult="$?" checkResult="$?"
if [ "$checkResult" = "0" ]; then if [ "$checkResult" = "0" ]; then
# file still exists # file still exists
exitWarn+=("${stamp}_5030") exitWarn+=("[$(stamp)]_5030")
else else
# file removed # file removed
echo -e "${info}${stamp} -- [INFO] 503 page removed from webroot" \ echo -e "${info}[$(stamp)] -- [INFO] 503 page removed from webroot" \
"--${normal}" >> "$logFile" "--${normal}" >> "$logFile"
fi fi
else else
echo -e "${op}${stamp} 503 error page never copied to webroot," \ echo -e "${op}[$(stamp)] 503 error page never copied to webroot," \
"nothing to cleanup" >> "$logFile" "nothing to cleanup" >> "$logFile"
fi fi
@@ -207,42 +208,41 @@ function cleanup {
# start and verify postfix # start and verify postfix
operateDocker start postfix operateDocker start postfix
if [ "$dockerResultState" = "true" ]; then if [ "$dockerResultState" = "true" ]; then
echo -e "${info}${stamp} -- [INFO] Postfix container is running --" \ echo -e "${info}[$(stamp)] -- [INFO] Postfix container is running --" \
"${normal}" >> "$logFile" "${normal}" >> "$logFile"
else else
exitError+=("${stamp}_103") exitError+=("[$(stamp)]_103")
fi fi
# start and verify dovecot # start and verify dovecot
operateDocker start dovecot operateDocker start dovecot
if [ "$dockerResultState" = "true" ]; then if [ "$dockerResultState" = "true" ]; then
echo -e "${info}${stamp} -- [INFO] Dovecot container is running --" \ echo -e "${info}[$(stamp)] -- [INFO] Dovecot container is running --" \
"${normal}" >> "$logFile" "${normal}" >> "$logFile"
else else
exitError+=("${stamp}_104") exitError+=("[$(stamp)]_104")
fi fi
} }
### operate docker containers ### operate docker containers
function operateDocker { function operateDocker {
containerName="$(docker ps --format '{{ .Names }}' --filter name=${COMPOSE_PROJECT_NAME}_${2}-mailcow_1)"
# determine action to take # determine action to take
if [ "$1" = "stop" ]; then if [ "$1" = "stop" ]; then
echo -e "${op}${stamp} Stopping ${2}-mailcow container...${normal}" \ echo -e "${op}[$(stamp)] Stopping ${2}-mailcow container...${normal}" \
>> "$logFile" >> "$logFile"
docker-compose stop --timeout ${dockerStopTimeout} ${2}-mailcow \ docker-compose stop --timeout ${dockerStopTimeout} ${2}-mailcow \
2>> "$logFile" 2>> "$logFile"
# verify container stopped (should return true) # verify container stopped (should return true)
dockerResultState=$(docker inspect -f '{{ .State.Running }}' \ dockerResultState="$(docker inspect -f '{{ .State.Running }}' $containerName)"
${COMPOSE_PROJECT_NAME}_${2}-mailcow_1)
# verify clean stop (exit code 0) # verify clean stop (exit code 0)
dockerResultExit=$(docker inspect -f '{{ .State.ExitCode }}' \ dockerResultExit="$(docker inspect -f '{{ .State.ExitCode }}' $containerName)"
${COMPOSE_PROJECT_NAME}_${2}-mailcow_1)
elif [ "$1" = "start" ]; then elif [ "$1" = "start" ]; then
echo -e "${op}${stamp} Starting ${2}-mailcow container...${normal}" \ echo -e "${op}[$(stamp)] Starting ${2}-mailcow container...${normal}" \
>> "$logFile" >> "$logFile"
docker-compose start ${2}-mailcow 2>> "$logFile" docker-compose start ${2}-mailcow 2>> "$logFile"
# verify # verify
dockerResultState=$(docker inspect -f '{{ .State.Running }}' \ dockerResultState="$(docker inspect -f '{{ .State.Running }}' $containerName)"
${COMPOSE_PROJECT_NAME}_${2}-mailcow_1)
fi fi
} }
@@ -257,9 +257,6 @@ scriptPath="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
scriptName="$( basename ${0} )" scriptName="$( basename ${0} )"
logFile="$scriptPath/${scriptName%.*}.log" logFile="$scriptPath/${scriptName%.*}.log"
# Set default mailcow configuration filename
mailcowConfigFile=mailcow.conf
# Set default docker-compose filename # Set default docker-compose filename
dockerComposeFile=docker-compose.yml dockerComposeFile=docker-compose.yml
@@ -380,8 +377,8 @@ while getopts ':l:v5:w:b:m:d:1:2:' PARAMS; do
borgDetails="${OPTARG%/}" borgDetails="${OPTARG%/}"
;; ;;
m) m)
# name of mailcow configuration file # full path to mailcow.conf configuration file
mailcowConfigFile="${OPTARG}" mailcowConfigFilePath="${OPTARG%/}"
;; ;;
d) d)
# name of docker-compose configuration file # name of docker-compose configuration file
@@ -411,20 +408,33 @@ if [ $(id -u) -ne 0 ]; then
exit 3 exit 3
fi fi
## Find mailcow configuration file so additional variables can be read ## verify mailcow.conf location provided
mailcowConfigFilePath=$( find / -mount -name "$mailcowConfigFile" -print )
if [ -z "$mailcowConfigFilePath" ]; then if [ -z "$mailcowConfigFilePath" ]; then
echo -e "\n${err}Could not locate the specified mailcow configuration" \ echo -e "\n${err}You MUST provide the full path to your mailcow.conf" \
"file: ${lit}${mailcowConfigFile}${normal}" "configuration file. Exiting.${normal}"
exit 1 exit 1
fi fi
## Find docker-compose file using mailcow configuration file path as a reference ## verify mailcow.conf and extract path
mailcowPath="${mailcowConfigFilePath%/$mailcowConfigFile*}" if checkExist ff "$mailcowConfigFilePath"; then
dockerComposeFilePath="$mailcowPath/$dockerComposeFile" # extract directory name
checkExist ff "$dockerComposeFilePath" case $mailcowConfigFilePath in
checkResult="$?" */*)
if [ "$checkResult" = 1 ]; then mailcowPath=${mailcowConfigFilePath%/*}
;;
*)
mailcowPath="."
;;
esac
else
echo -e "\n${err}Could not locate the specified mailcow configuration" \
"file: ${lit}${mailcowConfigFilePath}${normal}"
exit 1
fi
# verify docker-compose.yml exists at location of mailcow.conf (standard setup)
dockerComposeFilePath="${mailcowPath}/${dockerComposeFile}"
if ! checkExist ff "$dockerComposeFilePath"; then
echo -e "\n${err}Could not locate docker-compose configuration file:" \ echo -e "\n${err}Could not locate docker-compose configuration file:" \
"${lit}${dockerComposeFilePath}${normal}" "${lit}${dockerComposeFilePath}${normal}"
exit 1 exit 1
@@ -442,15 +452,16 @@ fi
### Log start of script operations ### Log start of script operations
echo -e "${note}${stamp} --- Start $scriptName execution ---${normal}" \ echo -e "${note}[$(stamp)] --- Start $scriptName execution ---${normal}" \
>> "$logFile" >> "$logFile"
echo -e "${info}${stamp} -- [INFO] using ${lit}${mailcowConfigFilePath}" \ echo -e "${info}[$(stamp)] -- [INFO] using ${lit}${mailcowConfigFilePath}" \
>> "$logFile" >> "$logFile"
echo -e "${info}${stamp} -- [INFO] using ${lit}${dockerComposeFilePath}" \ echo -e "${info}[$(stamp)] -- [INFO] using ${lit}${dockerComposeFilePath}" \
>> "$logFile" >> "$logFile"
### Import additional variables from mailcow configuration file ### Import additional variables from mailcow configuration file
source "${mailcowConfigFilePath}" source "$mailcowConfigFilePath"
### Export PATH so this script can access all docker and docker-compose commands ### Export PATH so this script can access all docker and docker-compose commands
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
@@ -472,7 +483,7 @@ dockerVolumeCrypt=$(docker volume inspect -f '{{ .Mountpoint }}' ${COMPOSE_PROJE
### Create sqlDump temporary directory and sqlDumpFile name ### Create sqlDump temporary directory and sqlDumpFile name
sqlDumpDir=$( mktemp -d ) sqlDumpDir=$( mktemp -d )
sqlDumpFile="backup-`date +%Y%m%d_%H%M%S`.sql" sqlDumpFile="backup-`date +%Y%m%d_%H%M%S`.sql"
echo -e "${info}${stamp} -- [INFO] mySQL dump file will be stored" \ echo -e "${info}[$(stamp)] -- [INFO] mySQL dump file will be stored" \
"at: ${lit}${sqlDumpDir}/${sqlDumpFile}${normal}" >> "$logFile" "at: ${lit}${sqlDumpDir}/${sqlDumpFile}${normal}" >> "$logFile"
@@ -485,9 +496,9 @@ echo -e "${info}${stamp} -- [INFO] mySQL dump file will be stored" \
## Check if webroot has been specified, if not, skip this entire section since there is nowhere to copy the 503 file. ## Check if webroot has been specified, if not, skip this entire section since there is nowhere to copy the 503 file.
if [ -z "$webroot" ]; then if [ -z "$webroot" ]; then
# no webroot path provided # no webroot path provided
echo -e "${info}${stamp} -- [INFO] ${warn503} --${normal}" \ echo -e "${info}[$(stamp)] -- [INFO] ${warn503} --${normal}" \
>> "$logFile" >> "$logFile"
exitWarn+=("${stamp}_5031") exitWarn+=("[$(stamp)]_5031")
clean503=0 clean503=0
else else
# verify webroot actually exists # verify webroot actually exists
@@ -495,41 +506,41 @@ else
checkResult="$?" checkResult="$?"
if [ "$checkResult" = "1" ]; then if [ "$checkResult" = "1" ]; then
# webroot directory specified could not be found # webroot directory specified could not be found
echo -e "${info}${stamp} -- [INFO] ${warn503} --${normal}" \ echo -e "${info}[$(stamp)] -- [INFO] ${warn503} --${normal}" \
>> "$logFile" >> "$logFile"
exitWarn+=("${stamp}_5032") exitWarn+=("[$(stamp)]_5032")
clean503=0 clean503=0
else else
# webroot exists # webroot exists
echo -e "${op}${stamp} Using webroot: ${lit}${webroot}${normal}" \ echo -e "${op}[$(stamp)] Using webroot: ${lit}${webroot}${normal}" \
>> "$logFile" >> "$logFile"
# Verify 503 file existance at given path # Verify 503 file existance at given path
checkExist ff "$err503Path" checkExist ff "$err503Path"
checkResult="$?" checkResult="$?"
if [ "$checkResult" = "1" ]; then if [ "$checkResult" = "1" ]; then
# 503 file could not be found # 503 file could not be found
echo -e "${info}${stamp} -- [INFO] ${warn503} --${normal}" \ echo -e "${info}[$(stamp)] -- [INFO] ${warn503} --${normal}" \
>> "$logFile" >> "$logFile"
exitWarn+=("${stamp}_5033") exitWarn+=("[$(stamp)]_5033")
clean503=0 clean503=0
else else
# 503 file exists and webroot is valid. Let's copy it! # 503 file exists and webroot is valid. Let's copy it!
echo -e "${op}${stamp} ${err503File} found at ${lit}${err503Path}" \ echo -e "${op}[$(stamp)] ${err503File} found at ${lit}${err503Path}" \
"${normal}" >> "$logFile" "${normal}" >> "$logFile"
echo -e "${op}${stamp} Copying 503 error page to webroot..." \ echo -e "${op}[$(stamp)] Copying 503 error page to webroot..." \
"${normal}" >> "$logFile" "${normal}" >> "$logFile"
cp "${err503Path}" "$webroot/" >> "$logFile" 2>&1 cp "${err503Path}" "$webroot/" >> "$logFile" 2>&1
copyResult="$?" copyResult="$?"
# verify copy was successful # verify copy was successful
if [ "$copyResult" = "1" ]; then if [ "$copyResult" = "1" ]; then
# copy was unsuccessful # copy was unsuccessful
echo -e "${info}${stamp} -- [INFO] ${warn503} --${normal}" \ echo -e "${info}[$(stamp)] -- [INFO] ${warn503} --${normal}" \
>> "$logFile" >> "$logFile"
exitWarn+=("${stamp}_5035") exitWarn+=("[$(stamp)]_5035")
clean503=0 clean503=0
else else
# copy was successful # copy was successful
echo -e "${info}${stamp} -- [INFO] 503 error page" \ echo -e "${info}[$(stamp)] -- [INFO] 503 error page" \
"successfully copied to webroot --${normal}" >> "$logFile" "successfully copied to webroot --${normal}" >> "$logFile"
clean503=1 clean503=1
fi fi
@@ -549,10 +560,10 @@ cd "$mailcowPath"
operateDocker stop postfix operateDocker stop postfix
# process result # process result
if [ "$dockerResultState" = "false" ] && [ "$dockerResultExit" -eq 0 ]; then if [ "$dockerResultState" = "false" ] && [ "$dockerResultExit" -eq 0 ]; then
echo -e "${info}${stamp} -- [INFO] Postfix container stopped --${normal}" \ echo -e "${info}[$(stamp)] -- [INFO] Postfix container stopped --${normal}" \
>> "$logFile" >> "$logFile"
else else
exitError+=("${stamp}_101") exitError+=("[$(stamp)]_101")
cleanup cleanup
quit quit
fi fi
@@ -560,62 +571,77 @@ fi
operateDocker stop dovecot operateDocker stop dovecot
# process result # process result
if [ "$dockerResultState" = "false" ] && [ "$dockerResultExit" -eq 0 ]; then if [ "$dockerResultState" = "false" ] && [ "$dockerResultExit" -eq 0 ]; then
echo -e "${info}${stamp} -- [INFO] Dovecot container stopped --${normal}" \ echo -e "${info}[$(stamp)] -- [INFO] Dovecot container stopped --${normal}" \
>> "$logFile" >> "$logFile"
else else
exitError+=("${stamp}_102") exitError+=("[$(stamp)]_102")
cleanup cleanup
quit quit
fi fi
### Dump SQL ### Dump SQL
echo -e "${op}${stamp} Dumping mailcow SQL database...${normal}" >> "$logFile" echo -e "${op}[$(stamp)] Dumping mailcow SQL database...${normal}" >> "$logFile"
docker-compose exec mysql-mailcow mysqldump --default-character-set=utf8mb4 -u${DBUSER} -p${DBPASS} ${DBNAME} > "$sqlDumpDir/$sqlDumpFile" 2>> "$logFile" docker-compose exec -T mysql-mailcow mysqldump --default-character-set=utf8mb4 \
## error checking sqldump within the container in a cron-friendly manner is a -u${DBUSER} -p${DBPASS} ${DBNAME} > "$sqlDumpDir/$sqlDumpFile" \
## nightmare, so let's just see if a non-empty file with the expected name was 2>> "$logFile"
## created dumpResult=$(docker-compose exec -T mysql-mailcow echo "$?")
## very mysqldump completed successfully
if [ "$dumpResult" = "0" ]; then
echo -e "${info}[$(stamp)] -- [INFO] mySQLdump completed successfully --" \
"${normal}" >> "$logFile"
else
exitError+=("[$(stamp)]_201")
fi
## verify the dump file was actually written to disk
checkExist fs "$sqlDumpDir/$sqlDumpFile" checkExist fs "$sqlDumpDir/$sqlDumpFile"
checkResult="$?" checkResult="$?"
if [ "$checkResult" = "0" ]; then if [ "$checkResult" = "0" ]; then
echo -e "${ok}${stamp} -- [SUCCESS] SQL successfully dumped --" \ echo -e "${ok}[$(stamp)] -- [SUCCESS] SQL successfully dumped --" \
"${normal}" >> "$logFile" "${normal}" >> "$logFile"
else else
exitError+=("${stamp}_201") exitError+=("[$(stamp)]_201")
fi fi
### Save redis state ### Save redis state
## Delete any existing redis dump file otherwise our check will be useless ## Delete any existing redis dump file otherwise our file check will be useless
echo -e "${op}${stamp} Cleaning up old redis state backup...${normal}" \ echo -e "${op}[$(stamp)] Cleaning up old redis state backup...${normal}" \
>> "$logFile" >> "$logFile"
checkExist ff "$dockerVolumeRedis/dump.rdb" checkExist ff "$dockerVolumeRedis/dump.rdb"
checkResult="$?" checkResult="$?"
if [ "$checkResult" = "0" ]; then if [ "$checkResult" = "0" ]; then
echo -e "${lit}${stamp} Old redis backup found.${normal}" >> "$logFile" echo -e "${lit}[$(stamp)] Old redis backup found. ${op}Deleting..." \
echo -e "${op}Deleting...${normal}" >> "$logFile" "${normal}" >> "$logFile"
rm -f "$dockerVolumeRedis/dump.rdb" 2>> "$logFile" rm -f "$dockerVolumeRedis/dump.rdb" 2>> "$logFile"
echo -e "${op}${stamp}...done${normal}" >> "$logFile" echo -e "${op}[$(stamp)] ...done${normal}" >> "$logFile"
else else
echo -e "${op}${stamp} No old redis backup found${normal}" \ echo -e "${op}[$(stamp)] No old redis backup found${normal}" \
>> "$logFile" >> "$logFile"
fi fi
## Export redis ## Export redis
echo -e "${op}${stamp} Saving redis state information...${normal}" >> "$logFile" echo -e "${op}[$(stamp)] Saving redis state information...${normal}" >> "$logFile"
docker-compose exec redis-mailcow redis-cli save >> "$logFile" 2>&1 docker-compose exec -T redis-mailcow redis-cli save >> "$logFile" 2>&1
## although redis returns an 'OK' if successful, seemingly nothing can test for saveResult=$(docker-compose exec -T redis-mailcow echo "$?")
## this... so let's just verify the dump file was written to disk # verify save operation completed successfully
if [ "$saveResult" = "0" ]; then
echo -e "${info}[$(stamp)] -- [INFO] redis save-state successful --" \
"${normal}" >> "$logFile"
else
exitError+=("[$(stamp)]_202")
fi
## verify save-file written to disk
checkExist fs "$dockerVolumeRedis/dump.rdb" checkExist fs "$dockerVolumeRedis/dump.rdb"
checkResult="$?" checkResult="$?"
if [ "$checkResult" = "0" ]; then if [ "$checkResult" = "0" ]; then
echo -e "${ok}${stamp} -- [SUCCESS] redis state saved --${normal}" \ echo -e "${ok}[$(stamp)] -- [SUCCESS] redis state saved --${normal}" \
>> "$logFile" >> "$logFile"
else else
exitError+=("${stamp}_202") exitError+=("[$(stamp)]_202")
fi fi
### Call borgbackup to copy actual files ### Call borgbackup to copy actual files
echo -e "${op}${stamp} Pre-backup tasks completed, calling borgbackup..." \ echo -e "${op}[$(stamp)] Pre-backup tasks completed, calling borgbackup..." \
"${normal}" >> "$logFile" "${normal}" >> "$logFile"
## Get borgbackup settings and repo details ## Get borgbackup settings and repo details
@@ -624,10 +650,10 @@ mapfile -t borgConfig < "$borgDetails"
## check if any required borg configuration variables in defintion file are ## check if any required borg configuration variables in defintion file are
## empty and exit with error, otherwise, map array items to variables ## empty and exit with error, otherwise, map array items to variables
# check: borg base directory # check: borg base directory
echo -e "${op}${stamp} Verifying supplied borg configuration variables..." \ echo -e "${op}[$(stamp)] Verifying supplied borg configuration variables..." \
"${normal}" >> "$logFile" "${normal}" >> "$logFile"
if [ -z "${borgConfig[0]}" ]; then if [ -z "${borgConfig[0]}" ]; then
exitError+=("${stamp}_210") exitError+=("[$(stamp)]_210")
cleanup cleanup
quit quit
else else
@@ -636,16 +662,16 @@ else
checkResult="$?" checkResult="$?"
if [ "$checkResult" = "1" ]; then if [ "$checkResult" = "1" ]; then
# borg base directory specified could not be found # borg base directory specified could not be found
exitError+=("${stamp}_210") exitError+=("[$(stamp)]_210")
cleanup cleanup
quit quit
fi fi
echo -e "${op}${stamp} Borg base dir... OK${normal}" >> "$logFile" echo -e "${op}[$(stamp)] Borg base dir... OK${normal}" >> "$logFile"
export BORG_BASE_DIR="${borgConfig[0]%/}" export BORG_BASE_DIR="${borgConfig[0]%/}"
fi fi
# check: path to SSH keyfile # check: path to SSH keyfile
if [ -z "${borgConfig[1]}" ]; then if [ -z "${borgConfig[1]}" ]; then
exitError+=("${stamp}_211") exitError+=("[$(stamp)]_211")
cleanup cleanup
quit quit
else else
@@ -653,28 +679,28 @@ else
checkResult="$?" checkResult="$?"
if [ "$checkResult" = 1 ]; then if [ "$checkResult" = 1 ]; then
# SSH keyfile specified could not be found # SSH keyfile specified could not be found
exitError+=("${stamp}_211") exitError+=("[$(stamp)]_211")
cleanup cleanup
quit quit
fi fi
echo -e "${op}${stamp} Borg SSH key... OK${normal}" >> "$logFile" echo -e "${op}[$(stamp)] Borg SSH key... OK${normal}" >> "$logFile"
export BORG_RSH="ssh -i ${borgConfig[1]}" export BORG_RSH="ssh -i ${borgConfig[1]}"
fi fi
# check: name of borg repo # check: name of borg repo
if [ -z "${borgConfig[2]}" ]; then if [ -z "${borgConfig[2]}" ]; then
exitError+=("${stamp}_212") exitError+=("[$(stamp)]_212")
cleanup cleanup
quit quit
else else
echo -e "${op}${stamp} Borg REPO name... OK${normal}" >> "$logFile" echo -e "${op}[$(stamp)] Borg REPO name... OK${normal}" >> "$logFile"
export BORG_REPO="${borgConfig[2]}" export BORG_REPO="${borgConfig[2]}"
fi fi
# repo password # repo password
if [ -n "${borgConfig[3]}" ]; then if [ -n "${borgConfig[3]}" ]; then
echo -e "${op}${stamp} Borg SSH/REPO password... OK${normal}" >> "$logFile" echo -e "${op}[$(stamp)] Borg SSH/REPO password... OK${normal}" >> "$logFile"
export BORG_PASSPHRASE="${borgConfig[3]}" export BORG_PASSPHRASE="${borgConfig[3]}"
else else
exitWarn+=("${stamp}_2111") exitWarn+=("[$(stamp)]_2111")
# if the password was omitted by mistake, export a dummy password so borg # if the password was omitted by mistake, export a dummy password so borg
# fails with an error instead of sitting and waiting for input # fails with an error instead of sitting and waiting for input
export BORG_PASSPHRASE="DummyPasswordSoBorgFails" export BORG_PASSPHRASE="DummyPasswordSoBorgFails"
@@ -687,32 +713,32 @@ borgExclude="${borgConfig[5]}"
borgPrune="${borgConfig[6]}" borgPrune="${borgConfig[6]}"
# export: borg remote path (if not blank) # export: borg remote path (if not blank)
if [ -n "${borgConfig[7]}" ]; then if [ -n "${borgConfig[7]}" ]; then
echo -e "${op}${stamp} Borg REMOTE path... OK${normal}" >> "$logFile" echo -e "${op}[$(stamp)] Borg REMOTE path... OK${normal}" >> "$logFile"
export BORG_REMOTE_PATH="${borgConfig[7]}" export BORG_REMOTE_PATH="${borgConfig[7]}"
else else
exitWarn+=("${stamp}_2112") exitWarn+=("[$(stamp)]_2112")
fi fi
## If borgXtra exists, map contents to an array variable ## If borgXtra exists, map contents to an array variable
if [ -n "$borgXtra" ]; then if [ -n "$borgXtra" ]; then
echo -e "${op}${stamp} Processing referenced extra files list for" \ echo -e "${op}[$(stamp)] Processing referenced extra files list for" \
"borgbackup to include in backup${normal}" >> "$logFile" "borgbackup to include in backup${normal}" >> "$logFile"
checkExist ff "$borgXtra" checkExist ff "$borgXtra"
checkResult="$?" checkResult="$?"
if [ "$checkResult" = "0" ]; then if [ "$checkResult" = "0" ]; then
echo -e "${op}${stamp} Found ${lit}${borgXtra}${normal}" >> "$logFile" echo -e "${op}[$(stamp)] Found ${lit}${borgXtra}${normal}" >> "$logFile"
mapfile -t xtraFiles < "$borgXtra" mapfile -t xtraFiles < "$borgXtra"
echo -e "${op}${stamp} Processed extra files list for inclusion in" \ echo -e "${op}[$(stamp)] Processed extra files list for inclusion in" \
"borgbackup${normal}" >> "$logFile" "borgbackup${normal}" >> "$logFile"
else else
exitWarn+=("${stamp}_2113") exitWarn+=("[$(stamp)]_2113")
fi fi
else else
# no extra locations specified # no extra locations specified
echo -e "${op}${stamp} No additional locations specified for backup." \ echo -e "${op}[$(stamp)] No additional locations specified for backup." \
"Only Mailcow data and config files will be backed up.${normal}" \ "Only Mailcow data and config files will be backed up.${normal}" \
>> "$logFile" >> "$logFile"
exitWarn+=("${stamp}_2116") exitWarn+=("[$(stamp)]_2116")
fi fi
## Check if borgExclude exists since borg will throw an error if it's missing ## Check if borgExclude exists since borg will throw an error if it's missing
@@ -720,16 +746,16 @@ if [ -n "$borgExclude" ]; then
checkExist ff "$borgExclude" checkExist ff "$borgExclude"
checkResult="$?" checkResult="$?"
if [ "$checkResult" = "0" ]; then if [ "$checkResult" = "0" ]; then
echo -e "${op}${stamp} Found ${lit}${borgExclude}${normal}" \ echo -e "${op}[$(stamp)] Found ${lit}${borgExclude}${normal}" \
>> "$logFile" >> "$logFile"
else else
# file not found, unset the variable so it's like it was not specified # file not found, unset the variable so it's like it was not specified
# in the first place and continue with backup # in the first place and continue with backup
unset borgExclude unset borgExclude
exitWarn+=("${stamp}_2114") exitWarn+=("[$(stamp)]_2114")
fi fi
else else
echo -e "${op}${stamp} Exclusion pattern file not specified." \ echo -e "${op}[$(stamp)] Exclusion pattern file not specified." \
"No exclusions will be processed${normal}" >> "$logFile" "No exclusions will be processed${normal}" >> "$logFile"
fi fi
@@ -740,13 +766,13 @@ fi
## the 'noexec' option for security. Thus, we will use/create a 'tmp' folder ## the 'noexec' option for security. Thus, we will use/create a 'tmp' folder
## within the BORG_BASE_DIR and instruct python to use that instead of /tmp ## within the BORG_BASE_DIR and instruct python to use that instead of /tmp
# check if BORG_BASE_DIR/tmp exists, if not, create it # check if BORG_BASE_DIR/tmp exists, if not, create it
echo -e "${op}${stamp} Checking for tmp directory at ${lit}${BORG_BASE_DIR}" \ echo -e "${op}[$(stamp)] Checking for tmp directory at ${lit}${BORG_BASE_DIR}" \
"${normal}" >> "$logFile" "${normal}" >> "$logFile"
checkExist fd "$BORG_BASE_DIR/tmp" checkExist fd "$BORG_BASE_DIR/tmp"
checkResult="$?" checkResult="$?"
if [ "$checkResult" = "1" ]; then if [ "$checkResult" = "1" ]; then
# folder not found # folder not found
echo -e "${op}${stamp} tmp folder not found... creating${lit}" \ echo -e "${op}[$(stamp)] tmp folder not found... creating${lit}" \
"${BORG_BASE_DIR}/tmp${normal}" >> "$logFile" "${BORG_BASE_DIR}/tmp${normal}" >> "$logFile"
mkdir "$BORG_BASE_DIR/tmp" 2>> "$logFile" mkdir "$BORG_BASE_DIR/tmp" 2>> "$logFile"
# verify folder created # verify folder created
@@ -754,17 +780,17 @@ if [ "$checkResult" = "1" ]; then
checkResult="$?" checkResult="$?"
if [ "$checkResult" = "0" ]; then if [ "$checkResult" = "0" ]; then
# folder exists # folder exists
echo -e "${op}${stamp} tmp folder created within borg base directory" \ echo -e "${op}[$(stamp)] tmp folder created within borg base directory" \
"${normal}" >> "$logFile" "${normal}" >> "$logFile"
else else
# problem creating folder and script will exit # problem creating folder and script will exit
exitError+=("${stamp}_215") exitError+=("[$(stamp)]_215")
cleanup cleanup
quit quit
fi fi
else else
# folder found # folder found
echo -e "${op}${stamp} tmp folder found within borg base directory" \ echo -e "${op}[$(stamp)] tmp folder found within borg base directory" \
"${normal}" >> "$logFile" "${normal}" >> "$logFile"
fi fi
# export TMPDIR environment variable # export TMPDIR environment variable
@@ -775,78 +801,78 @@ export TMPDIR="${BORG_BASE_DIR}/tmp"
# commandline depends on whether borgExclude is empty or not # commandline depends on whether borgExclude is empty or not
if [ -z "$borgExclude" ]; then if [ -z "$borgExclude" ]; then
# borgExclude is empty # borgExclude is empty
echo -e "${bold}${op}${stamp} Executing borg without exclusions${normal}" \ echo -e "${bold}${op}[$(stamp)] Executing borg without exclusions${normal}" \
>> "$logFile" >> "$logFile"
borg --show-rc create ${borgCreateParams} ::`date +%Y-%m-%d_%H%M%S` \ borg --show-rc create ${borgCreateParams} ::`date +%Y-%m-%d_%H%M%S` \
${xtraFiles[@]} \ "${xtraFiles[@]}" \
${sqlDumpDir} \ "${sqlDumpDir}" \
${dockerVolumeMail} ${dockerVolumeRspamd} ${dockerVolumePostfix} \ "${dockerVolumeMail}" "${dockerVolumeRspamd}" "${dockerVolumePostfix}" \
${dockerVolumeRedis} ${dockerVolumeCrypt} \ "${dockerVolumeRedis}" "${dockerVolumeCrypt}" \
2>> "$logFile" 2>> "$logFile"
else else
# borgExclude is not empty # borgExclude is not empty
echo -e "${bold}${op}${stamp} Executing borg with exclusions${normal}" \ echo -e "${bold}${op}[$(stamp)] Executing borg with exclusions${normal}" \
>> "$logFile" >> "$logFile"
borg --show-rc create ${borgCreateParams} --exclude-from ${borgExclude} \ borg --show-rc create ${borgCreateParams} --exclude-from "${borgExclude}" \
::`date +%Y-%m-%d_%H%M%S` \ ::`date +%Y-%m-%d_%H%M%S` \
${xtraFiles[@]} \ "${xtraFiles[@]}" \
${sqlDumpDir} \ "${sqlDumpDir}" \
${dockerVolumeMail} ${dockerVolumeRspamd} ${dockerVolumePostfix} \ "${dockerVolumeMail}" "${dockerVolumeRspamd}" "${dockerVolumePostfix}" \
${dockerVolumeRedis} ${dockerVolumeCrypt} \ "${dockerVolumeRedis}" "${dockerVolumeCrypt}" \
2>> "$logFile" 2>> "$logFile"
fi fi
## Check status of borg operation ## Check status of borg operation
borgResult="$?" borgResult="$?"
if [ "$borgResult" -eq 0 ]; then if [ "$borgResult" -eq 0 ]; then
echo -e "${ok}${stamp} -- [SUCCESS] Borg backup completed successfully --" \ echo -e "${ok}[$(stamp)] -- [SUCCESS] Borg backup completed successfully --" \
"${normal}" >> "$logFile" "${normal}" >> "$logFile"
elif [ "$borgResult" -eq 1 ]; then elif [ "$borgResult" -eq 1 ]; then
exitWarn+=("${stamp}_2200") exitWarn+=("[$(stamp)]_2200")
elif [ "$borgResult" -ge 2 ]; then elif [ "$borgResult" -ge 2 ]; then
exitError+=("${stamp}_220") exitError+=("[$(stamp)]_220")
cleanup cleanup
quit quit
else else
exitWarn+=("${stamp}_2201") exitWarn+=("[$(stamp)]_2201")
fi fi
## Generate and execute borg prune ## Generate and execute borg prune
# command depends on whether or not parameters have been defined # command depends on whether or not parameters have been defined
if [ -n "$borgPrune" ]; then if [ -n "$borgPrune" ]; then
# parameters defined # parameters defined
echo -e "${bold}${op}${stamp} Executing borg prune operation${normal}" \ echo -e "${bold}${op}[$(stamp)] Executing borg prune operation${normal}" \
>> "$logFile" >> "$logFile"
borg prune --show-rc -v ${borgPruneParams} ${borgPrune} \ borg prune --show-rc -v ${borgPruneParams} ${borgPrune} \
2>> "$logFile" 2>> "$logFile"
# check return-status # check return-status
pruneResult="$?" pruneResult="$?"
if [ "$pruneResult" -eq 0 ]; then if [ "$pruneResult" -eq 0 ]; then
echo -e "${ok}${stamp} -- [SUCCESS] Borg prune completed successfully" \ echo -e "${ok}[$(stamp)] -- [SUCCESS] Borg prune completed successfully" \
"--${normal}" >> "$logFile" "--${normal}" >> "$logFile"
elif [ "$pruneResult" -eq 1 ]; then elif [ "$pruneResult" -eq 1 ]; then
exitWarn+=("${stamp}_2210") exitWarn+=("[$(stamp)]_2210")
elif [ "$pruneResult" -ge 2 ]; then elif [ "$pruneResult" -ge 2 ]; then
exitError+=("${stamp}_221") exitError+=("[$(stamp)]_221")
else else
exitWarn+=("${stamp}_2212") exitWarn+=("[$(stamp)]_2212")
fi fi
else else
# parameters not defined... skip pruning # parameters not defined... skip pruning
exitWarn+=("${stamp}_2115") exitWarn+=("[$(stamp)]_2115")
fi fi
### borgbackup completed ### borgbackup completed
echo -e "${op}${stamp} Borgbackup completed... begin cleanup" \ echo -e "${op}[$(stamp)] Borgbackup completed... begin cleanup" \
"${normal}" >> "$logFile" "${normal}" >> "$logFile"
### Exit script ### Exit script
echo -e "${bold}${op}${stamp} ***Normal exit process***${normal}" \ echo -e "${bold}${op}[$(stamp)] ***Normal exit process***${normal}" \
>> "$logFile" >> "$logFile"
cleanup cleanup
echo -e "${bold}${ok}${stamp} -- [SUCCESS] All processes completed" \ echo -e "${bold}${ok}[$(stamp)] -- [SUCCESS] All processes completed" \
"successfully --${normal}" >> "$logFile" "successfully --${normal}" >> "$logFile"
quit quit
+3 -3
View File
@@ -1,7 +1,7 @@
<path to borgbackup base directory> /var/borgbackup <path to borgbackup base directory> /var/borgbackup
<path to SSH private key for repo> /var/borgbackup/sshPrivate.key <path to SSH private key for remote server> /var/borgbackup/sshPrivate.key
<connection string to remote repo> user@server-number.rsync.net:repoName/ <connection string to remote repo> user@servername.tld:repoName/
<password for SSH key/repo> pAsSwOrd <password for repo> pAsSwOrd
<path to file listing extra files> /root/scripts/xtraLocations.borg <path to file listing extra files> /root/scripts/xtraLocations.borg
<path to file with exclusions> /root/scripts/excludeLocations.borg <path to file with exclusions> /root/scripts/excludeLocations.borg
<purge timeframe options> --keep-within=7d --keep-daily=30 --keep-weekly=12 --keep-monthly=-1 <purge timeframe options> --keep-within=7d --keep-daily=30 --keep-weekly=12 --keep-monthly=-1