From 17a5eb51d9c88fdabb75968ddb2f829d154a08ca Mon Sep 17 00:00:00 2001 From: Nicolas Chan Date: Wed, 2 Jun 2021 20:31:44 -0700 Subject: [PATCH] More general worlds option and pass tests --- README.md | 2 +- backup.sh | 29 ++++++----------------- test/test.sh | 67 ++++++++++++++++++++++++++++++++-------------------- 3 files changed, 49 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index f08f143..bc83c7a 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ cd restored-world tar -xzvf /path/to/backups/2019-04-09_02-15-01.tar.gz ``` -Then you can move your restored world (`restored-world` in this case) to your Minecraft server folder and rename it (usually called `world`) so the Minecraft server uses it. +The restored worlds should be inside the `restored-world` directory, possibly nested under the parent directories. Then you can move your restored world to your Minecraft server folder under the proper name and path so the Minecraft server uses it. ### With `restic` Use [`restic restore`](https://restic.readthedocs.io/en/latest/050_restore.html) to restore from backup. diff --git a/backup.sh b/backup.sh index 052e67e..a5ce55b 100755 --- a/backup.sh +++ b/backup.sh @@ -9,7 +9,7 @@ # Default Configuration SCREEN_NAME="" # Name of the GNU Screen, tmux session, or hostname:port:password for RCON -SERVER_WORLD="" # Server world directory +SERVER_WORLDS=() # Server world directory BACKUP_DIRECTORY="" # Directory to save backups in MAX_BACKUPS=128 # -1 indicates unlimited DELETE_METHOD="thin" # Choices: thin, sequential, none; sequential: delete oldest; thin: keep last 24 hourly, last 30 daily, and monthly (use with 1 hr cron interval) @@ -23,7 +23,6 @@ SUPPRESS_WARNINGS=false # Suppress warnings LOCK_FILE="" # Optional lock file to acquire to ensure two backups don't run at once LOCK_FILE_TIMEOUT="" # Optional lock file wait timeout (in seconds) WINDOW_MANAGER="screen" # Choices: screen, tmux, RCON -BUKKIT=false # Other Variables (do not modify) DATE_FORMAT="%F_%H-%M-%S" @@ -71,7 +70,7 @@ while getopts 'a:cd:e:f:hi:l:m:o:p:qr:s:t:u:vw:x' FLAG; do echo "-x Bukkit-style server backup mode (world files are split by dimension)" exit 0 ;; - i) SERVER_WORLD=$OPTARG ;; + i) SERVER_WORLDS+=("$OPTARG") ;; l) COMPRESSION_LEVEL=$OPTARG ;; m) MAX_BACKUPS=$OPTARG ;; o) BACKUP_DIRECTORY=$OPTARG ;; @@ -83,7 +82,6 @@ while getopts 'a:cd:e:f:hi:l:m:o:p:qr:s:t:u:vw:x' FLAG; do u) LOCK_FILE_TIMEOUT=$OPTARG ;; v) DEBUG=true ;; w) WINDOW_MANAGER=$OPTARG ;; - x) BUKKIT=true ;; *) log-fatal "Invalid option -$FLAG"; exit 1 ;; esac done @@ -219,7 +217,7 @@ if ! $SUPPRESS_WARNINGS; then fi # Check for required arguments MISSING_CONFIGURATION=false -if [[ "$SERVER_WORLD" == "" ]]; then +if [[ "${#SERVER_WORLDS[@]}" == "0" ]]; then log-fatal "Server world not specified (use -i)" MISSING_CONFIGURATION=true fi @@ -445,7 +443,7 @@ clean-up () { TIME_DELTA=$((END_TIME - START_TIME)) if [[ "$BACKUP_DIRECTORY" != "" ]]; then - WORLD_SIZE_BYTES=$(du -b --max-depth=0 "$SERVER_WORLD" | awk '{print $1}') + WORLD_SIZE_BYTES=$(du --bytes --total --max-depth=0 "${SERVER_WORLDS[@]}" | tail -n 1 | awk '{print $1}') ARCHIVE_SIZE_BYTES=$(du -b "$ARCHIVE_PATH" | awk '{print $1}') ARCHIVE_SIZE=$(du -h "$ARCHIVE_PATH" | awk '{print $1}') BACKUP_DIRECTORY_SIZE=$(du -h --max-depth=0 "$BACKUP_DIRECTORY" | awk '{print $1}') @@ -479,19 +477,6 @@ clean-up () { trap "clean-up" 2 do-backup () { - # set bukkit world paths if this is a bukkit server - if "$BUKKIT"; then - WORLD_PARENT_DIR=$(dirname "$SERVER_WORLD") - WORLDS_NAME=$(realpath "$SERVER_WORLD") - # overwrite SERVER_WORLD so that restic gets all 3 dirs - WORLDS_NAME="$WORLDS_NAME/"\ "$WORLDS_NAME"_nether/\ "$WORLDS_NAME"_the_end/ - TAR_ARGS=$(basename -a $WORLDS_NAME) - else - # no bukkit--retain previous functionality - WORLD_PARENT_DIR="$SERVER_WORLD" - WORLDS_NAME="$SERVER_WORLD" - TAR_ARGS="." - fi # Notify players of start message-players "Starting backup..." "$ARCHIVE_FILE_NAME" @@ -507,10 +492,10 @@ do-backup () { case $COMPRESSION_ALGORITHM in # No compression - "") tar -cf "$ARCHIVE_PATH" -C "$WORLD_PARENT_DIR" $TAR_ARGS + "") tar -cf "$ARCHIVE_PATH" "${SERVER_WORLDS[@]}" ;; # With compression - *) tar -cf - -C "$WORLD_PARENT_DIR" $TAR_ARGS | $COMPRESSION_ALGORITHM -cv -"$COMPRESSION_LEVEL" - > "$ARCHIVE_PATH" 2>> /dev/null + *) tar -cf - "${SERVER_WORLDS[@]}" | $COMPRESSION_ALGORITHM -cv -"$COMPRESSION_LEVEL" - > "$ARCHIVE_PATH" 2>> /dev/null ;; esac EXIT_CODES=("${PIPESTATUS[@]}") @@ -532,7 +517,7 @@ do-backup () { if [[ "$RESTIC_REPO" != "" ]]; then RESTIC_TIMESTAMP="${TIMESTAMP:0:10} ${TIMESTAMP:11:2}:${TIMESTAMP:14:2}:${TIMESTAMP:17:2}" - restic backup -r "$RESTIC_REPO" $WORLDS_NAME --time "$RESTIC_TIMESTAMP" "$QUIET" + restic backup -r "$RESTIC_REPO" "${SERVER_WORLDS[@]}" --time "$RESTIC_TIMESTAMP" "$QUIET" ARCHIVE_EXIT_CODE=$? if [ "$ARCHIVE_EXIT_CODE" -eq 3 ]; then log-warning "Incomplete snapshot taken (some files could not be read)" diff --git a/test/test.sh b/test/test.sh index 6bbaf20..9fb69dc 100755 --- a/test/test.sh +++ b/test/test.sh @@ -62,7 +62,7 @@ check-backup-full-paths () { WORLD_DIR="$2" mkdir -p "$TEST_TMP/restored" tar --extract --file "$BACKUP_ARCHIVE" --directory "$TEST_TMP/restored" - assert-equals-directory "$WORLD_DIR" "$TEST_TMP/restored" + assert-equals-directory "$WORLD_DIR" "$TEST_TMP/restored/$WORLD_DIR" rm -rf "$TEST_TMP/restored" } @@ -80,6 +80,46 @@ check-latest-backup-restic () { # Tests +test-backup-defaults () { + TIMESTAMP="$(date +%F_%H-%M-%S --date="2021-01-01")" + ./backup.sh -i "$TEST_TMP/server/world" -o "$TEST_TMP/backups" -s "$SCREEN_TMP" -f "$TIMESTAMP" + check-backup "$TIMESTAMP.tar.gz" +} + +test-backup-multiple-worlds () { + TIMESTAMP="$(date +%F_%H-%M-%S --date="2021-01-01")" + cp -r "$TEST_TMP/server/world" "$TEST_TMP/server/world_nether" + cp -r "$TEST_TMP/server/world" "$TEST_TMP/server/world_the_end" + ./backup.sh -i "$TEST_TMP/server/world" -i "$TEST_TMP/server/world_nether" -i "$TEST_TMP/server/world_the_end" -o "$TEST_TMP/backups" -s "$SCREEN_TMP" -f "$TIMESTAMP" + mkdir -p "$TEST_TMP/restored" + tar --extract --file "$TEST_TMP/backups/$TIMESTAMP.tar.gz" --directory "$TEST_TMP/restored" + assert-equals-directory "$TEST_TMP/server/world" "$TEST_TMP/restored/$TEST_TMP/server/world" + assert-equals-directory "$TEST_TMP/server/world_nether" "$TEST_TMP/restored/$TEST_TMP/server/world_nether" + assert-equals-directory "$TEST_TMP/server/world_the_end" "$TEST_TMP/restored/$TEST_TMP/server/world_the_end" +} + +test-file-changed-as-read-warning () { + TIMESTAMP="$(date +%F_%H-%M-%S --date="2021-01-01")" + dd if=/dev/urandom of="$TEST_TMP/server/world/random" & + DD_PID="$!" + OUTPUT="$(./backup.sh -i "$TEST_TMP/server/world" -o "$TEST_TMP/backups" -s "$SCREEN_TMP" -f "$TIMESTAMP" 2>&1)" + EXIT_CODE="$?" + kill "$DD_PID" + assertEquals 0 "$EXIT_CODE" + assertContains "$OUTPUT" "Some files may differ in the backup archive" + + # Check that the backup actually resulted in a valid tar + assertTrue '[ -f '"$TEST_TMP/backups/$TIMESTAMP.tar.gz"' ]' + + mkdir -p "$TEST_TMP/restored" + tar --extract --file "$TEST_TMP/backups/$TIMESTAMP.tar.gz" --directory "$TEST_TMP/restored" + assert-equals-directory "$WORLD_DIR/file1.txt" "$TEST_TMP/restored/$WORLD_DIR/file1.txt" + assert-equals-directory "$WORLD_DIR/file2.txt" "$TEST_TMP/restored/$WORLD_DIR/file2.txt" + assert-equals-directory "$WORLD_DIR/file3.txt" "$TEST_TMP/restored/$WORLD_DIR/file3.txt" +} + + + test-lock-defaults () { TIMESTAMP="$(date +%F_%H-%M-%S --date="2021-01-01")" ./backup.sh -t "$TEST_TMP/lockfile" -i "$TEST_TMP/server/world" -o "$TEST_TMP/backups" -s "$SCREEN_TMP" -f "$TIMESTAMP" @@ -183,11 +223,6 @@ test-restic-defaults () { check-latest-backup-restic } -test-backup-defaults () { - TIMESTAMP="$(date +%F_%H-%M-%S --date="2021-01-01")" - ./backup.sh -i "$TEST_TMP/server/world" -o "$TEST_TMP/backups" -s "$SCREEN_TMP" -f "$TIMESTAMP" - check-backup "$TIMESTAMP.tar.gz" -} test-backup-spaces-in-directory () { TIMESTAMP="$(date +%F_%H-%M-%S --date="2021-01-01")" @@ -293,26 +328,6 @@ test-nonzero-exit-warning () { assertFalse '[ -f '"$TEST_TMP/backups/$TIMESTAMP.tar.gz"' ]' } -test-file-changed-as-read-warning () { - TIMESTAMP="$(date +%F_%H-%M-%S --date="2021-01-01")" - dd if=/dev/urandom of="$TEST_TMP/server/world/random" & - DD_PID="$!" - OUTPUT="$(./backup.sh -i "$TEST_TMP/server/world" -o "$TEST_TMP/backups" -s "$SCREEN_TMP" -f "$TIMESTAMP" 2>&1)" - EXIT_CODE="$?" - kill "$DD_PID" - assertEquals 0 "$EXIT_CODE" - assertContains "$OUTPUT" "Some files may differ in the backup archive" - - # Check that the backup actually resulted in a valid tar - assertTrue '[ -f '"$TEST_TMP/backups/$TIMESTAMP.tar.gz"' ]' - - mkdir -p "$TEST_TMP/restored" - tar --extract --file "$TEST_TMP/backups/$TIMESTAMP.tar.gz" --directory "$TEST_TMP/restored" - assert-equals-directory "$WORLD_DIR/file1.txt" "$TEST_TMP/restored/file1.txt" - assert-equals-directory "$WORLD_DIR/file2.txt" "$TEST_TMP/restored/file2.txt" - assert-equals-directory "$WORLD_DIR/file3.txt" "$TEST_TMP/restored/file3.txt" -} - test-screen-interface () { TIMESTAMP="$(date +%F_%H-%M-%S --date="2021-01-01")" ./backup.sh -i "$TEST_TMP/server/world" -o "$TEST_TMP/backups" -s "$SCREEN_TMP" -f "$TIMESTAMP"