diff --git a/Makefile b/Makefile index 4703ba5..9da136b 100644 --- a/Makefile +++ b/Makefile @@ -93,6 +93,7 @@ test: test-local-no_times test: test-local-copy_links test: test-local-crazy-filename-chars test: test-local-crazy-pathname-chars +test: test-local-custom-rsync-binary test: test-remote-default-abs test: test-remote-default-rel test: test-remote-ssh_1111_port-nouser @@ -145,6 +146,9 @@ test-local-crazy-filename-chars: test-local-crazy-pathname-chars: ./tests/06-run-local-crazy-pathname-chars.sh +test-local-custom-rsync-binary: + ./tests/07-run-local-custom-rsync-binary.sh + test-remote-default-abs: ./tests/10-run-remote-default-abs.sh diff --git a/tests/07-run-local-custom-rsync-binary.sh b/tests/07-run-local-custom-rsync-binary.sh new file mode 100755 index 0000000..f14bb31 --- /dev/null +++ b/tests/07-run-local-custom-rsync-binary.sh @@ -0,0 +1,235 @@ +#!/usr/bin/env bash + +set -e +set -u +set -o pipefail + +SCRIPTPATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" +FUNCPATH="${SCRIPTPATH}/.lib/functions.sh" +# shellcheck disable=SC1090 +. "${FUNCPATH}" + + +### +### RSYNC ARGUMENTS +### +RSYNC_ARGS="" + +print_section "07 Custom Rsync" + +### ################################################################################################ +### ################################################################################################ +### +### CREATE FILES AND DIRS +### +### ################################################################################################ +### ################################################################################################ + +print_headline "Creating files and directories" + +### +### Set up custom rsync command +### + +RSYNC_COMMAND_DIR="$( create_tmp_dir )" +create_link "${RSYNC_COMMAND_DIR}" "custom_rsync" "$(command -v rsync 2>/dev/null)" +export RSYNC_COMMAND="${RSYNC_COMMAND_DIR}/custom_rsync" + +### +### Create source and target dir +### +SRC_DIR="$( create_tmp_dir )" +DST_DIR="$( create_tmp_dir )" + +FILE1_NAME="file1.txt" +FILE2_NAME="file2.txt" +FILE3_NAME="sub/file3.txt" + +FILE1_PERM="607" +FILE2_PERM="707" +FILE3_PERM="607" + +LINK1_NAME="links/link1.txt" +LINK2_NAME="links/link2.txt" +LINK3_NAME="links/link3.txt" + +LINK1_FROM="../${FILE1_NAME}" +LINK2_FROM="../${FILE2_NAME}" +LINK3_FROM="../${FILE3_NAME}" + + +### +### Create source files +### +create_file "${SRC_DIR}" "${FILE1_NAME}" "2" "${FILE1_PERM}" +create_file "${SRC_DIR}" "${FILE2_NAME}" "5" "${FILE2_PERM}" +create_file "${SRC_DIR}" "${FILE3_NAME}" "1" "${FILE3_PERM}" + +create_link "${SRC_DIR}" "${LINK1_NAME}" "${LINK1_FROM}" +create_link "${SRC_DIR}" "${LINK2_NAME}" "${LINK2_FROM}" +create_link "${SRC_DIR}" "${LINK3_NAME}" "${LINK3_FROM}" +sleep 2 + + +### ################################################################################################ +### ################################################################################################ +### +### DEFINE CHECKS +### +### ################################################################################################ +### ################################################################################################ + +check_file() { + local file="${1}" + local perm="${2}" + local destination= + destination="${DST_DIR}/current/$(basename "${SRC_DIR}")" + + print_subline "Validate ${file}" + + check_dst_file_is_file "${file}" "${destination}" + + check_src_dst_file_exist "${file}" "${SRC_DIR}" "${destination}" + check_src_dst_file_equal "${file}" "${SRC_DIR}" "${destination}" + + check_dst_file_perm "${file}" "${perm}" "${perm}" "${destination}" + check_src_dst_file_perm "${file}" "${SRC_DIR}" "${destination}" + check_src_dst_file_size "${file}" "${SRC_DIR}" "${destination}" + check_src_dst_file_mod_time "${file}" "${SRC_DIR}" "${destination}" + check_src_dst_file_uid "${file}" "${SRC_DIR}" "${destination}" + check_src_dst_file_gid "${file}" "${SRC_DIR}" "${destination}" +} + +check_link() { + local link="${1}" + local destination= + destination="${DST_DIR}/current/$(basename "${SRC_DIR}")" + + print_subline "Validate ${link}" + check_src_dst_file_exist "${link}" "${SRC_DIR}" "${destination}" + check_dst_file_is_link "${link}" "${destination}" + check_src_dst_file_equal "${link}" "${SRC_DIR}" "${destination}" +} + +check_dir() { + local src="${1}" + local dst="${2}" + local destination= + destination="${dst}/current/$(basename "${src}")" + + check_dir_size "${src}" "${destination}" +} + +check_backup() { + local src="${1}" + local dst="${2}" + local backup1="${3}" + local backup2="${4}" + + local src_actual_size + local dst_actual_size + local backup1_actual_size + local backup2_actual_size + + print_subline "Check incremental Backup" + + backup1_actual_size="$( get_dir_size_without_hardlinks "${backup1}" "/$(basename "${src}")" )" + backup2_actual_size="$( get_dir_size_without_hardlinks "${backup2}" "/$(basename "${src}")" )" + if [ "${backup1_actual_size}" -eq "${backup2_actual_size}" ]; then + printf "[TEST] [FAIL] Incremental: inital backup (%s) and incremental backup (%s) disk sizes are equal\\r\\n" "${backup1_actual_size}" "${backup2_actual_size}" + exit 1 + fi + printf "[TEST] [OK] Incremental: inital backup (%s) and incremental backup (%s) disk sizes differ\\r\\n" "${backup1_actual_size}" "${backup2_actual_size}" + + + print_subline "Check incremental Backup after deleting initial full backup" + + run "rm -rf '${backup1}'" + src_actual_size="$( get_dir_size_with_hardlinks "${src}" )" + dst_actual_size="$( get_dir_size_without_hardlinks "${dst}/current/" "/$(basename "${src}")" )" + + if [ "${src_actual_size}" -ne "${dst_actual_size}" ]; then + printf "[TEST] [FAIL] Incremental Backup: src-dir(%s) size is not equal to dst-dir(%s)\\r\\n" "${src_actual_size}" "${dst_actual_size}" + exit 1 + fi + printf "[TEST] [OK] Incremental Backup: src-dir(%s) size is equal to dst-dir(%s)\\r\\n" "${src_actual_size}" "${dst_actual_size}" +} + + +### ################################################################################################ +### ################################################################################################ +### +### Run backup (Round 1) +### +### ################################################################################################ +### ################################################################################################ + +print_headline "Backup (Round 1)" + +print_subline "Run Backup" +run_backup \ + "${SCRIPTPATH}/../timemachine" \ + "${SRC_DIR}" \ + "${DST_DIR}" \ + "${RSYNC_ARGS}" \ + "full" + +check_file "${FILE1_NAME}" "${FILE1_PERM}" +check_file "${FILE2_NAME}" "${FILE2_PERM}" +check_file "${FILE3_NAME}" "${FILE3_PERM}" + +check_link "${LINK1_NAME}" +check_link "${LINK2_NAME}" +check_link "${LINK3_NAME}" + +check_dir "${SRC_DIR}" "${DST_DIR}" + +BACKUP_PATH_1="$( cd "${DST_DIR}/current/" && pwd -P )" + + +### ################################################################################################ +### ################################################################################################ +### +### Run backup (Round 2) +### +### ################################################################################################ +### ################################################################################################ + +print_headline "Backup (Round 2)" + +print_subline "Run Backup" +run_backup \ + "${SCRIPTPATH}/../timemachine" \ + "${SRC_DIR}" \ + "${DST_DIR}" \ + "${RSYNC_ARGS}" \ + "incremental" + +check_file "${FILE1_NAME}" "${FILE1_PERM}" +check_file "${FILE2_NAME}" "${FILE2_PERM}" +check_file "${FILE3_NAME}" "${FILE3_PERM}" + +check_link "${LINK1_NAME}" +check_link "${LINK2_NAME}" +check_link "${LINK3_NAME}" + +check_dir "${SRC_DIR}" "${DST_DIR}" + +BACKUP_PATH_2="$( cd "${DST_DIR}/current/" && pwd -P )" + + +### ################################################################################################ +### ################################################################################################ +### +### Validate Backups +### +### ################################################################################################ +### ################################################################################################ + +print_headline "Validate Backups" + +check_backup \ + "${SRC_DIR}" \ + "${DST_DIR}" \ + "${BACKUP_PATH_1}" \ + "${BACKUP_PATH_2}" diff --git a/timemachine b/timemachine index 27c7c38..1d83d87 100755 --- a/timemachine +++ b/timemachine @@ -22,6 +22,7 @@ PORT= KEY= # Default variables +RSYNC_COMMAND="${RSYNC_COMMAND:=rsync}" SSH_ARGS="-oStrictHostKeyChecking=no -oLogLevel=QUIET -q" @@ -379,10 +380,13 @@ if ! dir_exists "${2}"; then exit 1 fi -if ! command -v rsync >/dev/null 2>&1; then - logerr "rsync binary not found but required." +if ! command -v "$RSYNC_COMMAND" >/dev/null 2>&1; then + logerr "rsync binary '$RSYNC_COMMAND' not found but required." exit 1 fi +if [ "$RSYNC_COMMAND" != "rsync" ]; then + RSYNC_COMMAND="$(escape_path "$RSYNC_COMMAND")" +fi @@ -427,9 +431,9 @@ if link_exists "${DEST}/${BACKUP_LATEST}"; then BTYPE="incremental" logmsg "Starting incremental backup" - logmsg "\$ rsync $* $( escape_path "${SRC}" ) $( escape_path "${DEST}/${BACKUP_INPROGRESS}" )" + logmsg "\$ $RSYNC_COMMAND $* $( escape_path "${SRC}" ) $( escape_path "${DEST}/${BACKUP_INPROGRESS}" )" - cmd="rsync \ + cmd="$RSYNC_COMMAND \ -e \"ssh ${SSH_ARGS}\" \ --recursive \ --perms \ @@ -447,9 +451,9 @@ else BTYPE="full" logmsg "Starting full backup" - logmsg "\$ rsync $* $( escape_path "${SRC}" ) $( escape_path "${DEST}/${BACKUP_INPROGRESS}" )" + logmsg "\$ $RSYNC_COMMAND $* $( escape_path "${SRC}" ) $( escape_path "${DEST}/${BACKUP_INPROGRESS}" )" - cmd="rsync \ + cmd="$RSYNC_COMMAND \ -e \"ssh ${SSH_ARGS}\" \ --recursive \ --perms \