restic-compose-backup/src/backup.sh

162 lines
3.7 KiB
Bash

#!/bin/bash
set -euo pipefail
if [ "${DEBUG:-false}" == "true" ]; then
set -x
fi
: "${RCON_HOST:=localhost}"
: "${RCON_PORT:=25575}"
: "${RCON_PASSWORD:=minecraft}"
export RCON_HOST
export RCON_PORT
export RCON_PASSWORD
###############
## common ##
## functions ##
###############
is_elem_in_array() {
# $1 = element
# All remaining arguments are array to search for the element in
if [ "$#" -lt 2 ]; then
log INTERNALERROR "Wrong number of arguments passed to is_elem_in_array function"
return 2
fi
local element="${1}"
shift
local e
for e; do
if [ "${element}" == "${e}" ]; then
return 0
fi
done
return 1
}
log() {
if [ "$#" -lt 1 ]; then
log INTERNALERROR "Wrong number of arguments passed to log function"
return 2
fi
local level="${1}"
shift
local valid_levels=(
"INFO"
"WARN"
"ERROR"
"INTERNALERROR"
)
if ! is_elem_in_array "${level}" "${valid_levels[@]}"; then
log INTERNALERROR "Log level ${level} is not a valid level."
return 2
fi
(
# If any arguments are passed besides log level
if [ "$#" -ge 1 ]; then
# then use them as log message(s)
<<<"${*}" cat -
else
# otherwise read log messages from standard input
cat -
fi
if [ "${level}" == "INTERNALERROR" ]; then
echo "Please report this: https://github.com/itzg/docker-mc-backup/issues"
fi
) | awk -v level="${level}" '{ printf("%s %s %s\n", strftime("%FT%T%z"), level, $0); fflush(); }'
} >&2
retry() {
if [ "$#" -lt 3 ]; then
log INTERNALERROR "Wrong number of arguments passed to retry function"
return 1
fi
# How many times should we retry?
# Value smaller than zero means infinitely
local retries="${1}"
# Time to sleep between retries
local interval="${2}"
readonly retries interval
shift 2
if (( retries < 0 )); then
local retries_msg="infinite"
else
local retries_msg="${retries}"
fi
local i=-1 # -1 since we will increment it before printing
while (( retries >= ++i )) || [ "${retries_msg}" != "${retries}" ]; do
# Send SIGINT after 5 minutes. If it doesn't shut down in 30 seconds, kill it.
if output="$(timeout --signal=SIGINT --kill-after=30s 5m "${@}" 2>&1 | tr '\n' '\t')"; then
log INFO "Command executed successfully ${*}"
return 0
else
log ERROR "Unable to execute ${*} - try ${i}/${retries_msg}. Retrying in ${interval}"
if [ -n "${output}" ]; then
log ERROR "Failure reason: ${output}"
fi
fi
# shellcheck disable=SC2086
sleep ${interval}
done
return 2
}
is_function() {
if [ "${#}" -ne 1 ]; then
log INTERNALERROR "is_function expects 1 argument, received ${#}"
fi
name="${1}"
[ "$(type -t "${name}")" == "function" ]
}
call_if_function_exists() {
if [ "${#}" -lt 1 ]; then
log INTERNALERROR "call_if_function_exists expects at least 1 argument, received ${#}"
return 2
fi
function_name="${1}"
if is_function "${function_name}"; then
eval "${@}"
else
log INTERNALERROR "${function_name} is not a valid function!"
return 2
fi
}
##########
## main ##
##########
log INFO "waiting for rcon readiness..."
# 20 times, 10 second delay
retry 20 10s rcon-cli save-on
if retry 5 10s rcon-cli save-off; then
# No matter what we were doing, from now on if the script crashes
# or gets shut down, we want to make sure saving is on
trap 'retry 5 5s rcon-cli save-on' EXIT
retry 5 10s rcon-cli save-all
retry 5 10s sync
rcb backup
retry 20 10s rcon-cli save-on
# Remove our exit trap now
trap EXIT
else
log ERROR "Unable to turn saving off. Is the server running?"
exit 1
fi
if (( PRUNE_BACKUPS_DAYS > 0 )); then
rcb cleanup
fi