|
@ -386,7 +386,7 @@ EOF |
|
|
stopped_containers=1 |
|
|
stopped_containers=1 |
|
|
fi |
|
|
fi |
|
|
|
|
|
|
|
|
if [[ -n "$path" ]]; then |
|
|
|
|
|
|
|
|
if [[ "$path" == "/"* ]]; then |
|
|
|
|
|
|
|
|
## |
|
|
## |
|
|
## Additional intelligence to simple file copy |
|
|
## Additional intelligence to simple file copy |
|
@ -425,9 +425,35 @@ EOF |
|
|
fi |
|
|
fi |
|
|
fi |
|
|
fi |
|
|
else |
|
|
else |
|
|
|
|
|
ALL_TARGETS=(mailcow postfix rspamd redis crypt vmail{,-attachments} mysql) |
|
|
|
|
|
|
|
|
|
|
|
if [[ -n "$path" ]]; then |
|
|
|
|
|
targets=() |
|
|
|
|
|
bad_targets=() |
|
|
|
|
|
for target in ${path//,/ }; do |
|
|
|
|
|
if [[ " ${ALL_TARGETS[*]} " != *" $target "* ]]; then |
|
|
|
|
|
bad_targets+=("$target") |
|
|
|
|
|
fi |
|
|
|
|
|
targets+=("$target") |
|
|
|
|
|
done |
|
|
|
|
|
if [ "${#bad_targets[@]}" -gt 0 ]; then |
|
|
|
|
|
bad_target_msg=$(printf "%s, " "${bad_targets[@]}") |
|
|
|
|
|
err "Unknown components: ${bad_target_msg%, }. These are allowed components:" |
|
|
|
|
|
printf " - %s\n" "${ALL_TARGETS[@]}" >&2 |
|
|
|
|
|
return 1 |
|
|
|
|
|
fi |
|
|
|
|
|
msg_target="Partial mailcow backup" |
|
|
|
|
|
else |
|
|
|
|
|
targets=("${ALL_TARGETS[@]}") |
|
|
|
|
|
msg_target="Full mailcow backup" |
|
|
|
|
|
fi |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for target in "${targets[@]}"; do |
|
|
|
|
|
case "$target" in |
|
|
|
|
|
postfix|rspamd|redis|crypt|vmail|vmail-attachments) |
|
|
|
|
|
|
|
|
for vol in postfix rspamd redis crypt vmail{,-attachments}; do |
|
|
|
|
|
volume_name="mailcowdockerized_${vol}-vol-1" |
|
|
|
|
|
|
|
|
volume_name="mailcowdockerized_${target}-vol-1" |
|
|
volume_dir="/var/lib/docker/volumes/${volume_name}/_data" |
|
|
volume_dir="/var/lib/docker/volumes/${volume_name}/_data" |
|
|
if ! backup:path_exists "${volume_dir}/"; then |
|
|
if ! backup:path_exists "${volume_dir}/"; then |
|
|
warn "No '$volume_name' in backup. This might be expected." |
|
|
warn "No '$volume_name' in backup. This might be expected." |
|
@ -445,8 +471,8 @@ EOF |
|
|
|
|
|
|
|
|
echo "${WHITE}Downloading of $volume_name${NORMAL}" |
|
|
echo "${WHITE}Downloading of $volume_name${NORMAL}" |
|
|
backup:rsync "${volume_dir}/" "${volume_dir}" || return 1 |
|
|
backup:rsync "${volume_dir}/" "${volume_dir}" || return 1 |
|
|
done |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;; |
|
|
|
|
|
mailcow) |
|
|
## Mailcow git base |
|
|
## Mailcow git base |
|
|
COMPOSE_FILE= |
|
|
COMPOSE_FILE= |
|
|
for mailcow_dir in /opt/{apps/,}mailcow-dockerized; do |
|
|
for mailcow_dir in /opt/{apps/,}mailcow-dockerized; do |
|
@ -458,12 +484,40 @@ EOF |
|
|
backup:rsync "${mailcow_dir}"/ "${mailcow_dir}" || return 1 |
|
|
backup:rsync "${mailcow_dir}"/ "${mailcow_dir}" || return 1 |
|
|
break |
|
|
break |
|
|
done |
|
|
done |
|
|
|
|
|
|
|
|
if [ -z "$COMPOSE_FILE" ]; then |
|
|
if [ -z "$COMPOSE_FILE" ]; then |
|
|
err "Can't find mailcow base installation path in backup." |
|
|
err "Can't find mailcow base installation path in backup." |
|
|
return 1 |
|
|
return 1 |
|
|
fi |
|
|
fi |
|
|
|
|
|
|
|
|
|
|
|
;; |
|
|
|
|
|
mysql) |
|
|
|
|
|
if [ -z "$COMPOSE_FILE" ]; then |
|
|
|
|
|
## Mailcow git base |
|
|
|
|
|
compose_files=() |
|
|
|
|
|
for mailcow_dir in /opt/{apps/,}mailcow-dockerized; do |
|
|
|
|
|
ssh:run "root@$vps" -- "[ -e \"$mailcow_dir/docker-compose.yml\" ]" || continue |
|
|
|
|
|
## this possibly change last value |
|
|
|
|
|
compose_files+=("$mailcow_dir/docker-compose.yml") |
|
|
|
|
|
done |
|
|
|
|
|
if [ "${#compose_files[@]}" == 0 ]; then |
|
|
|
|
|
err "No compose file found for mailcow installation." |
|
|
|
|
|
return 1 |
|
|
|
|
|
elif [ "${#compose_files[@]}" -gt 1 ]; then |
|
|
|
|
|
err "Multiple compose files for mailcow found:" |
|
|
|
|
|
for f in "${compose_files[@]}"; do |
|
|
|
|
|
echo " - $f" >&2 |
|
|
|
|
|
done |
|
|
|
|
|
echo "Can't decide which to use for mounting mysql container." >&2 |
|
|
|
|
|
return 1 |
|
|
|
|
|
fi |
|
|
|
|
|
COMPOSE_FILE="${compose_files[0]}" |
|
|
|
|
|
ENV_FILE="${COMPOSE_FILE%/*}/.env" |
|
|
|
|
|
if ! ssh:run "root@$vps" -- "[ -e \"${COMPOSE_FILE%/*}/.env\" ]"; then |
|
|
|
|
|
err "No env file in '$ENV_FILE' found." |
|
|
|
|
|
return 1 |
|
|
|
|
|
fi |
|
|
|
|
|
fi |
|
|
|
|
|
|
|
|
## Mysql database |
|
|
## Mysql database |
|
|
echo "${WHITE}Downloading last backup of mysql backups${NORMAL}" |
|
|
echo "${WHITE}Downloading last backup of mysql backups${NORMAL}" |
|
|
backup:rsync "/var/backups/mysql/" "/var/backups/mysql" || return 1 |
|
|
backup:rsync "/var/backups/mysql/" "/var/backups/mysql" || return 1 |
|
@ -553,8 +607,14 @@ EOF |
|
|
echo "${WHITE}Bringing mysql-mailcow down${NORMAL}" |
|
|
echo "${WHITE}Bringing mysql-mailcow down${NORMAL}" |
|
|
echo "docker-compose -f \"${COMPOSE_FILE}\" --env-file \"${ENV_FILE}\" down" | |
|
|
echo "docker-compose -f \"${COMPOSE_FILE}\" --env-file \"${ENV_FILE}\" down" | |
|
|
ssh:run "root@$vps" -- bash |
|
|
ssh:run "root@$vps" -- bash |
|
|
|
|
|
;; |
|
|
|
|
|
*) |
|
|
|
|
|
err "Unknown component '$target'. Bailing out." |
|
|
|
|
|
return 1 |
|
|
|
|
|
esac |
|
|
|
|
|
done |
|
|
|
|
|
|
|
|
ssh:run "root@$vps" -- "rm -rf '$VPS_TMP_DIR'" |
|
|
ssh:run "root@$vps" -- "rm -rf '$VPS_TMP_DIR'" |
|
|
msg_target="Full mailcow backup" |
|
|
|
|
|
fi |
|
|
fi |
|
|
|
|
|
|
|
|
if [ -n "$stopped_containers" ]; then |
|
|
if [ -n "$stopped_containers" ]; then |
|
@ -619,7 +679,7 @@ vps_backup_recover() { |
|
|
## from vps to backup server. |
|
|
## from vps to backup server. |
|
|
backup:setup-rsync "$admin" "$vps" "$server" "$id" || return 1 |
|
|
backup:setup-rsync "$admin" "$vps" "$server" "$id" || return 1 |
|
|
|
|
|
|
|
|
if [ -n "$path" ]; then |
|
|
|
|
|
|
|
|
if [[ "$path" == "/"* ]]; then |
|
|
if ! backup:path_exists "${path}"; then |
|
|
if ! backup:path_exists "${path}"; then |
|
|
err "File or directory '$path' not found in backup." |
|
|
err "File or directory '$path' not found in backup." |
|
|
return 1 |
|
|
return 1 |
|
@ -640,14 +700,20 @@ vps_backup_recover() { |
|
|
mailcow:vps_backup_recover "$admin" "$server" "$id" "$path" "$vps" "$vps_path" |
|
|
mailcow:vps_backup_recover "$admin" "$server" "$id" "$path" "$vps" "$vps_path" |
|
|
;; |
|
|
;; |
|
|
*-*) |
|
|
*-*) |
|
|
if [ -n "$path" ]; then |
|
|
|
|
|
|
|
|
if [[ "$path" == "/"* ]]; then |
|
|
## For now, will require having $path and $vps_path set, no additional behaviors |
|
|
## For now, will require having $path and $vps_path set, no additional behaviors |
|
|
file:vps_backup_recover "$admin" "$server" "$id" "$path" "$vps" "$vps_path" |
|
|
file:vps_backup_recover "$admin" "$server" "$id" "$path" "$vps" "$vps_path" |
|
|
|
|
|
else |
|
|
|
|
|
if [ -n "$path" ]; then |
|
|
|
|
|
err "Partial component recover of ${rtype:-unknown} backup type on" \ |
|
|
|
|
|
"${type:-unknown} type VPS is not yet implemented." |
|
|
|
|
|
return 1 |
|
|
else |
|
|
else |
|
|
err "Full recover of ${rtype:-unknown} backup type on" \ |
|
|
err "Full recover of ${rtype:-unknown} backup type on" \ |
|
|
"${type:-unknown} type VPS is not yet implemented." |
|
|
"${type:-unknown} type VPS is not yet implemented." |
|
|
return 1 |
|
|
return 1 |
|
|
fi |
|
|
fi |
|
|
|
|
|
fi |
|
|
;; |
|
|
;; |
|
|
esac |
|
|
esac |
|
|
|
|
|
|
|
@ -1000,7 +1066,8 @@ cmdline.spec:vps-backup:cmd:recover:run() { |
|
|
|
|
|
|
|
|
: :posarg: BACKUP_ID 'Backup id. |
|
|
: :posarg: BACKUP_ID 'Backup id. |
|
|
(ie: myadmin@backup.domain.org:10023#mx.myvps.org |
|
|
(ie: myadmin@backup.domain.org:10023#mx.myvps.org |
|
|
myadmin@ark-01.org#myid:/a/path)' |
|
|
|
|
|
|
|
|
myadmin@ark-01.org#myid:/a/path |
|
|
|
|
|
admin@ark-02.io#myid:myqsl,mailcow)' |
|
|
: :posarg: VPS_PATH 'Target host(s) to check. |
|
|
: :posarg: VPS_PATH 'Target host(s) to check. |
|
|
(ie: myvps.com |
|
|
(ie: myvps.com |
|
|
myvps.com:/a/path)' |
|
|
myvps.com:/a/path)' |
|
|