2021-10-25 11:35:40 +00:00
|
|
|
#!/bin/bash
|
|
|
|
|
2021-11-06 17:45:39 +00:00
|
|
|
server_name="${SERVER_NAME:?SERVER_NAME not set}"
|
2021-10-25 11:35:40 +00:00
|
|
|
|
2021-11-06 17:45:39 +00:00
|
|
|
restic_password_file="${RESTIC_PASSWORD_FILE:?RESTIC_PASSWORD_FILE not set}"
|
2021-10-25 11:35:40 +00:00
|
|
|
|
2021-11-09 12:20:11 +00:00
|
|
|
restic_host="${RESTIC_HOST:?RESTIC_HOST not set}"
|
2021-10-25 11:35:40 +00:00
|
|
|
|
2021-11-09 12:30:19 +00:00
|
|
|
backup_path="${BACKUP_DEST:?BACKUP_DEST not set}"
|
|
|
|
|
2021-11-10 22:02:44 +00:00
|
|
|
# shellcheck disable=SC2153
|
2021-11-09 12:20:11 +00:00
|
|
|
ssh_key_file="${SSH_KEY_FILE}"
|
|
|
|
s3_key_file="${AWS_SECRET_ACCESS_KEY_FILE}"
|
|
|
|
|
|
|
|
restic_repo=
|
|
|
|
restic_extra_options=
|
|
|
|
|
|
|
|
if [ -n "$ssh_key_file" ] && [ -f "$ssh_key_file" ]; then
|
|
|
|
restic_repo="sftp:$restic_host:/$server_name"
|
2021-11-10 20:01:57 +00:00
|
|
|
|
|
|
|
# Only check server against provided SSH_HOST_KEY, if set
|
|
|
|
if [ -n "$SSH_HOST_KEY" ]; then
|
|
|
|
tmpfile=$(mktemp)
|
2021-11-24 10:17:13 +00:00
|
|
|
echo "$SSH_HOST_KEY" >>"$tmpfile"
|
2021-11-10 22:00:39 +00:00
|
|
|
echo "using host key $SSH_HOST_KEY"
|
2021-11-10 20:01:57 +00:00
|
|
|
ssh_options="-o 'UserKnownHostsFile $tmpfile'"
|
|
|
|
elif [ "$SSH_HOST_KEY_DISABLE" = "1" ]; then
|
2021-11-10 22:00:39 +00:00
|
|
|
echo "disabling SSH host key checking"
|
2021-11-10 20:01:57 +00:00
|
|
|
ssh_options="-o 'StrictHostKeyChecking=No'"
|
|
|
|
else
|
2021-11-10 22:00:39 +00:00
|
|
|
echo "neither SSH_HOST_KEY nor SSH_HOST_KEY_DISABLE set"
|
2021-11-10 20:01:57 +00:00
|
|
|
fi
|
|
|
|
restic_extra_options="sftp.command=ssh $ssh_options -i $ssh_key_file $restic_host -s sftp"
|
2021-11-09 12:20:11 +00:00
|
|
|
fi
|
|
|
|
|
2021-11-09 13:25:43 +00:00
|
|
|
if [ -n "$s3_key_file" ] && [ -f "$s3_key_file" ] && [ -n "$AWS_ACCESS_KEY_ID" ]; then
|
2021-11-09 12:30:19 +00:00
|
|
|
AWS_SECRET_ACCESS_KEY="$(cat "${s3_key_file}")"
|
|
|
|
export AWS_SECRET_ACCESS_KEY
|
2021-11-16 10:44:10 +00:00
|
|
|
restic_repo="s3:$restic_host:/$server_name"
|
2021-11-09 12:20:11 +00:00
|
|
|
fi
|
|
|
|
|
|
|
|
if [ -z "$restic_repo" ]; then
|
2021-11-10 22:00:39 +00:00
|
|
|
echo "you must configure either SFTP or S3 storage, see README"
|
2021-11-09 12:20:11 +00:00
|
|
|
exit 1
|
|
|
|
fi
|
|
|
|
|
2021-11-09 12:30:19 +00:00
|
|
|
echo "restic_repo: $restic_repo"
|
|
|
|
|
2021-11-09 12:20:11 +00:00
|
|
|
# Pre-bake-in some default restic options
|
|
|
|
_restic() {
|
2021-11-09 13:25:43 +00:00
|
|
|
if [ -z "$restic_extra_options" ]; then
|
|
|
|
# shellcheck disable=SC2068
|
|
|
|
restic -p "$restic_password_file" \
|
|
|
|
--quiet -r "$restic_repo" \
|
|
|
|
$@
|
|
|
|
else
|
|
|
|
# shellcheck disable=SC2068
|
|
|
|
restic -p "$restic_password_file" \
|
|
|
|
--quiet -r "$restic_repo" \
|
|
|
|
-o "$restic_extra_options" \
|
|
|
|
$@
|
|
|
|
fi
|
2021-11-09 12:20:11 +00:00
|
|
|
}
|
2021-11-06 17:45:39 +00:00
|
|
|
|
|
|
|
if [ -n "$SERVICES_OVERRIDE" ]; then
|
2021-11-09 10:37:56 +00:00
|
|
|
# this is fine because docker service names should never include spaces or
|
|
|
|
# glob characters
|
|
|
|
# shellcheck disable=SC2206
|
2021-11-06 17:45:39 +00:00
|
|
|
services=($SERVICES_OVERRIDE)
|
|
|
|
else
|
|
|
|
mapfile -t services < <(docker service ls --format '{{ .Name }}')
|
|
|
|
fi
|
2021-10-25 11:35:40 +00:00
|
|
|
|
2021-11-10 20:01:57 +00:00
|
|
|
if [[ \ $*\ != *\ --skip-backup\ * ]]; then
|
2021-12-13 10:37:02 +00:00
|
|
|
rm -rf "${backup_path}"
|
|
|
|
|
2021-11-10 20:01:57 +00:00
|
|
|
for service in "${services[@]}"; do
|
|
|
|
echo "service: $service"
|
|
|
|
details=$(docker service inspect "$service" --format "{{ json .Spec.Labels }}")
|
|
|
|
if echo "$details" | jq -r '.["backupbot.backup"]' | grep -q 'true'; then
|
|
|
|
pre=$(echo "$details" | jq -r '.["backupbot.backup.pre-hook"]')
|
|
|
|
post=$(echo "$details" | jq -r '.["backupbot.backup.post-hook"]')
|
|
|
|
path=$(echo "$details" | jq -r '.["backupbot.backup.path"]')
|
2021-11-24 10:17:13 +00:00
|
|
|
|
2021-11-10 20:01:57 +00:00
|
|
|
if [ "$path" = "null" ]; then
|
|
|
|
echo "ERROR: missing 'path' for $service"
|
2021-11-24 10:17:13 +00:00
|
|
|
continue # or maybe exit?
|
2021-11-10 20:01:57 +00:00
|
|
|
fi
|
|
|
|
|
|
|
|
container=$(docker container ls -f "name=$service" --format '{{ .ID }}')
|
2021-11-24 10:17:13 +00:00
|
|
|
|
2021-11-10 20:01:57 +00:00
|
|
|
echo "backing up $service"
|
2021-11-24 10:17:13 +00:00
|
|
|
|
2021-11-10 20:01:57 +00:00
|
|
|
if [ "$pre" != "null" ]; then
|
|
|
|
# run the precommand
|
|
|
|
# shellcheck disable=SC2086
|
|
|
|
docker exec "$container" sh -c "$pre"
|
|
|
|
fi
|
|
|
|
|
|
|
|
# run the backup
|
2021-11-23 16:38:31 +00:00
|
|
|
for p in ${path//,/ }; do
|
2021-11-24 10:17:13 +00:00
|
|
|
# creates the parent folder, so `docker cp` has reliable behaviour no matter if $p ends with `/` or `/.`
|
|
|
|
dir=$backup_path/$service/$(dirname "$p")
|
|
|
|
test -d "$dir" || mkdir -p "$dir"
|
2021-12-13 10:37:02 +00:00
|
|
|
docker cp -a "$container:$p" "$dir/$(basename "$p")"
|
2021-11-23 16:38:31 +00:00
|
|
|
done
|
2021-11-10 20:01:57 +00:00
|
|
|
|
|
|
|
if [ "$post" != "null" ]; then
|
|
|
|
# run the postcommand
|
|
|
|
# shellcheck disable=SC2086
|
|
|
|
docker exec "$container" sh -c "$post"
|
|
|
|
fi
|
2021-10-25 11:35:40 +00:00
|
|
|
fi
|
2021-11-10 20:01:57 +00:00
|
|
|
done
|
2021-10-25 11:35:40 +00:00
|
|
|
|
2021-11-10 20:01:57 +00:00
|
|
|
# check if restic repo exists, initialise if not
|
|
|
|
if [ -z "$(_restic cat config)" ] 2>/dev/null; then
|
|
|
|
echo "initializing restic repo"
|
|
|
|
_restic init
|
2021-10-25 11:35:40 +00:00
|
|
|
fi
|
2021-11-09 12:20:11 +00:00
|
|
|
fi
|
|
|
|
|
2021-11-10 20:01:57 +00:00
|
|
|
if [[ \ $*\ != *\ --skip-upload\ * ]]; then
|
2021-11-23 16:38:31 +00:00
|
|
|
_restic backup --host "$server_name" --tag coop-cloud "$backup_path"
|
2021-11-10 20:01:57 +00:00
|
|
|
fi
|
2021-11-24 10:17:13 +00:00
|
|
|
|