diff --git a/README.org b/README.org index 0e7b365..1e430b8 100644 --- a/README.org +++ b/README.org @@ -924,6 +924,21 @@ la source un chemin et possible aussi à la destination. 0km vps-backup recover myadmin@core-06.0k.io:10023#mail.mybackupedvps.com:/mon/chemin mynewvps.com:/ma/dest #+end_quote +***** Récupération d'un composant + +Suivant si le type de backup est reconnu et le supporte, il est +possible de nommer un composant précis en lieu et place d'un +répertoire ou d'un fichier. + +Par exemple, les backup de type 'mailcow' supportent les composants +suivants: =mailcow=, =postfix=, =rspamd=, =redis=, =crypt=, =vmail=, +=vmail-attachments=, =mysql=. + +#+begin_quote +0km vps-backup recover myadmin@core-06.0k.io:10023#mail.mybackupedvps.com:mailcow,mysql mynewvps.com +0km vps-backup recover myadmin@core-06.0k.io:10023#mail.mybackupedvps.com:postfix mynewvps.com +#+end_quote + ** Troubleshooting diff --git a/bin/0km b/bin/0km index 07dfdd9..ddbfbb2 100755 --- a/bin/0km +++ b/bin/0km @@ -386,7 +386,7 @@ EOF stopped_containers=1 fi - if [[ -n "$path" ]]; then + if [[ "$path" == "/"* ]]; then ## ## Additional intelligence to simple file copy @@ -425,76 +425,130 @@ EOF fi fi else - - for vol in postfix rspamd redis crypt vmail{,-attachments}; do - volume_name="mailcowdockerized_${vol}-vol-1" - volume_dir="/var/lib/docker/volumes/${volume_name}/_data" - if ! backup:path_exists "${volume_dir}/"; then - warn "No '$volume_name' in backup. This might be expected." - continue - fi - ## Create volumes if not existent - if ! ssh:run "root@$vps" -- " - [ -d '${volume_dir}' ] || - docker run --rm -v ${volume_name}:/tmp/dummy docker.0k.io/alpine:3.9 - [ -d '${volume_dir}' ] - "; then - err "Couldn't find nor create '${volume_dir}'." + 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 - - echo "${WHITE}Downloading of $volume_name${NORMAL}" - backup:rsync "${volume_dir}/" "${volume_dir}" || return 1 - done - - ## Mailcow git base - COMPOSE_FILE= - for mailcow_dir in /opt/{apps/,}mailcow-dockerized; do - backup:path_exists "${mailcow_dir}/" || continue - ## this possibly change last value - COMPOSE_FILE="$mailcow_dir/docker-compose.yml" - ENV_FILE="$mailcow_dir/.env" - echo "${WHITE}Download of $mailcow_dir${NORMAL}" - backup:rsync "${mailcow_dir}"/ "${mailcow_dir}" || return 1 - break - done - - if [ -z "$COMPOSE_FILE" ]; then - err "Can't find mailcow base installation path in backup." - return 1 + msg_target="Partial mailcow backup" + else + targets=("${ALL_TARGETS[@]}") + msg_target="Full mailcow backup" fi - ## Mysql database - echo "${WHITE}Downloading last backup of mysql backups${NORMAL}" - backup:rsync "/var/backups/mysql/" "/var/backups/mysql" || return 1 - if ! env_content=$(echo "cat '$ENV_FILE'" | ssh:run "root@$vps" -- bash); then - err "Can't access env file: '$ENV_FILE'." - return 1 - fi + for target in "${targets[@]}"; do + case "$target" in + postfix|rspamd|redis|crypt|vmail|vmail-attachments) + + volume_name="mailcowdockerized_${target}-vol-1" + volume_dir="/var/lib/docker/volumes/${volume_name}/_data" + if ! backup:path_exists "${volume_dir}/"; then + warn "No '$volume_name' in backup. This might be expected." + continue + fi + ## Create volumes if not existent + if ! ssh:run "root@$vps" -- " + [ -d '${volume_dir}' ] || + docker run --rm -v ${volume_name}:/tmp/dummy docker.0k.io/alpine:3.9 + [ -d '${volume_dir}' ] + "; then + err "Couldn't find nor create '${volume_dir}'." + return 1 + fi + + echo "${WHITE}Downloading of $volume_name${NORMAL}" + backup:rsync "${volume_dir}/" "${volume_dir}" || return 1 + ;; + mailcow) + ## Mailcow git base + COMPOSE_FILE= + for mailcow_dir in /opt/{apps/,}mailcow-dockerized; do + backup:path_exists "${mailcow_dir}/" || continue + ## this possibly change last value + COMPOSE_FILE="$mailcow_dir/docker-compose.yml" + ENV_FILE="$mailcow_dir/.env" + echo "${WHITE}Download of $mailcow_dir${NORMAL}" + backup:rsync "${mailcow_dir}"/ "${mailcow_dir}" || return 1 + break + done + if [ -z "$COMPOSE_FILE" ]; then + err "Can't find mailcow base installation path in backup." + return 1 + 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 + echo "${WHITE}Downloading last backup of mysql backups${NORMAL}" + backup:rsync "/var/backups/mysql/" "/var/backups/mysql" || return 1 + + if ! env_content=$(echo "cat '$ENV_FILE'" | ssh:run "root@$vps" -- bash); then + err "Can't access env file: '$ENV_FILE'." + return 1 + fi - root_password=$(printf "%s\n" "$env_content" | grep ^DBROOT= | cut -f 2 -d =) + root_password=$(printf "%s\n" "$env_content" | grep ^DBROOT= | cut -f 2 -d =) - echo "${WHITE}Bringing mysql-mailcow up${NORMAL}" - if ! image=$(cat < "$VPS_TMP_DIR/my.cnf" @@ -504,35 +558,35 @@ docker-compose -f "${COMPOSE_FILE}" --env-file "${ENV_FILE}" \ mysql-mailcow EOF - ); then - err "Failed to bring up mysql-mailcow" - return 1 - fi - - START="$SECONDS" - retries=0 - timeout=600 - while true; do - ((retries++)) - echo " waiting for mysql db..." \ - "(retry $retries, $(($SECONDS - $START))s elapsed, timeout is ${timeout}s)" >&2 - cat <&2 + cat </dev/null 2>&1 EOF - if (($SECONDS - $START > $timeout)); then - err "Failed to connect to mysql-mailcow." - echo "docker-compose -f \"${COMPOSE_FILE}\" --env-file \"${ENV_FILE}\" down" | - ssh:run "root@$vps" -- bash - return 1 - fi - sleep 0.4 - done + if (($SECONDS - $START > $timeout)); then + err "Failed to connect to mysql-mailcow." + echo "docker-compose -f \"${COMPOSE_FILE}\" --env-file \"${ENV_FILE}\" down" | + ssh:run "root@$vps" -- bash + return 1 + fi + sleep 0.4 + done - DBUSER=$(printf "%s\n" "$env_content" | grep ^DBUSER= | cut -f 2 -d =) - DBPASS=$(printf "%s\n" "$env_content" | grep ^DBPASS= | cut -f 2 -d =) + DBUSER=$(printf "%s\n" "$env_content" | grep ^DBUSER= | cut -f 2 -d =) + DBPASS=$(printf "%s\n" "$env_content" | grep ^DBPASS= | cut -f 2 -d =) - echo "${WHITE}Uploading mysql dump${NORMAL}" - cat <